最近在做项目的时候用到logback作为项目日志,而抛弃了log4j。具体为什么用logback,我总结了以下几点:
1.log4j和logback的创作者是同一个人,logback是log4j的升级版,是对log4j核心的一个封装。
2.性能提升,在某些特定场景上执行速度提升10倍以上,同时初始化内存更小
3.扩展文档,丰富的并且详细的文档和不断的更新,可在官网上查看
4.Filters(过滤器),过滤器的使用又是logback的一大特色,在使用log4j的时候,当我们遇到问题时,我们需要降低日志等级,但这时又会有大量的日志输出。但logback可以根据用户设定不同的日志级别,比如一个用户的日志级别时debug,其他的用户可以时error,这里要了解MDCFilter
5.日志自动清除和日志自动压缩,自动清除通过设置TimeBasedRollingPolicy 或者 SizeAndTimeBasedFNATP的 maxHistory 属性,自动压缩RollingFileAppender可以在回滚操作中,自动压缩归档日志文件,回滚是异步的
6.自动载入配置文件,Logback-classic可以在配置文件被修改后,自动重新载入
上面做的是理论的介绍,现在我们就通过具体的实例来使用logback框架
1.配置maven依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.logback-extensions/logback-ext-spring -->
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.3</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<!--这里一定要配置slf4j-simple,要不会报缺少logback.groovy文件-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
注意事项:配置了logback的jar包后,一定不要再配置log4j的jar包,因为这样会发生冲突,有可能导致输出的日志文件中没有内容,如果其他的jar包中包含的有log4j的jar包我们要exclusion,比如我们引入的shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
<version>1.2.2</version>
</dependency>
2.配置文件
首先在web.xml中配置,用于加载logback.xml和过滤
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:logback.xml</param-value>
</context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>
注意:一定要有listener,要不logback不生效,还有logback一定要放在rescore文件夹下,要是放在webapp下会加载不到
3.配置logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration debug="true">
<!--设置上下文名称-->
<contextName>logback</contextName>
<!--设置全局日志输出格式-->
<property name="CONSOLE_LOG_PATTERN" value="%X{uuid} %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger:%-3L - %msg%n" />
<!--设置全局日志输出路径-->
<!--value指定日志输出路径,以服务器输出路径为准-->
<property name="CONSOLE_LOG_PATH" value="E:/log" />
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d:表示日期 %thread:表示线程名 %-5level:级别从左显示5个字符宽度 %msg:日志消息 %n:是换行符-->
<pattern>%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger:%-3L) - %cyan(%msg%n)</pattern>
</layout>
</appender>
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--指定了file属性,当天的文件名为file属性值-->
<file> ${CONSOLE_LOG_PATH}/neoinfo.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--拦截到error级别日志-->
<level>INFO</level>
<!--匹配成功,禁止打印-->
<onMatch>DENY</onMatch>
<!--匹配失败,放行,执行打印操作-->
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<append>true</append>
<prudent>false</prudent>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>
${CONSOLE_LOG_PATH}/neoinfo.%d.log
</fileNamePattern>
<!-- 最多保存15天的历史,最多占用10G的空间 -->
<maxHistory>15</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--指定了file属性,当天的文件名为file属性值-->
<file> ${CONSOLE_LOG_PATH}/neoinfo_error.log</file>
<!--只拦截error日志级别-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--拦截到error级别日志-->
<level>ERROR</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<append>false</append>
<prudent>false</prudent>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>
${CONSOLE_LOG_PATH}/neoinfo_error.%d.log
</fileNamePattern>
<!-- 最多保存15天的历史,最多占用10G的空间 -->
<maxHistory>15</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- 打印sql语句 -->
<logger name="com.dao" level="DEBUG" />
<!--日志级别-->
<root level="INFO">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileInfoLog" />
<appender-ref ref="fileErrorLog" />
</root>
</Configuration>
4.配置aop类
package com.aop;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.UUID;
/**
* 日志输出
* */
@Aspect
@Component
public class LogAspect {
private static Logger log = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping)")
public void webLogs() {
}
@Before("webLogs()")
public void doBefore(JoinPoint joinPoint) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
log.info("DATE:"+new Date());
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("IP : " + request.getRemoteAddr());
log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = (String) enu.nextElement();
log.info("name:{" + name + "},value:{" + request.getParameter(name) + "}");
}
}
@AfterReturning(returning = "ret", pointcut = "webLogs()")
public void doAfterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"));
try {
if (log.isDebugEnabled()) {
log.debug("returnValue={}", mapper.writeValueAsString(ret));
}
} catch (Exception ex) {
log.error("拦截器异常", ex);
}
}
}