前言
日志是开发过程中必不可少的模块,出现异常后可以通过查看日志看排查原因
使用
常用日志框架
JDK Logging
JDK提供的日志框架,由于自身有些局限性,故不太流行
Commons Logging
Commons Logging是一个第三方日志库,它是由Apache创建的日志模块。Commons Logging的特色是,它可以挂接不同的日志系统,并通过配置文件指定挂接的日志系统。默认情况下,Commons
Loggin自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Loggin
Log4j
Apache的提供的一种日志实现,现已不推荐使用
SLF4J
SLF4J类似于Commons Logging,也是一个日志接口,比Commons Logging更加好用,现在流行程度也更高
Logback
Logback是由log4j创始人设计的另一个开源日志组件,性能比Log4j要高很多
Log4j2
Log4j的重构版,性能提升很多,尤其在多线程环境下,性能也高于Logback
一般使用SLF4J提供的接口,引入Logback或者Log4j2
集成
SpringBoot2默认已经集成了logback,如果不想修改可直接在resource中新建logback.xml进行配置即可。
不过一般都推荐使用log4j2(SpringBoot2高版本已经不再支持log4j),故可以在pom.xml中修改引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
//如果没有引入web模块,则 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
</dependencies>
|
在resource中新建log4j2-spring.xml(或者log4j2.xml)进行配置即可
喜欢yml格式的话,加入支持yml格式
1 2 3 4
| <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> </dependency>
|
在resources目录下新建log4j2.yml进行配置(推荐使用yml格式)
相比与其他的日志系统,log4j2丢数据这种情况少;disruptor技术,在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生
应用
需要注意的时,一般推荐使用slf4j(这里使用了设计模式中的外观模式或者叫门面模式),方便灵活的替换日志组件:
1 2 3 4 5 6 7 8
| import org.slf4j.Logger; import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(TestService.class);
logger.info("xxxxxxxxxxxxxxx");
|
日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,只会输出大于或等于设置级别的内容
配合lombok
如果项目使用了lombok插件,则引用可直接在类上注解即可:
1 2 3 4 5 6 7
| @Slf4j public class Main { public static void main(Strin[] args) { log.error("Something else is wrong here"); } }
|
拼接字符串
SLF4J的日志接口可以这样来拼接字符串:
1 2
| int score = 99; logger.info("Set score {} for Person {} ok.", score, p.getName());
|
使用日志的好处
如果使用日志有很多好处:
- 可以设置输出样式,避免自己每次都写”ERROR: “ + var;
- 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
- 可以被重定向到文件,这样可以在程序运行结束后查看日志;
- 可以按包名控制日志级别,只输出某些包打的日志;
- 可以输出到不同的目的地:
- console:输出到屏幕;
- file:输出到文件;
- socket:通过网络输出到远程计算机;
- jdbc:输出到数据库
- ……
日常开发中有些童鞋在跟进bug时会习惯性的使用**System.out.println()**来打印log日志,然后再删除掉,下次再出现问题再打印,可以自己考虑哪个更好
log42j官方配置参考
springboot推荐配置
控制台打印的日志配上颜色
在配置文件的PatternLayout标签里指定disableAnsi=”false” noConsoleNoAnsi=”false”
1 2 3 4
| <Console name="Console" target="SYSTEM_OUT"> <PatternLayout disableAnsi="false" noConsoleNoAnsi="false" pattern="%style{%d{ISO8601}} %highlight{%-5level }{ERROR=Bright RED, WARN=Bright Yellow, INFO=Bright Green, DEBUG=Bright Cyan, TRACE=Bright White}[%style{%t}{bright,blue}] %l: %msg%n%style{%throwable}{red}" /> </Console>
|
动态设置日志保存路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?xml version="1.0" encoding="UTF-8"?> <Configuration> <properties> <property name="LOG_HOME">${sys:user.dir}</property> <property name="FILE_FOLDER">spring-boot-test</property> </properties> <Appenders>
<Console name="Console" target="SYSTEM_OUT"> <PatternLayout disableAnsi="false" noConsoleNoAnsi="false" pattern="%highlight{%d [%t] %-5level %l: %msg%n%throwable}{ERROR=Bright RED, WARN=Bright Yellow, INFO=Normal, DEBUG=Bright Blue, TRACE=Bright White}"/> </Console>
<RollingFile name="RollingFile" fileName="${LOG_HOME}/${FILE_FOLDER}/logs/logger-log4j2.log" filePattern="${LOG_HOME}/${FILE_FOLDER}/logs/$${date:yyyy-MM}/logger-log4j2-%d{-dd-MMMM-yyyy}-%i.log.gz"> <PatternLayout> <pattern>%d %p %C{1.} [%t] %m%n</pattern> </PatternLayout> <Policies>
<OnStartupTriggeringPolicy/> <SizeBasedTriggeringPolicy size="10 MB"/> <TimeBasedTriggeringPolicy/> </Policies> </RollingFile> </Appenders>
<Loggers> <Root level="info"> <AppenderRef ref="Console"/> <AppenderRef ref="RollingFile"/> </Root>
<Logger name="org.jonesun" level="debug"/> </Loggers>
</Configuration>
|
异步日志
官方建议一般程序员查看的日志改成异步方式,一些运营日志改成同步。日志异步输出的好处在于,使用单独的进程来执行日志打印的功能,可以提高日志执行效率,减少日志功能对正常业务的影响。
Log4j2中的异步日志实现方式有两种:
- AsyncAppender采用了ArrayBlockingQueue来保存需要异步输出的日志事件
- AsyncLogger则使用了Disruptor框架来实现高吞吐
AsyncAppender
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> ... <Async name="asyncTest" blocking="true"> <AppenderRef ref="RollingFile"/> </Async> </Appenders>
<Loggers> ... <Root level="info"> <AppenderRef ref="Console"/> <AppenderRef ref="asyncTest"/> </Root> </Loggers> </Configuration>
|
AsyncLogger
AsyncLogger需要加载disruptor-3.0.0.jar或者更高的版本(pom.xml):
1 2 3 4 5 6
| <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> </dependency>
|
配置文件loggers改为(或者新增Appender再配置给异步):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <Loggers>
<Logger name="com.jonesun" level="trace"/>
<asyncRoot level="info" includeLocation="true"> <appender-ref ref="Console" /> <appender-ref ref="RollingFile" /> </asyncRoot> </Loggers>
|
高阶
随着业务量的提高,普通日志已经满足不了需求,此时可以引入Elasticsearch,使用ELK
来搭建日线日志系统,,这几个组件都挺能抢内存的,elasticsearch默认就要2g内存,线上机器性能不够的话,还是乖乖使用原始log收集
log4j2 高危漏洞
最近全网爆出log4j2存在一个高危漏洞, 故如果项目中引用了log4j2,则需检查是否是2.15.0+, 低于这个版本的尽快更新下
熟悉Spring Boot组件的版本机制的话,其实这个并不需要特地发版解决。只需要pom.xml加个简单配置就可以了:
1 2 3
| <properties> <log4j2.version>2.15.0</log4j2.version> </properties>
|