切面收集日志
- 手机
- 2025-09-04 18:36:01

切面收集日志 1. 使用切面在方法执行完毕后收集日志1.1 注解1.2 切面1.3 使用1.4 YML中对该注解的操作开关 1. 使用切面在方法执行完毕后收集日志 1.1 注解 import cn.ac.ict.knowledge.graph.enums.annotation.OptTypeEnum; import java.lang.annotation.*; /** * 日志收集注解 * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogOperate { /** * 操作类型,默认为其他类型(OptTypeEnum.OTHER) * 用于记录方法执行的操作属于哪种预定义类型 * 这有助于在日志或审计过程中快速识别操作的类别 */ OptTypeEnum optTypeEnum() default OptTypeEnum.OTHER; /** * 操作详细描述,默认为空字符串 * 提供方法执行的具体操作描述,用于详细记录操作内容 * 在日志记录时,这将帮助开发者更好地理解操作的细节 */ String optDetail() default ""; =========================================================== import lombok.Getter; /** * 日志收集操作枚举 */ @Getter public enum OptTypeEnum { LOGIN, LOGOUT, QUERY, VIEW, EDIT, DELETE, IMPORT, EXPORT, ADD, DISABLE, ENABLE, OTHER } 1.2 切面
这个切面是先通过方法调用之后,通过方法是正常执行完成,还是跑出了异常,来进行不同的方式处理。最后在通过POST请求把封装好VO信息发送给远端接口,来保存本次方法调用的请求参数以及想要的参数。
import cn.ac.ict.knowledge.graph mon.auth.LoginUser; import cn.ac.ict.knowledge.graph.enums.annotation.OptTypeEnum; import cn.ac.ict.knowledge.graph.framework mon.pojo.CommonResult; import cn.ac.ict.knowledge.graph.utils.TimeUtil; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache mons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import ua_parser.Client; import ua_parser.Parser; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * 日志操作切面:拦截 @LogOperate 注解的方法,并将操作日志发送到日志采集服务器 * <p> * 支持 application-dev.yml 配置: * log-operate.enabled=true 开启日志切面 * log-operate.enabled=false 关闭日志切面 * */ @Slf4j @Aspect @Component public class LogOperateAspect { @Resource private LoginUser loginUser; /** * 应用id */ @Value("${xxx000.xxx:1}") private String appId; /** * 是否启用日志切面(通过 application.yml 配置) */ @Value("${log-operate.enabled:true}") private boolean logOperateEnabled; /** * 日志采集服务器地址 */ @Value("${xxx-xxx.host}") private String collectLogsHost; /** * 日志采集 API 路径 */ @Value("${xxx-xxx.collect}") private String collectReqPath; /** * 切点:拦截所有标注了 @LogOperate 的方法 */ @Pointcut("@annotation(xxx.xxx mon.annotation.log.LogOperate)") public void logOperatePointcut() { } /** * 方法执行完毕后,记录操作日志 * * @param joinPoint 连接点 * @param result 方法返回值 */ @AfterReturning(pointcut = "logOperatePointcut()", returning = "result") public void logOperation(JoinPoint joinPoint, Object result) { if (!logOperateEnabled) { return; } // 获取被拦截方法的签名信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取方法上标注的LogOperate注解 LogOperate annotation = signature.getMethod().getAnnotation(LogOperate.class); // 操作结果(0-成功;1-失败) LogOperateReqVO vo = buildLogOperateReqVO(joinPoint, annotation, result, "", "0"); log.info("======>[LogOperateAspect::sendLog]logOperateReqVO: {}", JSONUtil.toJsonStr(vo)); // 发送日志到日志收集服务器 sendLog(vo); } /** * 在抛出异常后执行的方法,用于记录操作日志 * 该方法使用了AspectJ的@AfterThrowing注解,以便在匹配的切点方法抛出异常后执行日志记录操作 * * @param joinPoint 切入点对象,提供了关于目标方法的信息,如方法签名和参数 * @param exception 抛出的异常对象,用于获取异常信息 */ @AfterThrowing(pointcut = "logOperatePointcut()", throwing = "exception") public void logOperationException(JoinPoint joinPoint, Exception exception) { // 如果日志记录功能未启用,则直接返回,不执行后续操作 if (!logOperateEnabled) { return; } // 获取方法签名,以便后续获取方法上的注解信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取方法上的LogOperate注解,用于获取日志记录相关的元数据 LogOperate annotation = signature.getMethod().getAnnotation(LogOperate.class); // 获取异常信息,如果异常消息为空,则使用空字符串 String exceptionMessage = StringUtils.isNotBlank(exception.getMessage()) ? exception.getMessage() : ""; // 构建日志对象 LogOperateReqVO vo = buildLogOperateReqVO(joinPoint, annotation, "", exceptionMessage, "1"); // 发送日志对象到日志系统 sendLog(vo); } /** * 构建日志对象 * * @param joinPoint 连接点 * @param annotation 方法上的 @LogOperate 注解 * @param result 方法返回值 * @param exception 异常信息 * @param optResult 操作结果 (0-成功, 1-失败) * @return 日志对象 */ private LogOperateReqVO buildLogOperateReqVO(JoinPoint joinPoint, LogOperate annotation, Object result, String exception, String optResult) { LogOperateReqVO vo = new LogOperateReqVO(); // 应用id vo.setAppId(appId); // 获取浏览器信息 String browserInfo = getBrowserInfo(); vo.setBrowserInfo(browserInfo); // 获取操作IP String ip = getIpAddress(); vo.setIp(ip); // 获取操作详情 String optDetail = annotation.optDetail(); vo.setOptDetail(optDetail); // 操作类型 vo.setOptType(annotation.optTypeEnum() != null ? annotation.optTypeEnum().name() : OptTypeEnum.OTHER.name()); // 操作类型说明 vo.setOptTypeAddition(annotation.optTypeEnum() != null ? annotation.optTypeEnum().name() : OptTypeEnum.OTHER.name()); // 操作结果 vo.setOptResult(optResult); // 异常信息(仅在失败时记录) vo.setException(exception); // 获取请求参数 Object[] args = joinPoint.getArgs(); if (args.length > 0) { vo.setReqParam(JSONUtil.toJsonStr(args)); } else { vo.setReqParam(""); } // 获取返回参数 vo.setReturnParam(result != null ? JSONUtil.toJsonStr(result) : ""); // 获取接口路径 String url = getRequestUrl(); vo.setUrl(url); // 获取用户ID(假设用户信息从上下文或session中获取) String userId = getUserId(); vo.setUserId(userId); // 获取操作时间 String optTime = getCurrentTime(); vo.setOptTime(optTime); // 设置创建时间为当前时间 String createTime = getCurrentTime(); vo.setCreateTime(createTime); return vo; } /** * 获取浏览器信息(名称 + 版本) * * @return 例如 "Chrome 129.0.0.0" */ private String getBrowserInfo() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String userAgentStr = request.getHeader("User-Agent"); if (userAgentStr == null || userAgentStr.isEmpty()) { return "Unknown Browser"; } Parser parser = new Parser(); Client client = parser.parse(userAgentStr); String browserName = client.userAgent.family; String browserVersion = client.userAgent.major != null ? client.userAgent.major : "Unknown"; return browserName + " " + browserVersion; } /** * 获取操作IP * * @return IP 地址 */ private String getIpAddress() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty()) { ip = request.getRemoteAddr(); } return ip; } /** * 获取接口路径 * * @return 请求接口路径 */ private String getRequestUrl() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 获取协议(http 或 https) String scheme = request.getScheme(); // 获取服务器的 IP 地址或主机名 String serverName = request.getServerName(); // 获取服务器端口 int serverPort = request.getServerPort(); // 获取请求路径(不包括协议、主机和端口) String requestURI = request.getRequestURI(); // 拼接完整的 URL return scheme + "://" + serverName + ":" + serverPort + requestURI; } /** * 获取当前时间 * * @return 当前时间的字符串表示 */ private String getCurrentTime() { return TimeUtil.getCurrentTime(); } /** * 获取当前用户ID * * @return 当前用户的ID */ private String getUserId() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 从请求头获取 token String token = request.getHeader("token"); if (StringUtils.isBlank(token)) { return ""; } return loginUser.getLoginUserId("token"); } /** * 发送日志到日志收集服务器 * * @param vo 日志对象 */ private void sendLog(LogOperateReqVO vo) { if (!logOperateEnabled) { return; } String requestBody = JSONUtil.toJsonStr(vo); String requestMapping = collectLogsHost + collectReqPath; log.info("======>[LogOperateAspect::sendLog] 日志采集请求路径:{}", requestMapping); log.info("======>[LogOperateAspect::sendLog] 日志请求体:{}", requestBody); try { HttpResponse response = HttpUtil.createPost(requestMapping) .body(requestBody) .execute(); CommonResult<String> commonResult = JSONUtil.toBean(response.body(), CommonResult.class); if (commonResult.isError()) { log.error("======>[LogOperateAspect::sendLog] 请求失败,错误信息:{}", commonResult.getMsg()); } } catch (Exception e) { log.error("======>[LogOperateAspect::sendLog] 请求异常,异常信息:", e); } } } import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.io.Serializable; /** * 日志采集请求参数VO,详情见内部日志说明V1.0.1.docx * */ @Data public class LogOperateReqVO implements Serializable { private static final long serialVersionUID = 1L; private String appId; private String browserInfo; private String exception; private String ip; private String optDetail; private String optResult; private String optType; private String optTypeAddition; private String reqParam; private String returnParam; private String userId; private String url; private String optTime; private String createTime; } 1.3 使用在Controller层上直接加上 @LogOperate(optTypeEnum = OptTypeEnum. x, optDetail = "xxxxxx")
1.4 YML中对该注解的操作开关 log-operate: enabled: true # 日志采集开启与关闭上一篇
799.最长连续不重复子序列
下一篇
C程序多线程拆分文件