目录
话不多说,直接上代码:
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());
}
}
在上面的代码示例中,我们定义了一个WebLogAspect
切面,并通过@Pointcut
注解指定了切点为所有com.example.controller
包下的公共方法。在切面中,我们使用了@Before
注解来记录请求信息,在HttpServletRequest
对象中获取请求URL、请求方法和远程IP地址等信息,并使用ObjectMapper
将方法参数序列化为JSON字符串。同时,我们使用了@AfterReturning
注解来记录响应信息和请求处理时长。
注意:(1)@Pointcut那部分导入的Controller是自己路径下的,记得修改
(2)相关依赖自行查找导入,这里不再赘述
部分依赖示例:
<!--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.配置log4j2.yml
这里我们需要用到相关依赖如下:
<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>
在Resource目录下创建log4j2.yml文件:
# 共有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
在application.yml中引入:
logging:
config: classpath:log4j2.yml