最近借到一个需求,是真的难搞!要造一个工具包,自动收集请求日志,然后做分析.
卧槽,如果只是在项目中的logback.xml中配置分分钟根据类分个配置早搞定了,但是为了以后的方便,要集成到一个随时拔插的jar包里!
经过几番尝试,踩了不少坑,总算搞出来了!
一些依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
然后是aop+日志配置类
/**
* @author linnine
* @since 2022-03-28,v1.0.0
*/
@Aspect
@Component
//有配置才会启用
@ConditionalOnProperty(name = "req-log.file")
public class LogAspect {
private final Logger log= new RootLogger(Level.INFO);
//读取日志输出路径
@Value("${req-log.file}")
private String reqLogFile;
@Value("spring.application.name")
private String appName;
@PostConstruct
public void setLoggerFactory() throws IOException {
//从resources文件夹中读取配置文件
Resource resource = new ClassPathResource("log4j-aop.properties");
//创建配置类
PropertyConfigurator pc = new PropertyConfigurator();
Properties p = new Properties();
//解析配置文件
p.load(resource.getInputStream());
//添加日志输出路径
p.setProperty("log4j.appender.API_LOG.File", reqLogFile);
//开启控制台日志
String enableStdout = SpringContextUtil.getEnvironmentProperty("thread0.req-log.stdout");
if (StringUtils.equalsAny(enableStdout,"true","enabled")){
// ### 输出到控制台 ###
p.setProperty("log4j.rootLogger","INFO,stdout,API_LOG");
}
LoggerRepository lr1 = new Hierarchy(log);
//设置log
pc.doConfigure(p, lr1);
}
@Pointcut("execution(public * com.thread0..*.controller..*.*(..))")
public void controllerLog() {
//切点
}
@Before("controllerLog()")
public void doBefore(JoinPoint joinPoint) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
log.info(JsonUtil.toJsonStrSerializeNulls(new ReqLog(request, joinPoint,appName)));
}
}
最后是日志配置文件,要放在这个包里的resources目录下,文件名要叫 log4j-aop.properties
log4j.rootLogger = INFO,API_LOG
### 输出到日志文件 ###
log4j.appender.API_LOG = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.API_LOG.File = /Users/linnine/home/log/api/req-aop.log
log4j.appender.API_LOG.Append = true
log4j.appender.API_LOG.Threshold = INFO ## 输出INFO级别以上的日志
log4j.appender.API_LOG.layout = org.apache.log4j.PatternLayout
#只输出纯日志+换行 上面已经转成对象了,输出来就是一个json格式的日志
log4j.appender.API_LOG.layout.ConversionPattern = %m%n
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
补充一下日志对象
@Data
@Accessors(chain = true)
public class ReqLog {
//标签
private String tag;
//应用名
private String appName;
//请求时间
private String time;
//访问地址
private String url;
//请求方法
private String reqMethod;
private String sessionId;
private String ip;
//private String classMethod;
private Object[] allArgs;
public ReqLog(HttpServletRequest request, JoinPoint joinPoint,String appName) {
this.tag = "reqLog";
this.time = DateUtil.getNow(DateUtil.ALL_TIME_EN);
this.url = request.getRequestURL().toString();
this.reqMethod = request.getMethod();
this.sessionId=request.getRequestedSessionId();
this.ip = IpUtil.getIpFromRequest(request);
//与url功能类似 大可不必
//this.classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
this.allArgs = joinPoint.getArgs();
this.appName=appName;
}
}