1、日志重要吗
程序中的日志重要吗? 在回答这个问题前,笔者先说个事例: ❝
笔者印象尤深的便是去年某个同事,收到了客户反馈的紧急bug。尽管申请到了日志文件,但由于非常多关键过程无打印日志,引起排查进度很慢,数个小时都没能排查到问题,亦没法给出处理对策。引起了客户程序始终阻断,最后产生了不少损失。
事后,经过仔细推敲,成功复现了这个bug,其实是一个很不起眼的数据转换引起的。可由于日志内容的匮乏,排查起来难度很大。其实只要在数据转换前后进行日志输出,这个问题便是一眼的事。但可惜没倘若,故事的最后,研发分部还是遭到了客户的投诉,影响到了分部绩效 ❞
针对刚学习编程的朋友,非常多人都对日志满不在乎,咱们在做code review的时候,经常发掘有些新朋友爱好一个办法写得很长,而后中间的注释和日志都少的可怜。
坦白的说,这是很欠好的习惯,这寓意着日后办法出了bug,或需要迭代,要花费海量时间来理清办法的思路。千万别迷信什么“办法名、字段名起的见明知意,就能够不写注释与日志”,那是她们的业务场景不足繁杂。以笔者为例,繁杂的场景触及非常多公式、奇特的规定,不写注释与日志,后续没人能守护得了
因此请务必记住,日志在研发过程中非常重要。它能够帮忙研发人员认识程序中出现了什么,以及在某些状况下为何会出现错误或反常。经过查看日志,研发人员能够容易地定位并处理问题,并且能够更好地监控和调节应用程序的性能,在必要时进行故障排除和安全检测
2、日志分级
最起始的日志分级是由于Syslog的研发者Eric Allman在1981年提出的。之后,这个级别分级系统被广泛应用于各样行业的日志记录和信息处理中。下面咱们就来介绍下常用的日志等级 TRACE是最低级别的日志记录,用于输出最仔细的调试信息,一般用于研发调试目的。在生产环境中,应该关闭 TRACE 级别的日志记录,以避免输出太多无用信息。 DEBUG是用于输出程序中的有些调试信息,一般用于研发过程中。像 TRACE 同样,在生产环境中应该关闭 DEBUG 级别的日志记录。 INFO用于输出程序正常运行时的有些关键信息,例如程序的起步、运行日志等。一般在生产环境中开启 INFO 级别的日志记录。 WARN是用于输出有些警告信息,提示程序可能会显现有些反常或错误。在应用程序中,WARN 级别的日志记录一般用于记录有些非致命性反常信息,以便能够即时发掘并处理这些问题。 ERROR是用于输出程序运行时的有些错误信息,一般暗示程序显现了有些不可预料的错误。在应用程序中,ERROR 级别的日志记录一般用于记录有些致命性的反常信息,以便能够即时发掘并处理这些问题。
当然,除了这五种级别以外,还有有些日志框架定义了其他级别,例如 Python 中的 CRITICAL、PHP 中的 FATAL 等。CRITICAL 和 FATAL 都是用于暗示程序显现了致命性错误或反常,即不可恢复的错误。当然,针对咱们今天要说的内容,晓得以上五种日志等级就够了。
3、常用日志插件Log4j(1999年诞生)Log4j 是Java行业中最早的流行日志框架之一。它由Ceki Gülcü研发,并后来由Apache软件基金会接管。Log4j 供给了灵活的配置选项、多种输出目的地、日志级别和分层日志体系。尽管Log4j 1在其时代取得了巨大的成功,但在性能和某些功能方面存在限制,因此呢后来演化为Log4j 2。 SLF4J(2004年诞生)严格来讲,SLF4J(Simple Logging Facade for Java)并不算一个插件,而是Ceki Gülcü研发的一个日志门面接口。它为Java应用程序供给了统一的日志抽象,使研发人员能够运用一致的API进行日志记录,而不需要直接依赖于特定的日志实现。SLF4J 能够与多种底层日志框架(如Logback、Log4j 2、java.util.logging等)结合运用。 Logback(2009年诞生)Logback 是Ceki Gülcü研发的日志框架,他亦是Log4j的作者。Logback 是Log4j 1的后续版本,旨在供给更高性能、更灵活的配置和现代化的日志处理方法。Logback 支持异步日志记录、多种输出格式、灵活的配置以及与SLF4J紧密集成。 Log4j 2(2014年诞生)Log4j 2 是Apache软件基金会研发的Log4j的下一代版本。它引入了许多新特性,如异步日志记录、插件支持、丰富的过滤器等,旨在供给更好的性能和灵活性。Log4j 2 在设计上思虑了Log4j 1的局限性,并且支持多种配置方式。 小故事不难重视到,一个有意思的小故事是,前三款日志插件都是Ceki Gülcü研发的,但 Log4j 2 并不是,虽然此刻有非常多人以为log4j2亦是他写的,但咱们在github上能够看到其个人说明 “Unaffiliated with log4j 2.x.” (与 log4j 2.x 无关),因此log4j2 和 logback 都自叫作是log4j 的后续版本,到底谁才算正统续作呢?这就留给各位读者自己玩味了
4、外观模式与SLF4J
在讲解更加多插件详情之前,咱们先来瞧瞧运用最多的SLF4J ,咱们前面说了 SLF4J(Simple Logging Facade for Java)是Ceki Gülcü研发的一个日志门面接口,那样很显然这儿就用到了门面模式(即Facade 或 外观模式),笔者比较习惯说成是外观模式,后续就叫作为外观模式。 1. 外观模式
定义:外观模式是一种结构型设计模式,它供给了一个简单的接口,封装了底层繁杂的子系统,使得客户端能够更方便地运用这个子系统
目的:外观模式的目的是隐匿底层系统的繁杂性,降低拜访成本。
倘若说看定义有些抽象,那咱们能够以生活中的例子来讲,咱们都知道此刻越来越流行智能家居,亦便是家庭内装了非常多智能家电,从电视、空调、到廊灯乃至窗帘都是智能的。这类家庭常常会有一个掌控中心,咱们不需要手动去开电视,只需要对着掌控中心说:“小A小A,帮我打开电视,音量调到30%”,电视就会应声打开并调节音量
那样这般的话,咱们不需要晓得怎么开电视,怎么调音量。通通都能用最简单的话语来调节。同理,此刻手机上的拍照功能:感光度,对焦,白平衡这些细节都给你自动完成为了,因此这些繁杂的内容你此刻基本不消管,只需要猛按拍照键就可。
这便是外观模式的道理,外观模式便是为了隐匿系统的繁杂性而设计出来的,让客户端只对接触到一个外观类,而不会接触到系统内部的繁杂规律 2. SLF4J 的诞生
在初期运用日志框架时,应用程序一般需要直接与详细的日志框架进行耦合,这就引起了以下几个问题: 代码依赖性应用程序需要直接引用详细的日志框架,从而引起代码与日志框架强耦合,难以满足应用程序对日志框架的灵活配置。 日志框架不统一在运用区别的日志框架时,应用程序需要按照详细的日志框架来编写代码,这不仅会增多研发难度,况且在多种日志框架中切换时需要进行海量的代码改动。 性能问题在日志输出频繁的状况下,因为日志框架的实现方式和API设计区别,可能会引起性能问题。
为认识决这些问题,SLF4J供给了一套通用的日志门面接口,让应用程序能够经过这些接口来记录日志信息,而不需要直接引用详细的日志框架。这般,应用程序就能够在区别的日志框架之间进行灵活配置和切换,同期还能够得到更好的性能表现。因此,我剧烈意见各位运用SLF4J, 而不是直接对接某个详细的日志框架。 3. SLF4J 的运用
首要,咱们需要在工程内引入包,然则倘若你用了springboot,各样 spring-boot-starter 起步器已然引用过了,因此引用前最好确认下: <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.32</version></dependency>
而后在咱们要打印日志的类里加上一行 ;private static final Logger logger = LoggerFactory.getLogger(XXXX.class); 就可运用,如下: import
org.slf4j.Logger; import
org.slf4j.LoggerFactory; public class MyClass
{ private static final Logger log = LoggerFactory.getLogger(MyClass.class)
; //... public static void main(String[] args)
{ log.info("This is an info message."
);
}
}
倘若咱们引用了lombok的话,亦能够运用lombok的注解@Slf4j 代替上面那句话来运用 SLF4J ,如下: import
lombok.extern.slf4j.Slf4j; @Slf
4j public class MyClass
{ public static void main(String[] args)
{ log.info("This is an info message."
);
}
}
然则,咱们都晓得SLF4J仅仅是个门面,换句话说,仅有接口而无实现,倘若此刻咱们直接运行,打印日志是无用处的
因此,咱们倘若要运行,咱们必须要给 SLF4J 安排上实现,而日前最常用的便是 logback 和 log4j2 了,就让咱们接着往下看
5、双雄之争
其实关于 Logback 和 Log4j 2,网络上有非常多评测,就不需赘述了,重点是围绕性能方面的,从日前大众的反馈看,Log4j 2 晚显现好几年,还是有后发优良的,性能会比 Logback 好。当然, Logback 本身性能亦很强,针对大都数场景,完全是够用的,况且配置比较直观,是spring-boot 默认运用的日志插件。
因此,选谁都能够,倘若不想费神,能够直接运用spring-boot自带的Logback,倘若对日志性能需求很高,运用log4j2更保险,咱们接下来分别介绍两者。 1. Logback1. 引用因为 Logback 为 spring-boot 默认日志框架,因此无需再引用,但针对非spring - boot 项目,能够做如下引用 <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.12</version></dependency>
Logback 的核心模块为 logback-classic,它供给了一个 SLF4J 的实现,兼容 Log4j API,能够无缝地替换 Log4j。它自己已然包括了 logback-core 模块,而 logback-core,顾名思义便是 logback 的核心功能,包含日志记录器、Appender、Layout 等。其他 logback 模块都依赖于该模块 2. 配置logback 能够经过 XML 或 Groovy 配置。下面以 XML 配置为例。logback 的 XML 配置文件名叫作一般为 logback.xml 或 logback-spring.xml(在 Spring Boot 中),需要安置在 classpath 的根目录下, <?xml version="1.0" encoding="UTF-8"?><configuration> <!--定义日志文件的存储位置,运用Spring的属性文件配置方式--> <springProperty scope="context" name="log.home" source="log.home" defaultValue="logs"/> <!--定义日志文件的路径--> <property name="LOG_PATH" value="${log.home}"/> <!--定义掌控台输出--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-5relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <!--定义 INFO 及以上级别信息输出到掌控台--> <root level="INFO"> <appender-ref ref="console"/> </root> <!--定义所有组件的日志级别,如所有 DEBUG--> <logger name="com.example" level="DEBUG"/> <!-- date 格式定义 --> <property name="LOG_DATEFORMAT" value="yyyy-MM-dd"/> <!-- 定义日志归档文件名叫作格式,每日生成一个日志文件 --> <property name="ARCHIVE_PATTERN" value="${LOG_PATH}/%d{${LOG_DATEFORMAT}}/app-%d{${LOG_DATEFORMAT}}-%i.log.gz"/> <!--定义文件输出,会按照定义的阈值进行切割,支持自动归档压缩过期日志--> <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_PATH}/app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!--定义日志文件切割的阈值,本例是 50MB--> <maxFileSize>50MB</maxFileSize> <!--定义日志文件保存时间,本例是每日生成一个日志文件--> <fileNamePattern>${ARCHIVE_PATTERN}</fileNamePattern> <maxHistory>30</maxHistory> <!-- zip 压缩生成的归档文件 --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>50MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!-- 删除过期文件 --> <cleanHistoryOnStart>true</cleanHistoryOnStart> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <!--定义 ERROR 级别以上信息输出到文件--> <logger name="com.example.demo" level="ERROR" additivity="false"> <appender-ref ref="file"/> </logger> <!--异步输出日志信息--> <appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender"> <discardingThreshold>0</discardingThreshold> <queueSize>256</queueSize> <appender-ref ref="file"/> </appender> <!--定义INFO及以上级别信息异步输出到文件--> <logger name="com.example" level="INFO" additivity="false"> <appender-ref ref="asyncFile"/> </logger></configuration>
其中,重点包含以下配置: springProperty定义了 log 文件的存储路径,能够通过 Spring 的属性文件配置方式进行设置,倘若无配置则默认存储在 logs 目录下。appender 定义了日志输出的目的,这儿包含了掌控台输出和文件输出两种,详细能够按照需要进行配置。root定义了默认的日志级别和输出目的,默认状况下,INFO 级别以上的日志信息会输出到掌控台,能够按照实质需求进行修改。logger 定义了区别组件的日志级别和输出目的,例如,这儿定义了 com.example 这个组件的日志级别为 DEBUG,而 com.example.demo 这个组件的日志级别为 ERROR,并将其输出到文件中。rollingPolicy定义了日志文件的切割规则和归档策略,此处定义了日志文件每一个 50MB 进行切割,每日生成一个日志文件,并且压缩和删除过期文件,最多保存 30 天的日志文件。encoder 定义了日志信息的输出格式,详细的格式能够自动定义。asyncAppender 定义了异步输出日志的方式,针对高并发时,能够运用异步输出来加强系统的性能。discardingThreshold 定义了异步输出队列的阈值,当队列中的数据量超过此值时,会丢弃最早放入的数据,此处设置为 0 暗示队列不会丢弃任何数据。queueSize 定义了异步输出队列的体积,当队列满时,会等待队列中的数据被消费后再将数据放入队列中,此处设置为 256。3. 演示咱们新建一个普通工程(非spring工程),引用Logback后,把以上配置文件复制进logback.xml,而后将工程结构设置成如下模式
其中两个类的代码如下: public class Main
{ private static final Logger log = LoggerFactory.getLogger(Main.class)
; public static void main(String[] args)
{ log.trace("This is a Main trace message."
); log.debug("This is a Main debug message."
); log.info("This is a Main info message."
); log.warn("This is a Main warn message."
); log.error("This is a Main error message."
);
Slave.main(args);
}
} public class Slave
{ private static finalLogger log = LoggerFactory.getLogger(Slave.class)
; public static void main(String[] args)
{ log.trace("This is a Slave trace message."
); log.debug("This is a Slave debug message."
); log.info("This is a Slave info message."
); log.warn("This is a Slave warn message."
); log.error("This is a Slave error message."
);
}
}
我们想实现这般的效果,首要日志要同期 输出到掌控台 及 日志文件,且区别层级的代码,输出的日志层级亦区别。那样咱们能够对以上的xml做出有些调节:
由于是非Spring项目,因此 springProperty 这般的标签就不要用了,咱们直接写死一个日志文件位置就可。 <!--定义日志文件的路径--><property name="LOG_PATH" value="D://logs/log"/>
去掉原有的哪些root、logger标签,咱们自己新建两个logger,用于两个区别的层级。咱们想里层输出 debug 级别,外层输出info 级别,咱们能够这么设置。并且同期输出到掌控台及日志文件 <logger name="com.zhanfu" level="INFO"> <appender-ref ref="file" /> <appender-ref ref="console"/></logger><logger name="com.zhanfu.child" level="DEBUG" additivity="false"> <appender-ref ref="file" /> <appender-ref ref="console"/></logger>
当咱们运行Main.main的时候,就能够得到以下日志,slave 能输出debug级别,Main 只能输出 info及以上级别 4. 细节点其实咱们上面的演示,有两个细节点,需要重视一下。一个便是咱们的 <logger name="com.zhanfu.child" level="DEBUG" additivity="false">
运用了一个 additivity="false" 的属性,这其实是由于 logger 这个标签在锁定某个目录时,可能会出现层级关系。例如咱们的两个 logger, 一个针对的目录是 com.zhanfu 另一个是 com.zhanfu.child ,后者就会被前者包括。
当咱们的 com.zhanfu.child.Slave 打印日志时,当然会运用后者(更精确)的设置,但前者的设置还运用吗?就依赖于 additivity=“false”,此处倘若咱们把 additivity="false" (该属性默认值为true)去掉,再来打印日志
就会发掘,Slave 的日志打了两遍,况且连 debug 级别的都打了两遍,咱们能够把这种规律理解为继承,子类执行一遍,父类还能在执行一遍,但 leve 属性还是会采用子类而非父类的。
另一点便是咱们把 root 标签删除了,root 其实是一个顶级的 logger , 其他的logger都能够视为它的子类,倘若哪些logger存在没涵盖的地区,或其无指定 additivity="false" ,那最后root的设置就会被运用。例如咱们将设置改为如下: <logger name="com.zhanfu" level="INFO"> <appender-ref ref="file" /> <appender-ref ref="console"/></logger><logger name="com.zhanfu.child" level="DEBUG"> <appender-ref ref="file" /> <appender-ref ref="console"/></logger><root level="WARN"> <appender-ref ref="console"/></root>
结果掌控台的输出日志,Main会重复两次,Slave 会重复三次,如下
然则由于咱们的 root 只配置了掌控台输出,因此日志文件里还是不会变的 2. Log4j 21. 引用针对spring-boot项目,除了引用 Log4j 2 咱们还需要先剔除 Logback 的引用,针对普通项目,咱们只需直接引用就可。但重视咱们的原则,经过 SLF4J 来运用 Log4j2,因此引用下面这个包 <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.13.3</version></dependency>
其内包括 Log4j2 的实现,和 SLF4J 的 API,如下: 2. 配置Log4j2 的配置规律和 logback 是类似的,仅有些细节区别,例如Logger 的首字母大写等等,最后咱们写下这般一个 log4j2.xml <?xml version="1.0" encoding="UTF-8"?><Configuration status="INFO" monitorInterval="30"> <Properties> <Property name="logPath">logs</Property> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n" /> </Console> <RollingFile name="File" fileName="${logPath}/example.log" filePattern="${logPath}/example-%d{yyyy-MM-dd}-%i.log"> <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n"/> <Policies> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy max="4"/> </RollingFile> </Appenders> <Loggers> <Logger name="com.zhanfu.child" level="DEBUG"> <AppenderRef ref="File"/> <AppenderRef ref="Console"/> </Logger> <Logger name="com.zhanfu" level="INFO"> <AppenderRef ref="File"/> <AppenderRef ref="Console"/> </Logger> <Root level="WARN"> <AppenderRef ref="Console" /> </Root> </Loggers></Configuration>Properties部分定义了一个 logPath 属性,方便在其他地区引用。 Appenders定义了两个 Appender:Console 和 RollingFile,分别将日志输出到掌控台和文件中。RollingFile 运用了 RollingFileAppender,并设置了日志滚动策略和默认的备份文件数量。 Loggers定义了三个 Logger:com.zhanfu.child 的日志级别为 DEBUG,com.zhanfu 的日志级别为INFO,Root Logger 的日志级别为 WARN。并指定了两个 Appender:Console 和 File。 3. 演示因为咱们的配置规律没变,因此日志的结果还是同样的: 3. 对比
Log4j2和Logback都是Java应用程序中最流行的日志框架之一。它们均具备高度的可配置性和运用灵活性,并供给了一系列有用的功能,例如异步日志记录和日志过滤等。下面从配置遍历性、功能性、性能等方面进行比较和总结。 配置遍历性Logback的配置文件格式相对简单,易于阅读和修改。它支持符号来引用变量、属性和环境变量等。另外,它还支持要求日志记录(按照日志级别、日志记录器名叫作或时间等),以及滚动文件的体积或日期等。
Log4j2的配置文件格式较繁杂,但它在配置文件中供给了海量的选项来掌控日志记录。它支持在配置文件中直接声明上下文参数、过滤器、输出器和Appender等,这使得它的配置更加灵活。另外,Log4j2还支持异步日志记录、日志事件序列化和性能优化等。
总体来讲,两者都很好地支持了配置遍历性,但Log4j2供给了更加多的选项和更高的灵活性。 功能性Logback供给了一系列基本的日志记录功能,例如异步Appender、滚动文件和GZIP压缩等。它还支持与SLF4J一块运用,能够很容易地与其他日志框架集成。
Log4j2供给了更加多的高级功能,例如异步日志记录、性能优化和日志事件序列化等。它还支持Lambda表达式,能够使日志记录器更加简洁和易读。另外,Log4j2还支持Flume和Kafka等大数据处理框架,能够方便地将日志记录发送到这些框架中。
总体来讲,Log4j2供给了更加多的高级功能,并且能够更好地与大数据处理框架集成。 性能Logback的性能很好,能够处理高吞吐量的日志记录。它采用了异步记录器,利用了多线程来加强性能。
Log4j2在性能方面更加强大。它运用了异步记录器和多线程,还引入了RingBuffer数据结构和Disruptor库来加速日志事件的传递和处理。这使得它比Logback拥有更高的吞吐量和更低的延迟。
综上所述,Log4j2在配置灵活性、功能性和性能方面都比Logback更为强大。但倘若需要轻量级的日志框架或只需要基本的日志记录功能,Logback亦是一个不错的选取
但倘若咱们同期引用了这两者,会报错吗?还是会运用其中的某一个? <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.12</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.13.3</version></dependency>
能够看到,SLF4J 发掘了系统中同期存在两个插件框架,并最后选取了运用 Logback
总结
学习完本文,你应当对此刻这几个常用框架的有所认识,并能基本应用了。此次咱们无讲源码,亦无深入的讲其配置及进阶运用,这些咱们会在后面慢慢学习。但此刻我期盼你能晓得的是。必定要写好日志,必定要写好日志,必定要写好日志。重要的事情说三遍!这是区别新人和老鸟的一个重要依据,亦是让自己排查问题更容易的不二法门!
另一,此刻非常多中间件都自己引用了日志插件,咱们做为一个整体工程在运用中间件时,要即时发掘并处理插件冲突,避免咱们自己的日志配置失效,这亦是一个程序员该重视的点。 源自:blog.csdn.net/u011709538/article/details/132363370
13 秒插进 30 万条数据,这才是批量插进正确的姿势!
和 XShell 说再见,这款 SSH 工具足够惊艳,还支持网页版!
我进入银行科技部半年,已然丧失跳槽的能力了!
程序员:有那些话一听就晓得对方很水 (段子)
23 届校招技术岗薪资汇总
面试官问我 ,try catch 应该在 for 循环里面还是外面?
再见了 shiro 近期面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666领取,更加多内容持续奉上。明天见(。・ω・。
|