日志门面,是门面模式的一个典型的应用。
门面模式(Facade Pattern),也称之为外观模式,其核心为:外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。
常用的日志框架
java.util.logging
java提供的原生日志功能,一般在项目中很少使用。
log4j
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以将日志信息输送到控制台、文件、GUI组件,甚至可以是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
一般我们使用自定义日志的输出格式,通过控制日志的打印级别,可以更加细致的控制日志的生成过程及结果。
logback
springboot默认的日志框架,目标是代替log4j。
log4j2
log4j2相较于log4j和logback来说功能更为强大,目标也是代替log4j和logback。
log4j2具有的优势:
Log4j2旨在用作审计日志记录框架。Log4j1.x和Logback 都会在重新配置时丢失事件。Log4j2不会。在 Logback中,Appender中的异常永远不会对应用程序可见。在Log4j 中,可以将 Appender 配置为允许异常渗透到应用程序。
Log4j2在多线程场景中,异步Loggers 的吞吐量比 Log4j 1.x 和 Logback 高10倍,延迟低几个数量级。
Log4j2对于独立应用程序是无垃圾的,对于稳定状态日志记录期间的 Web应用程序来说是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应时间性能。
Log4j2使用插件系统,通过添加新的Appender、Filter、Layout、Lookup和PatternConverter,可以非常轻松地扩展框架,而无需对Log4j 进行任何更改。
由于插件系统配置更简单。配置中的条目不需要指定类名。
支持自定义日志等级。
支持 lambda 表达式。
支持消息对象。
Log4j和Logback的Layout返回的是字符串,而Log4j2返回的是二进制数组,这使得它能被各种 Appender 使用。
Syslog Appender 支持 TCP 和 UDP 并且支持 BSD 系统日志。
Log4j2利用Java5 并发特性,尽量小粒度的使用锁,减少锁的开销。
既然有这么多日志框架,那么我们为什么要选择slf4j呢
那就是为了在应用中屏蔽掉底层日志框架的具体实现。这样的话,即使有一天要更换代码的日志框架,只需要修改jar包,最多再改改日志输出相关的配置文件就可以了。这就是解除了应用和日志框架之间的耦合。
SLF4J的作者就是Log4j的作者 Ceki Gülcü,这位大神专注日志开发五百年,先后开发了log4j,logback,log4j2。
logback在springboot中的应用
虽然log4j2的优势很多,但是由于slf4j+logback已经在市场有了成熟的应用经验,大部分项目还是采用这种配置。
由于springboot已经集成了logback,所以我们直接可以通过配置文件使用日志记录。
先看一个常见的配置模板:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
<contextName>logback</contextName>
<springProperty scope="context" name="log.path" source="logback.path"/>
<property name="projectName" value="sales"/>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %X{unique_trace_id} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/${projectName}_debug.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line -%X{unique_trace_id} %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${projectName}-debug-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/${projectName}_info.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line -%X{unique_trace_id} %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${projectName}-info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
</filter>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/${projectName}_warn.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line -%X{unique_trace_id} %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${projectName}-warning-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/${projectName}_error.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line -%X{unique_trace_id} %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${projectName}-error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<springProfile name="test">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
</configuration>
下面逐一解释各配置属性:
<configuration>
scan:扫描配置文件是否发生了变化,如果配置为true,会动态的扫描并加载配置文件
scanPeriod:扫描的时间间隔。
<contextName>
上下文名称,可自定义。
<springProperty>
定义logback.path。
<appender>
格式化日志输出。
file:定义我们的日志文件名称
encoder :定义日志输出格式
rollingPolice:滚动策略,一般日志会比较大,所以要进行压缩节省空间
<springProfile>
定义不同环境下的日志打印级别,比如测试环境我们就可以开启debug,而线上则要关掉
MDC
MDC是一种轻量级的日志跟踪工具,基于线程级别。log4j和logback都支持MDC。
先来看一个需求,对于一个支付系统,我们希望知道一笔订单从创建->发起支付->支付完成->后续逻辑的整个过程,又或者我们希望在日志中能清楚的看到每一个请求从开始到结束的轨迹,如何去设计呢?
利用MDC可以很容易的解决这个问题,而且配置十分简单。
1,利用过滤器往上下文中添加一个自定义标识,这里我们随机生成一个10位的数字
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
ContextHolder.init();
String requestId = RandomUtil.randomNumbers(10);
MDC.put(UserConstant.UNIQUE_ID, "uid:" + requestId);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
ContextHolder.clear();
MDC.clear(); //记得清理线程 否则会产生内存泄漏
}
}
2,在日志配置文件中添加这个标识
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line -%X{unique_trace_id} %msg%n</pattern>
<charset>UTF-8</charset
</encoder>
我们自定义的属性使用 %X + {} 来引用,%X{unique_trace_id}就是我们的自定义标识
ok,这样我们的日志中,每一个请求的日志记录都会包含这个请求id了,
至于怎么根据关键字查日志就不用多说了吧。
对于订单轨迹呢,我们可以用更讨巧一点的方式。
一般来说,订单号肯定是唯一的,那么我们就可以对订单的逻辑中都加上这个订单号来作为一个标识,这样根据关键字订单号直接查日志就能查到这笔订单的所有操作记录了
MDC.put(UserConstant.UNIQUE_ID, "订单号");
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/84858.html