aop实现日志记录
- 记录工具
- 切面
- logback配置
- 测试
记录工具
目标:
统计rest接口请求参数,请求地址,请求IP,响应数据,响应时间。方便后续问题排查,以及性能的总体分析。
- 基于springboot
- 会使用面向切面编程
- 基于logback,使用slf4j
切面
package top.lel.ft.config.aop;import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
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.MDC;
import org.springframework.stereotype.Component;
import top.lel.ft.common.utils.IpUtils;
import top.lel.ft.framework.BaseController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;/*** @author echo lovely* @description web url 日志记录* @since 2022/6/14 09:22*/@Slf4j
@Aspect
@Component
public class UrlLogAspect {// 使用ThreadLocal记录第一次的请求时间, 统计接口响应时间// 也可用环绕通知统计接口响应时间final ThreadLocal<Long> tl = new ThreadLocal<>();// 作用于controller的切点@Pointcut("execution(public * top.lel.ft.controller.*.*(..))" +"|| execution(public * top.lel.ft.web.*.*(..))")public void controllerAspect() {}// 记录IP,日志,请求参数@Before(value = "controllerAspect()")public void methodBefore(JoinPoint joinPoint) {List<String> paramList = new ArrayList<>();Object[] args = joinPoint.getArgs();if (args != null) {for (Object arg : args) {if (!isOriginType(arg)) {paramList.add(JSON.toJSONString(arg));}}}String methodName = joinPoint.getSignature().getName();HttpServletRequest httpReq = BaseController.getServletReq();String ipAddress = IpUtils.getIpAddr(httpReq);// 日志增强实现MDC.put("ip", ipAddress);MDC.put("traceId", UUID.randomUUID().toString().replaceAll("-", ""));log.debug("请求路径: {}, 请求方法: {}, 请求参数: {}, 请求IP: {}", httpReq.getRequestURI(), methodName, String.join(", ", paramList), ipAddress);tl.set(System.currentTimeMillis());}@AfterReturning(pointcut = "controllerAspect()", returning = "retVal")public void afterRet(Object retVal) {log.info("响应时间: {}毫秒, 响应内容: {}", System.currentTimeMillis() - tl.get(), JSON.toJSONString(retVal));tl.remove();MDC.clear();}/*** 判断是原生servlet容器*/private boolean isOriginType(Object e) {return e instanceof HttpServletRequest || e instanceof HttpSession;}
}
logback配置
application.yaml 配置logback.logPath
<configuration><!-- 日志存放路径 --><springProperty scope="context" name="logPath" source="logback.logPath"/><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /><!-- 日志输出格式 --><!-- traceId为日志跟踪id,ip为请求ip,见切面MDC工具 --><property name="log.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%X{traceId}] %boldMagenta([%X{ip}] [%thread]) %highlight(%-5level) %clr(%logger{50}){pink} - %cyan(%msg%n)" /><!-- <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{ip}] [%thread] %-5level %logger{20} - %msg%n" />--><springProperty scope="context" name="appName" source="spring.application.name"/><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder></appender><!-- 系统日志输出 --><appender name="server" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/${appName}_server.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${logPath}/%d{yyyyMMdd}/${appName}_server.%i.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>60</maxHistory><!-- 文件最大50M --><TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><MaxFileSize>50MB</MaxFileSize></TimeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!-- 过滤的级别 --><level>DEBUG</level></filter></appender><appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/${appName}_error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${logPath}/%d{yyyyMMdd}/${appName}_error.%i.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>60</maxHistory><!-- 文件最大50M --><TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><MaxFileSize>50MB</MaxFileSize></TimeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!-- 过滤的级别 --><level>ERROR</level></filter></appender><!--异常日志监控告警 todo: 实现WarningAppender, 如进行告警邮件通知.--><!--ch.qos.logback.core.UnsynchronizedAppenderBase(ILoggingEvent logEvent)--><!--<appender name="warningAppender" class="top.lel.ft.common.warning.WarningAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>ERROR</level></filter></appender>--><!-- Spring日志级别控制 --><logger name="org.springframework" level="warn" /><root level="info">
<!-- <appender-ref ref="warningAppender" />--><appender-ref ref="STDOUT" /></root><root level="info"><appender-ref ref="server" /><appender-ref ref="error" /></root>
</configuration>
测试
or