日志介绍
记录日志的好处
1. 记录应用系统曰志主要有三个原因:记录操作轨迹、监控系统运行状况、回溯系统故障。
记录操作行为及操作轨迹数据,可以数据化地分析用户偏好,有助于优化业务逻辑,为用户提供个性化的服务。例如,通过 access.log 记录用户的操作频度和跳转链接,有助于分析用户的后续行为。
2. 全面有效的日志系统有助于建立完善的应用监控体系,由此工程师可以实时监控系统运行状况,及时预警,避免故障发生。
监控系统运行状况,是指对服务器使用状态,如内存、CPU 等使用情况,应用运行情况,如响应时间QPS等交互状态;应用错误信息,如空指针、SQL异常等的监控。例如,在CPU使用率大于60%,四核服务器中load 大于4时发出报警,提醒工程师及时处理,避免发生故障。
3. 当系统发生线上问题时,完整的现场日志有助于工程师快速定位问题。
例如当系统内存溢出时,如果日志系统记录了问题发生现场的堆信息,就可以通过这个曰志分析是什么对象在大量产生并且没有释放内存,回溯系统故障,从而定位问题。
日志框架
门面设计模式是面向对象设计模式中的一种,日志框架采用的就是这种模式,类似JDBC的设计理念。它只提供一套接口规范,自身不负责日志功能的实现。目的是让使用者不需要关注底层具体是哪个日志库来负责日志打印及具体的使用细节等。目前用得最为广泛的是slf4j
日志门面是一种门面设计模式,为了提高程序的扩展性给的一套类似于JDBC的接口,只需设定规则,具体的细节都留给对应实现
日志库
负责实现日志的相关功能,主流日志库有三个,分别是
log4j、log-jdk (java.util.logging.Logger)、logback是最晚出现的,与log4j同一个作者,是log4j的升级版且本身实现了slf4j的接口。
日志适配器
老工程直接使用日志库API完成日志打印,要改成业界标准的门面模式(如slf4j+logback),但是老工程代码打印日志地方太多难以改动,这时就需要一个适配器来完成从旧日志库的API到slf4j的路由,这样在不改动原有代码的情况下也能使用slf4j来统一管理日志(如:log4j-over-slf4j),后续自由替换具体日志库也不成问题。
日志体系
在开发的时候不应该直接使用日志实现类,应该使用日志的抽象层。。具体参考SLF4J官方
下图是SLF4J结合各种日志框架的官方示例,从图中可以清晰的看出SLF4J API永远作为日志的门面,直接应用与应用程序中。
spring boot的底层spring-boot-starter-logging可以看出,它依赖3个日志框架:slf4j、Logback和Log4j2。
它们的区别是:
logback和log4j是日志实现框架,就是实现怎么记录日志的。
slf4j-api提供了java所有日志框架的简单规范和标准(日志的门面设计模式)说白了就是一个日志API(没有实现类),它不能单独使用;故必须结合logback和Log4j2日志框架
注意:spring Boot采用了slf4j+logback的组合形式,Spring Boot也提供对JUL、log4j2.Logback提供了默认配置
注意:由于每一个日志的实现框架都有自己的配置文件,所以在使用SLF4j之后,配置文件还是要使用实现日志框架的配置文件。
日志的使用
日志级别和格式
从上面的分析,发现 Spring Boot默认已经使用了SLF4J+LogBack 。所以我们在不进行任何额外操作的情况下就可以使用SLF4J + Logback 进行日志输出。
日志的级别分为6种:
TRACE:运行堆栈信息,使用率低
DEBUG:程序员调试代码使用
INFO:记录运维过程数据
WARN:记录运维过程报警数据
ERROR:记录错误堆栈信息
FATAL:灾难信息,合并计入ERROR
编写测试类测试
package com.example.springboot.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { //获取日志对象 Logger Logger logger = LoggerFactory.getLogger(HelloController.class); @GetMapping("/hello") public String hello(){ logger.debug("debug..."); logger.info("info..."); logger.warn("warn..."); logger.error("error..."); return "Hello SpringBoot!"; } }
application.yml
server: port: 80 # 设置日志级别会给root根节点设置,代表整体应用的级别 #logging: # level: # root: debug # 设置组的日志级别或包的日志级别 logging: group: mygroup: com.example.springboot.controller level: root: warn # 给组设置日志级别 mygroup: debug
获取日志对象
导入Lombok依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
添加@Slf4j注解
package com.example.springboot.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController public class HelloController { @GetMapping("/hello") public String hello(){ log.debug("debug..."); log.info("info..."); log.warn("warn..."); log.error("error..."); return "Hello SpringBoot!"; } }
哪里需要用到日志,那里添加注解即可
日志格式
Spring Boot默认输出的日志格式如下:
日期和时间:毫秒精度,易于排序
日志级别:ERROR、WARN、INFO、DEBUG或TRACE
进程标识
— :用于区分实际日志开始的分隔符
线程名称:在方括号内
记录器名称:通常为源类名称
日志消息
日志格式配置
# 修改日志级别 logging: # 设置日志格式 pattern: #控制台输出格式 #格式化输出: %d :表示日期 %thread:表示线程名 # %-5level:级别从左显示5个字符宽度 %msg :日志消息 %n :是换行符 console: "[console]==%d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger - %msg%n" # 文件输出格式 file: "[file]===%d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger - %msg%n"
日志格式配置信息如下:
%d或者%date:指定日志的日期。默认是ISO8601的标准日期,相当于yyyy-MM-dd HH:mm:ss:SSS
%level:指定日志的级别: Trace > Debug > Info> Warn> Error
%logger:指定日志输出的包名+类名,{n}可以限定长度,比如: %logger{50}
%M:指定日志发生时的方法名
%L:指定日志调用时所在的行。线下运行的时候不建议使用此参数,因为获取代码的行号对性能有损耗
%m或者%msg:表示日志的输出的内容
%n :日志是否换行
%thread:打印线程的名字
彩色编码输出
如果您的终端支持ANSI,则使用颜色输出来提高可读性。您可以设置spring.output.ansi.enabled为支持的值以覆盖自动检测。
使用%clr转换字配置颜色编码。在最简单的形式中,转换器根据日志级别为输出着色,如以下所示:
%clr(%5p)
下表描述了日志级别到颜色的映射:
或者,您可以通过将其作为转换选项提供来指定应使用的颜色或样式。例如,要使文本变黄,请使用以下设置:
%clr(%d{yyyy-MM-dd HH:mm:ss:SSS}){yello}
支持的颜色有:blue cyan faint green magenta red yellow
因此我们可以自己编写日志的输出格式如下:
logging: pattern: console: "%d %clr(%p){green} ---[%16t] %m%n"
自定义日志输出
日志不能仅显示在控制台上,要把日志记录到文件中,方便后期维护查阅。
对于日志文件的使用存在各种各样的策略,例如每日记录,分类记录,报警后记录等。这里主要研究日志文件如何记录。
logging: file: name: tomcat.log
虽然使用上述格式可以将日志记录下来了,但是面对线上的复杂情况,一个文件记录肯定是不能够满足运维要求的,通常会每天记录日志文件,同时为了便于维护,还要限制每个日志文件的大小。下面给出日志文件的常用配置方式:
logging: logback: rollingpolicy: max-file-size: 2MB file-name-pattern: server.%d{yyyy-MM-dd}.%i.log
以上格式是基于logback日志技术设置每日日志文件的设置格式,要求容量到达2MB以后就转存信息到第二个文件中。文件命名规则中的%d标识日期,%i是一个递增变量,用于区分日志文件。
替换日志框架
查看官方文档,可以看到我们可以在 Log4j 和 logging 之间二选一。
官方是这样描述Log4j的:An alternative to spring-boot-starter-logging
翻译过来就是:spring-boot-starter-logging的替代方案。
所以下面来展示如何将 logging 替换为 Log4j
第一步:排除spring-boot-starter-logging依赖
<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>
第二步:添加spring-boot-starter-Log4j2依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>