市面上的日志框架
日志实现 | |
---|---|
Log4j JUL(java.util.logging) Log4j2 Logback |
补充说明:Logback, Log4j , Log4j2都出于同一作者之手, 但Logback更新。
在使用日志时, 都是选一个左边门面(抽象层)、再选一个右边的实现;该用方法时只用右边门面方法 ,
Spring框架选用JCL日志,需要引用commons-logging.jar , SpringBoot框架选用 SLF4j和logback;
如何让系统中所有的日志都统一到slf4j;
1、将系统中其他日志框架先排除出去 (比如删除log4j.jar;
2、用中间包来替换原有的日志框架 (比如添加log4j-over-slf4j.jar);
为什么还要jcl-over-slf4j.jar ,log4j-over-slf4j.jar, jul-to-slf4j.jar?
这是一个历史遗留问题 , 因为一个项目只一般不可能只有一个日志框架 , 比如Spring 使用commons-logging , hibernate使用 jboss-logging , MyBatis , XXX等其它框架又用了各自的日志框架, 那么为了把各种框架都整合到一个日志中去输出 , 所以有了这些需要替换原始功能的jar包
SpringBoot日志关系
SpringBoot使用它来做日志功能;
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency>
底层依赖关系图
总结:
1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录;
2)、SpringBoot也把其他的日志都替换成了slf4j;
3)、中间替换包(以jck-over-slf4j.jar为例),其实内部被换成了SLF4JLogFactory
@SuppressWarnings("rawtypes") public abstract class LogFactory { static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = "http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j"; static LogFactory logFactory = new SLF4JLogFactory();
4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移<exclusion>除掉,比如Spring框架用的是commons-logging;
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
日志测试
logback测试用例
@RunWith(SpringRunner.class) @SpringBootTest public class LoggingTest { Logger logger = LoggerFactory.getLogger(getClass()); @Autowired ApplicationContext ac;//注入上下文 @Test public void contextLoads() { //日志的级别; //由低到高 trace<debug<info<warn<error //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效 logger.trace("这是trace日志..."); logger.debug("这是debug日志..."); //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别(用了info级别) logger.info("这是info日志..."); logger.warn("这是warn日志..."); logger.error("这是error日志..."); } public ApplicationContext getAc() { return ac; } public void setAc(ApplicationContext ac) { this.ac = ac; } }
application.properties配置文件
# springboot日志的配置文件,本处由application.properties中的spring.profiles.active=logging来启用本文件 # 指定log文件,也可以写固定的绝对路径, 如果file和path都指定了,那么file优先级更高 #logging.file=springboot02.log # 指定log路径,默认使用使用spring.log作为文件名, 如果以/开头,则在当前磁盘的根目录下创建路径,如果不以/开头,则在当前应用目录下创建日志路径 logging.path=/springbootlog01/logs # 日志占位符说明 # %d表示日期时间 # %thread表示线程名 # %-5level:级别从左显示5个字符宽度,左对齐 # %logger{50} 表示logger包路径加类名字最长50个字符 # %msg:日志消息 # %n是换行符 # 在控制台输出的日志的格式 logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n # 指定文件中日志输出的格式 logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n # 指定com下的所有日志输出级别为trace logging.level.com=trace
logback默认配置位于该目录: spring-boot-1.5.14.RELEASE.jar!/org/springframework/boot/logging/logback/defaults.xml和base.xml
日志配置
日志系统 | 自定义文件 | 说明 | |
---|---|---|---|
Logback | logback.xml | 直接被日志框架识别,不经过springboot , 不支持SpringBoot的profile功能. | |
Logback | logback-spring.xml |
日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能 |
|
Log4j2 | log4j2.xml | ||
Log4j2 | log4j2-spring.xml |
||
JDK (Java Util Logging) | logging.properties |
logback-spring.xml 使用SpringBoot的profile功能如下:
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <!-- 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> <layout class="ch.qos.logback.classic.PatternLayout"> <springProfile name="dev"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern> </springProfile> <springProfile name="!dev"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern> </springProfile> </layout> </appender>
日志切换
slf4j+log4j的方式 (已亲测) processon地图 | log4j2 (未亲测) | ||
pom.xml内容 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排队具体的日志实现logback--> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> <!--排队log4j的替换包--> <exclusion> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </exclusion> </exclusions> </dependency> <!--使用log4j的适配层,它底层依赖log4j.jar,所以不用再导log4j.jar了,在classpath根目录放一个log4j.properties即可 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> |
|
说明 | 此处直接去掉了slf4j的实现 , 必须换成log4j的实现 | 此处引用的是spring-boot-starter-log4j2场景启动器,替换了spring-boot-starter-logging场景启动器的功能 | |
自己喜欢的简易风
日志配置
# 在控制台输出的日志的格式 logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} ↓%n%msg%n
日志格式
2019-12-04 09:59:37 ↓ Root WebApplicationContext: initialization completed in 813 ms 2019-12-04 09:59:38 ↓ Initializing ExecutorService 'applicationTaskExecutor' 2019-12-04 09:59:38 ↓ Tomcat started on port(s): 8080 (http) with context path '/es' 2019-12-04 09:59:38 ↓
在日志上打印请求唯一log标识
重点语句
org.slf4j.MDC;
MDC.put(TRACE_ID, traceId);
MDC.remove(TRACE_ID);
使用拦截器实现
package com.interceptors.logtag.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; /** * <p>日志拦截器</p> * * @author zetting * @date 2018/8/27 11:18 */ @Component public class LogInterceptor extends HandlerInterceptorAdapter { private Logger log = LoggerFactory.getLogger(this.getClass()); /** * 日志跟踪标识 */ private static final String TRACE_ID = "TRACE_ID"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String traceId = UUID.randomUUID().toString(); if (StringUtils.isEmpty(MDC.get(TRACE_ID))) { MDC.put(TRACE_ID, traceId); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { MDC.remove(TRACE_ID); } }
【Springboot之搜索日志妙招】在日志上打印请求唯一log标识==>https://www.jianshu.com/p/b74ade8bbb57