Table of contents
Not much to say, directly on the code:
1.WebLogAspect
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.text.translate.AggregateTranslator;
import org.apache.commons.text.translate.CharSequenceTranslator;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class WebLogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ObjectMapper objectMapper;
ThreadLocal<Long> startTime = new ThreadLocal<>();
@Pointcut("execution(public * com.example.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 获取当前Http请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录请求内容
logger.info("URL:{}", request.getRequestURL().toString());
logger.info("HTTP_METHOD:{}", request.getMethod());
logger.info("IP:{}", request.getRemoteAddr());
String params = StringUtils.EMPTY;
Object[] args = joinPoint.getArgs();
if (args.length > 0) {
// 格式化参数,防止出现特殊字符
CharSequenceTranslator translator = new AggregateTranslator(
new CharSequenceTranslator[] { new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()),
new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE()) });
params = objectMapper.writeValueAsString(translator.translate(String.valueOf(args[0])));
}
logger.info("PARAMS:{}", params);
startTime.set(System.currentTimeMillis());
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("RESPONSE:{}", objectMapper.writeValueAsString(ret));
logger.info("SPEND TIME:{}ms", System.currentTimeMillis() - startTime.get());
}
}
In the above code example, we defined an WebLogAspect
aspect and @Pointcut
specified the pointcut as com.example.controller
a public method under all packages through annotations. In the aspect, we use @Before
annotations to record the request information, HttpServletRequest
obtain information such as the request URL, request method, and remote IP address in the object, and use it ObjectMapper
to serialize the method parameters into a JSON string. At the same time, we use @AfterReturning
annotations to record response information and request processing time.
Note: (1) The Controller imported by @Pointcut is under its own path, remember to modify it
(2) Relevant dependencies are searched and imported by themselves, so I won’t go into details here
Example of partial dependencies:
<!--AspectJ 开始-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!--AspectJ 结束-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
2. Configure log4j2.yml
Here we need to use the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 包含 mvc,aop 等jar资源 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 切换log4j2日志读取 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 配置 log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 加上这个才能辨认到log4j2.yml文件 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
Create a log4j2.yml file in the Resource directory:
# 共有8个级别,按照从低到高为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。
# intLevel值依次为0,100,200,300,400,500,600,700
# intLevel 值越小,级别越高
Configuration:
#日志框架本身的输出日志级别
status: WARN
#自动加载配置文件的间隔时间,不低于5秒
monitorInterval: 5
Properties: # 定义全局变量
Property: # 缺省配置(用于开发环境)。其他环境需要在VM参数中指定,如下:
#测试:-Dlog.level.console=warn -Dlog.level.xjj=trace
#生产:-Dlog.level.console=warn -Dlog.level.xjj=info
- name: log.level.console
value: info
- name: log.path
value: ./${project.name}_log
- name: project.name
value: daily
- name: log.pattern
value: "%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:-} [%15.15t] %-30.30C{1.} : %m%n"
Appenders:
Console: #输出到控制台
name: CONSOLE
target: SYSTEM_OUT
PatternLayout: #日志消息格式
pattern: ${log.pattern}
# 启动日志
RollingFile:
- name: ROLLING_FILE
fileName: ${log.path}/daily/${project.name}.log #输出文件的地址
filePattern: "${log.path}/daily/$${date:yyyy-MM-dd}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz" #文件生成规则
PatternLayout:
pattern: ${log.pattern}
Filters:
# 一定要先去除不接受的日志级别,然后获取需要接受的日志级别
ThresholdFilter: # 日志级别过滤器
- level: error # 日志级别
onMatch: DENY # 高于的拒绝
onMismatch: NEUTRAL # 低于的
- level: info
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy: # 日志拆分规则
size: "10MB"
TimeBasedTriggeringPolicy: # 按天分类
modulate: true
interval: 1
DefaultRolloverStrategy: # 单目录下,文件最多20个,超过会删除最早之前的
max: 20
# 错误日志
- name: EXCEPTION_ROLLING_FILE
ignoreExceptions: false
fileName: ${log.path}/exception/${project.name}_exception.log
filePattern: "${log.path}/exception/$${date:yyyy-MM-dd}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
ThresholdFilter:
level: error
#onMatch="ACCEPT" 匹配该级别及以上
#onMatch="DENY" 不匹配该级别及以上
#onMismatch="ACCEPT" 表示匹配该级别以下的级别
#onMismatch="DENY" 不表示匹配该级别以下的级别
onMatch: ACCEPT
onMismatch: DENY
PatternLayout:
pattern: ${log.pattern}
Policies:
SizeBasedTriggeringPolicy: # 日志拆分规则
size: "10MB"
TimeBasedTriggeringPolicy: # 按天分类
modulate: true
interval: 1
DefaultRolloverStrategy: # 文件最多100个
max: 100
# 警告日志
- name: WARN_ROLLING_FILE
ignoreExceptions: false
fileName: ${log.path}/warn/${project.name}_warn.log
filePattern: "${log.path}/warn/$${date:yyyy-MM-dd-dd}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
ThresholdFilter:
level: warn
#onMatch="ACCEPT" 匹配该级别及以上
#onMatch="DENY" 不匹配该级别及以上
#onMismatch="ACCEPT" 表示匹配该级别以下的级别
#onMismatch="DENY" 不表示匹配该级别以下的级别
onMatch: ACCEPT
onMismatch: DENY
PatternLayout:
pattern: ${log.pattern}
Policies:
SizeBasedTriggeringPolicy: # 日志拆分规则
size: "10MB"
TimeBasedTriggeringPolicy: # 按天分类
modulate: true
interval: 1
DefaultRolloverStrategy: # 文件最多100个
max: 20
# 用户行为日志
- name: ROLLING_FILE_USER
fileName: ${log.path}/user/user-${project.name}.log
filePattern: "${log.path}/user/$${date:yyyy-MM-dd}/user-${project.name}-%d{yyyy-MM-dd}-%i.log.gz"
PatternLayout:
pattern: ${log.pattern}
Filters:
# 一定要先去除不接受的日志级别,然后获取需要接受的日志级别
ThresholdFilter:
- level: error
onMatch: DENY
onMismatch: NEUTRAL
#onMismatch:NEUTRAL 交给下一个filter处理
- level: info
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy: # 日志拆分规则
size: "10MB"
TimeBasedTriggeringPolicy: # 按天分类
modulate: true
interval: 1
DefaultRolloverStrategy: # 文件最多100个
max: 100
Loggers:
Root:
# 共有8个级别,按照从低到高为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF 选择all则输出全部的日志
level: info
AppenderRef:
- ref: CONSOLE
- ref: ROLLING_FILE
- ref: EXCEPTION_ROLLING_FILE
- ref: WARN_ROLLING_FILE
Logger:
- name: exception
level: debug
additivity: true #追加
AppenderRef:
- ref: EXCEPTION_ROLLING_FILE
#监听具体包下面的日志
# Logger: # 为com.xjj包配置特殊的Log级别,方便调试
- name: com.example.aspect
additivity: false
level: info
AppenderRef:
- ref: CONSOLE
- ref: ROLLING_FILE_USER
Introduced in application.yml:
logging:
config: classpath:log4j2.yml