文章目录
引言:
对于java中,使用日志,估计大多数人一听就是很懵逼。认识比较片面,而且对于各种概念很模糊。尤其是对于日志的最佳实践,充满着各种稀奇古怪的习惯。本文,借助Spring-boot 集成主流java日志框架,logback和log4j2,给大家做一个简单的示例。
先有一个直观的代码印象,如果大家有兴趣想要更深地了解日志的最佳实践。请移步我的另一篇博客《Java专家之路(六)—日志知识体系的总结–以java程序中的日志为例》
想随意切换,集成主流java日志框架的实现?
这个需求,实现很简单,依赖并使用日志门面框架slf4J进行编程,而不是依赖具体的日志框架。如:logback、log4j、log4j2等
想在不同环境,使用不同的日志配置 ?
spring-boot提供了一种很简单的日志配置标签< springProfile >,支持多环境的日志配置。如下所示
<!-- 测试环境 -->
<springProfile name="test">
<!-- 每天产生一个文件 -->
<appender name="TEST-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 文件路径 -->
<file>${TEST_FILE_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 文件名称 -->
<fileNamePattern>${TEST_FILE_PATH}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 文件最大保存历史数量 -->
<MaxHistory>100</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${PATTERN}</pattern>
</layout>
</appender>
<root level="info">
<appender-ref ref="TEST-FILE" />
</root>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<appender name="PROD_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${PRO_FILE_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${PRO_FILE_PATH}/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
<MaxHistory>100</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${PATTERN}</pattern>
</layout>
</appender>
<logger name="com.dynamic.springbootlogback" level="info"/>
<root level="warn">
<appender-ref ref="PROD_FILE" />
</root>
</springProfile>
想知道,日志的最佳实践 ?
- 不同环境,不同的配置。
*开发环境:
*测试环境:
*生产环境: - 按类型(级别),将日志分类输出
- 定期清理
- 日志严格执行合理正确的分级
- 自定义日志输出格式
- 伺机而动(把握记录日志的时机)
- 警惕性能陷阱(日志,少而精)
想在spring-boot下优雅地打印日志(AOP)?
在我们的程序中,有很多的日志,例如:异常日志、入参值和返回值都很重要,是程序运行调试的重要依据和参考。而且,应该在每个方法上都要记录,那么问题就来了。这样的系统日志,重复的代码,如何优雅地处理?
答案是:我们可以使用AOP,实施面向切面的编程。将这种和业务无关的代码,剥离到一个切面类中。通过动态代理的方式,织入到我们的代码当中来。以此来增强我们的目标方法。
想利用SLF4j的MDC机制,定制日志输出格式和内容 ?
MDC,是什么?Mapped Diagnostic Context,映射调试上下文。是 log4j 和 logback 提供的,一种方便在多线程条件下记录日志的功能。
很多时候,日志框架并不能提供足够多的有用的信息,以辅助我们进行更好的定位问题。
需求一:排查问题用得最多的方式是查看日志,但是在现有系统中,各种无关日志穿行其中,导致我没办法快速的找出用户在一次请求中所有的日志。我们想要在所有的日志标准输出内容中,加上session_id , 或者user_id 等业务相关的日志信息。
需求二:想要快速过滤一次请求的所有日志,并通过装饰器模式使得MDC工具在异步线程里也能生效。有了MDC,再通过AOP技术对所有的切面植入requestId,就可以将整个系统的任意流程的日志过滤出来。
MDC代码片段一:
备注:put值到threadlocal(MDC的实现,使用的就是threadlocal)中,所以出于线程安全和使用规范考虑,一定要在线程结束后,及时清除线程变量。
@Before("weblog()")
public void doBefore(JoinPoint joinPoint) {
//target
LOGGER.debug("进入前置通知:dorBefore方法");
//记录请求的内容:
// 请求的IP,方式,url,
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
// 解析请求报文,使用MDC,打印日志,也可在前置通知中获取
MDC.put("SESSION_ID", req.getSession().getId());
MDC.put("IP", req.getRemoteAddr());
MDC.put("URL", req.getRequestURL().toString());
Object[] objects = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
LOGGER.debug("signature ={}", signature.toString());
LOGGER.debug("方法:{}", signature.getName());
LOGGER.debug("方法所在的类:{}", signature.getDeclaringType());
LOGGER.debug("方法所在的包:{}", signature.getDeclaringTypeName());
MethodSignature methodSignature = (MethodSignature) signature;
String[] parameterNames = methodSignature.getParameterNames();
LOGGER.debug("方法参数的名字:{}", Arrays.toString(parameterNames));
LOGGER.debug("方法的参数的值:{}", Arrays.toString(objects));
LOGGER.debug("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//wave
}
MDC代码片段二:清理线程变量MDC中的值
@After("weblog()")
public void doAfter(JoinPoint joinPoint) {
LOGGER.debug("whatever happened i very execute finally !");
MDC.clear();
}
配置示例:
<!-- console专用 日志内容输出格式 -->
<property name="PATTERN-CONSOLE" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight(|-%-5level) [%thread] [%X{URL}] [%X{IP}] [%X{SESSION_ID}] %c %cyan([%L-]) | %msg%n" />
想要自定义日志的格式?炫酷的个性化颜色?
logback提供了对日志内容的格式自定义的强大支持,包括自定义颜色字体、内容等。
注意:只能在console控制台使用
<!-- console专用 日志内容输出格式 -->
<property name="PATTERN-CONSOLE" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight(|-%-5level) [%thread] [%X{URL}] [%X{IP}] [%X{SESSION_ID}] %c %cyan([%L-]) | %msg%n" />
本文源码下载
https://github.com/bill4j/spring-boot-course/tree/develop/spring-boot-logconfig