第241集SpringBoot AOP请求链路日志架构实战:统一日志打印、链路追踪、性能监控的企业级解决方案
|字数总计:8.9k|阅读时长:44分钟|阅读量:
第241集SpringBoot AOP请求链路日志架构实战:统一日志打印、链路追踪、性能监控的企业级解决方案
前言
在复杂的微服务架构中,请求链路日志的统一管理和追踪是系统可观测性的核心要素。传统的日志记录方式分散在各个业务模块中,缺乏统一的规范和链路追踪能力,导致问题排查困难、性能分析复杂。基于SpringBoot的AOP请求链路日志架构,不仅能够实现统一的日志打印,还能提供完整的链路追踪、性能监控和异常分析能力。随着微服务架构和分布式系统的普及,构建可观测、可追踪的日志体系,已成为企业级架构师必须掌握的核心技能。
本文将深入探讨SpringBoot中AOP请求链路日志的架构设计与实战应用,从统一日志打印到链路追踪,从性能监控到异常分析,为企业构建稳定、高效的请求链路日志解决方案提供全面的技术指导。
一、SpringBoot AOP请求链路日志架构概述与核心原理
1.1 AOP请求链路日志架构设计
SpringBoot AOP请求链路日志系统采用分层架构设计,通过统一日志打印、链路追踪、性能监控等技术,实现高效的请求链路管理能力。
1.2 核心组件架构
二、企业级AOP请求链路日志管理器设计
2.1 AOP切面核心管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
|
@Aspect @Component @Slf4j public class RequestLogAspect {
private final LogManager logManager; private final TraceManager traceManager; private final PerformanceMonitor performanceMonitor; private final ExceptionAnalyzer exceptionAnalyzer;
private final ThreadLocal<RequestLogContext> requestLogContext = new ThreadLocal<>();
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) || " + "@annotation(org.springframework.web.bind.annotation.GetMapping) || " + "@annotation(org.springframework.web.bind.annotation.PostMapping) || " + "@annotation(org.springframework.web.bind.annotation.PutMapping) || " + "@annotation(org.springframework.web.bind.annotation.DeleteMapping)") public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable { return processRequest(joinPoint, "CONTROLLER"); }
@Around("@annotation(org.springframework.stereotype.Service)") public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable { return processRequest(joinPoint, "SERVICE"); }
@Around("@annotation(org.springframework.stereotype.Repository)") public Object aroundRepository(ProceedingJoinPoint joinPoint) throws Throwable { return processRequest(joinPoint, "REPOSITORY"); }
@Around("@annotation(com.company.log.annotation.RequestLog)") public Object aroundRequestLog(ProceedingJoinPoint joinPoint) throws Throwable { RequestLog requestLogAnnotation = getRequestLogAnnotation(joinPoint); return processRequest(joinPoint, requestLogAnnotation.value()); }
private Object processRequest(ProceedingJoinPoint joinPoint, String layer) throws Throwable { long startTime = System.currentTimeMillis(); String traceId = null; String spanId = null;
try { RequestLogContext context = createRequestLogContext(joinPoint, layer); requestLogContext.set(context);
traceId = traceManager.generateTraceId(); spanId = traceManager.generateSpanId();
traceManager.setTraceContext(traceId, spanId, context);
logManager.logRequestStart(context, traceId, spanId);
performanceMonitor.startMonitoring(context);
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime;
context.setExecutionTime(executionTime); context.setResult(result); context.setSuccess(true);
logManager.logRequestSuccess(context, traceId, spanId);
performanceMonitor.endMonitoring(context, true);
return result;
} catch (Exception e) { long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime;
RequestLogContext context = requestLogContext.get(); if (context != null) { context.setExecutionTime(executionTime); context.setException(e); context.setSuccess(false);
logManager.logRequestError(context, traceId, spanId);
exceptionAnalyzer.analyzeException(context, e);
performanceMonitor.endMonitoring(context, false); }
throw e;
} finally { cleanup(); } }
private RequestLogContext createRequestLogContext(ProceedingJoinPoint joinPoint, String layer) { try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Object target = joinPoint.getTarget(); Object[] args = joinPoint.getArgs();
HttpServletRequest request = getHttpServletRequest();
RequestLogContext context = RequestLogContext.builder() .layer(layer) .className(target.getClass().getSimpleName()) .methodName(method.getName()) .methodSignature(signature.toString()) .arguments(args) .startTime(System.currentTimeMillis()) .threadId(Thread.currentThread().getId()) .threadName(Thread.currentThread().getName()) .build();
if (request != null) { context.setRequestUrl(request.getRequestURL().toString()); context.setRequestMethod(request.getMethod()); context.setClientIp(getClientIp(request)); context.setUserAgent(request.getHeader("User-Agent")); context.setRequestHeaders(getRequestHeaders(request)); context.setRequestParams(getRequestParams(request)); }
return context;
} catch (Exception e) { log.error("创建请求日志上下文失败", e); throw new RequestLogException("创建请求日志上下文失败: " + e.getMessage()); } }
private HttpServletRequest getHttpServletRequest() { try { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes instanceof ServletRequestAttributes) { return ((ServletRequestAttributes) requestAttributes).getRequest(); } } catch (Exception e) { log.debug("获取HTTP请求失败: {}", e.getMessage()); } return null; }
private String getClientIp(HttpServletRequest request) { try { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } catch (Exception e) { log.debug("获取客户端IP失败: {}", e.getMessage()); return "unknown"; } }
private Map<String, String> getRequestHeaders(HttpServletRequest request) { try { Map<String, String> headers = new HashMap<>(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); headers.put(headerName, headerValue); } return headers; } catch (Exception e) { log.debug("获取请求头失败: {}", e.getMessage()); return new HashMap<>(); } }
private Map<String, Object> getRequestParams(HttpServletRequest request) { try { Map<String, Object> params = new HashMap<>(); Map<String, String[]> parameterMap = request.getParameterMap(); for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { String key = entry.getKey(); String[] values = entry.getValue(); if (values.length == 1) { params.put(key, values[0]); } else { params.put(key, Arrays.asList(values)); } } return params; } catch (Exception e) { log.debug("获取请求参数失败: {}", e.getMessage()); return new HashMap<>(); } }
private RequestLog getRequestLogAnnotation(ProceedingJoinPoint joinPoint) { try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); return method.getAnnotation(RequestLog.class); } catch (Exception e) { log.debug("获取RequestLog注解失败: {}", e.getMessage()); return null; } }
private void cleanup() { try { requestLogContext.remove(); traceManager.clearTraceContext(); } catch (Exception e) { log.error("清理资源失败", e); } } }
|
2.2 日志管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
@Component @Slf4j public class LogManager {
private final LogProcessor requestLogProcessor; private final LogProcessor responseLogProcessor; private final LogProcessor businessLogProcessor; private final LogProcessor exceptionLogProcessor;
private final LogFormatter logFormatter; private final LogOutputter logOutputter;
public void logRequestStart(RequestLogContext context, String traceId, String spanId) { try { RequestLogInfo logInfo = RequestLogInfo.builder() .traceId(traceId) .spanId(spanId) .layer(context.getLayer()) .className(context.getClassName()) .methodName(context.getMethodName()) .requestUrl(context.getRequestUrl()) .requestMethod(context.getRequestMethod()) .clientIp(context.getClientIp()) .userAgent(context.getUserAgent()) .requestHeaders(context.getRequestHeaders()) .requestParams(context.getRequestParams()) .arguments(context.getArguments()) .startTime(context.getStartTime()) .threadId(context.getThreadId()) .threadName(context.getThreadName()) .logType(LogType.REQUEST_START) .build();
requestLogProcessor.process(logInfo);
String formattedLog = logFormatter.format(logInfo);
logOutputter.output(formattedLog, LogLevel.INFO);
log.debug("请求开始日志记录成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("记录请求开始日志失败", e); } }
public void logRequestSuccess(RequestLogContext context, String traceId, String spanId) { try { ResponseLogInfo logInfo = ResponseLogInfo.builder() .traceId(traceId) .spanId(spanId) .layer(context.getLayer()) .className(context.getClassName()) .methodName(context.getMethodName()) .requestUrl(context.getRequestUrl()) .requestMethod(context.getRequestMethod()) .clientIp(context.getClientIp()) .result(context.getResult()) .executionTime(context.getExecutionTime()) .startTime(context.getStartTime()) .endTime(System.currentTimeMillis()) .threadId(context.getThreadId()) .threadName(context.getThreadName()) .logType(LogType.REQUEST_SUCCESS) .build();
responseLogProcessor.process(logInfo);
String formattedLog = logFormatter.format(logInfo);
logOutputter.output(formattedLog, LogLevel.INFO);
log.debug("请求成功日志记录成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("记录请求成功日志失败", e); } }
public void logRequestError(RequestLogContext context, String traceId, String spanId) { try { ExceptionLogInfo logInfo = ExceptionLogInfo.builder() .traceId(traceId) .spanId(spanId) .layer(context.getLayer()) .className(context.getClassName()) .methodName(context.getMethodName()) .requestUrl(context.getRequestUrl()) .requestMethod(context.getRequestMethod()) .clientIp(context.getClientIp()) .exception(context.getException()) .executionTime(context.getExecutionTime()) .startTime(context.getStartTime()) .endTime(System.currentTimeMillis()) .threadId(context.getThreadId()) .threadName(context.getThreadName()) .logType(LogType.REQUEST_ERROR) .build();
exceptionLogProcessor.process(logInfo);
String formattedLog = logFormatter.format(logInfo);
logOutputter.output(formattedLog, LogLevel.ERROR);
log.debug("请求异常日志记录成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("记录请求异常日志失败", e); } }
public void logBusiness(String traceId, String spanId, String businessType, Object businessData) { try { BusinessLogInfo logInfo = BusinessLogInfo.builder() .traceId(traceId) .spanId(spanId) .businessType(businessType) .businessData(businessData) .timestamp(System.currentTimeMillis()) .threadId(Thread.currentThread().getId()) .threadName(Thread.currentThread().getName()) .logType(LogType.BUSINESS) .build();
businessLogProcessor.process(logInfo);
String formattedLog = logFormatter.format(logInfo);
logOutputter.output(formattedLog, LogLevel.INFO);
log.debug("业务日志记录成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("记录业务日志失败", e); } }
public void logPerformance(String traceId, String spanId, PerformanceMetrics metrics) { try { PerformanceLogInfo logInfo = PerformanceLogInfo.builder() .traceId(traceId) .spanId(spanId) .metrics(metrics) .timestamp(System.currentTimeMillis()) .threadId(Thread.currentThread().getId()) .threadName(Thread.currentThread().getName()) .logType(LogType.PERFORMANCE) .build();
String formattedLog = logFormatter.format(logInfo);
logOutputter.output(formattedLog, LogLevel.INFO);
log.debug("性能日志记录成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("记录性能日志失败", e); } } }
|
2.3 链路追踪管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
@Component @Slf4j public class TraceManager {
private final TraceIdGenerator traceIdGenerator; private final SpanManager spanManager; private final ThreadLocal<TraceContext> traceContext = new ThreadLocal<>();
public String generateTraceId() { try { String traceId = traceIdGenerator.generate(); log.debug("生成TraceId: {}", traceId); return traceId; } catch (Exception e) { log.error("生成TraceId失败", e); return UUID.randomUUID().toString().replace("-", ""); } }
public String generateSpanId() { try { String spanId = spanManager.generateSpanId(); log.debug("生成SpanId: {}", spanId); return spanId; } catch (Exception e) { log.error("生成SpanId失败", e); return UUID.randomUUID().toString().replace("-", ""); } }
public void setTraceContext(String traceId, String spanId, RequestLogContext requestContext) { try { TraceContext context = TraceContext.builder() .traceId(traceId) .spanId(spanId) .parentSpanId(getParentSpanId()) .requestContext(requestContext) .startTime(System.currentTimeMillis()) .build();
traceContext.set(context);
MDC.put("traceId", traceId); MDC.put("spanId", spanId);
log.debug("设置链路上下文成功: {} - {}", traceId, spanId);
} catch (Exception e) { log.error("设置链路上下文失败", e); } }
public TraceContext getCurrentTraceContext() { return traceContext.get(); }
public String getCurrentTraceId() { TraceContext context = traceContext.get(); return context != null ? context.getTraceId() : null; }
public String getCurrentSpanId() { TraceContext context = traceContext.get(); return context != null ? context.getSpanId() : null; }
private String getParentSpanId() { try { HttpServletRequest request = getHttpServletRequest(); if (request != null) { String parentSpanId = request.getHeader("X-Parent-Span-Id"); if (parentSpanId != null && !parentSpanId.isEmpty()) { return parentSpanId; } }
TraceContext currentContext = traceContext.get(); if (currentContext != null) { return currentContext.getSpanId(); }
return null;
} catch (Exception e) { log.debug("获取父SpanId失败: {}", e.getMessage()); return null; } }
private HttpServletRequest getHttpServletRequest() { try { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes instanceof ServletRequestAttributes) { return ((ServletRequestAttributes) requestAttributes).getRequest(); } } catch (Exception e) { log.debug("获取HTTP请求失败: {}", e.getMessage()); } return null; }
public void clearTraceContext() { try { traceContext.remove(); MDC.remove("traceId"); MDC.remove("spanId");
log.debug("清理链路上下文成功");
} catch (Exception e) { log.error("清理链路上下文失败", e); } }
public String createChildSpan(String operationName) { try { TraceContext parentContext = traceContext.get(); if (parentContext == null) { log.warn("无法创建子Span,父链路上下文不存在"); return null; }
String childSpanId = generateSpanId();
TraceContext childContext = TraceContext.builder() .traceId(parentContext.getTraceId()) .spanId(childSpanId) .parentSpanId(parentContext.getSpanId()) .operationName(operationName) .startTime(System.currentTimeMillis()) .build();
traceContext.set(childContext);
MDC.put("spanId", childSpanId);
log.debug("创建子Span成功: {} - {}", parentContext.getTraceId(), childSpanId); return childSpanId;
} catch (Exception e) { log.error("创建子Span失败", e); return null; } }
public void finishSpan() { try { TraceContext context = traceContext.get(); if (context == null) { log.warn("无法完成Span,链路上下文不存在"); return; }
context.setEndTime(System.currentTimeMillis()); context.setDuration(context.getEndTime() - context.getStartTime());
spanManager.recordSpan(context);
log.debug("完成Span成功: {} - {}", context.getTraceId(), context.getSpanId());
} catch (Exception e) { log.error("完成Span失败", e); } } }
|
三、性能监控与异常分析
3.1 性能监控器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
@Component @Slf4j public class PerformanceMonitor {
private final Map<String, PerformanceMetrics> metricsMap = new ConcurrentHashMap<>(); private final AtomicLong totalRequests = new AtomicLong(0); private final AtomicLong totalExecutionTime = new AtomicLong(0);
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startMonitoring(RequestLogContext context) { try { String key = generateKey(context);
PerformanceMetrics metrics = metricsMap.computeIfAbsent(key, k -> PerformanceMetrics.builder() .key(k) .className(context.getClassName()) .methodName(context.getMethodName()) .layer(context.getLayer()) .requestCount(0) .totalExecutionTime(0) .averageExecutionTime(0) .maxExecutionTime(0) .minExecutionTime(Long.MAX_VALUE) .successCount(0) .errorCount(0) .lastExecutionTime(0) .build() );
metrics.setLastExecutionTime(System.currentTimeMillis());
log.debug("开始性能监控: {}", key);
} catch (Exception e) { log.error("开始性能监控失败", e); } }
public void endMonitoring(RequestLogContext context, boolean success) { try { String key = generateKey(context); PerformanceMetrics metrics = metricsMap.get(key);
if (metrics != null) { long executionTime = context.getExecutionTime();
metrics.setRequestCount(metrics.getRequestCount() + 1); metrics.setTotalExecutionTime(metrics.getTotalExecutionTime() + executionTime); metrics.setAverageExecutionTime(metrics.getTotalExecutionTime() / metrics.getRequestCount()); metrics.setMaxExecutionTime(Math.max(metrics.getMaxExecutionTime(), executionTime)); metrics.setMinExecutionTime(Math.min(metrics.getMinExecutionTime(), executionTime));
if (success) { metrics.setSuccessCount(metrics.getSuccessCount() + 1); } else { metrics.setErrorCount(metrics.getErrorCount() + 1); }
totalRequests.incrementAndGet(); totalExecutionTime.addAndGet(executionTime);
log.debug("结束性能监控: {} - 执行时间: {}ms", key, executionTime); }
} catch (Exception e) { log.error("结束性能监控失败", e); } }
private String generateKey(RequestLogContext context) { return context.getLayer() + ":" + context.getClassName() + "." + context.getMethodName(); }
public Map<String, PerformanceMetrics> getMetrics() { return new HashMap<>(metricsMap); }
public OverallPerformanceMetrics getOverallMetrics() { try { long total = totalRequests.get(); long totalTime = totalExecutionTime.get(); double averageTime = total > 0 ? (double) totalTime / total : 0;
return OverallPerformanceMetrics.builder() .totalRequests(total) .totalExecutionTime(totalTime) .averageExecutionTime(averageTime) .monitoredMethodsCount(metricsMap.size()) .build();
} catch (Exception e) { log.error("获取总体性能指标失败", e); throw new PerformanceException("获取总体性能指标失败: " + e.getMessage()); } }
@PostConstruct public void startMonitoring() { scheduler.scheduleAtFixedRate(() -> { try { logPerformanceMetrics(); } catch (Exception e) { log.error("性能监控失败", e); } }, 0, 60, TimeUnit.SECONDS); }
private void logPerformanceMetrics() { try { OverallPerformanceMetrics overallMetrics = getOverallMetrics();
log.info("性能监控指标 - 总请求数: {}, 总执行时间: {}ms, 平均执行时间: {}ms, 监控方法数: {}", overallMetrics.getTotalRequests(), overallMetrics.getTotalExecutionTime(), overallMetrics.getAverageExecutionTime(), overallMetrics.getMonitoredMethodsCount());
for (PerformanceMetrics metrics : metricsMap.values()) { log.debug("方法性能指标 - {}: 请求数={}, 平均执行时间={}ms, 最大执行时间={}ms, 最小执行时间={}ms, 成功率={}%", metrics.getKey(), metrics.getRequestCount(), metrics.getAverageExecutionTime(), metrics.getMaxExecutionTime(), metrics.getMinExecutionTime(), metrics.getRequestCount() > 0 ? (double) metrics.getSuccessCount() / metrics.getRequestCount() * 100 : 0); }
} catch (Exception e) { log.error("记录性能指标失败", e); } }
@PreDestroy public void cleanup() { try { scheduler.shutdown(); metricsMap.clear();
log.info("性能监控器清理完成");
} catch (Exception e) { log.error("性能监控器清理失败", e); } } }
|
3.2 异常分析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
@Component @Slf4j public class ExceptionAnalyzer {
private final Map<String, ExceptionInfo> exceptionMap = new ConcurrentHashMap<>(); private final AtomicLong totalExceptions = new AtomicLong(0); private final AtomicLong totalErrors = new AtomicLong(0);
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void analyzeException(RequestLogContext context, Exception exception) { try { totalExceptions.incrementAndGet();
String exceptionKey = generateExceptionKey(exception); ExceptionInfo exceptionInfo = exceptionMap.computeIfAbsent(exceptionKey, k -> ExceptionInfo.builder() .exceptionKey(k) .exceptionType(exception.getClass().getSimpleName()) .exceptionMessage(exception.getMessage()) .occurrenceCount(0) .firstOccurrenceTime(System.currentTimeMillis()) .lastOccurrenceTime(System.currentTimeMillis()) .affectedMethods(new HashSet<>()) .affectedLayers(new HashSet<>()) .build() );
exceptionInfo.setOccurrenceCount(exceptionInfo.getOccurrenceCount() + 1); exceptionInfo.setLastOccurrenceTime(System.currentTimeMillis()); exceptionInfo.getAffectedMethods().add(context.getClassName() + "." + context.getMethodName()); exceptionInfo.getAffectedLayers().add(context.getLayer());
ExceptionDetail detail = ExceptionDetail.builder() .traceId(context.getTraceId()) .spanId(context.getSpanId()) .layer(context.getLayer()) .className(context.getClassName()) .methodName(context.getMethodName()) .requestUrl(context.getRequestUrl()) .requestMethod(context.getRequestMethod()) .clientIp(context.getClientIp()) .exception(exception) .stackTrace(getStackTrace(exception)) .executionTime(context.getExecutionTime()) .timestamp(System.currentTimeMillis()) .build();
storeExceptionDetail(detail);
checkExceptionAlert(exceptionInfo);
log.debug("异常分析完成: {} - {}", exceptionKey, exception.getMessage());
} catch (Exception e) { log.error("异常分析失败", e); } }
private String generateExceptionKey(Exception exception) { return exception.getClass().getSimpleName() + ":" + exception.getMessage(); }
private String getStackTrace(Exception exception) { try { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); exception.printStackTrace(pw); return sw.toString(); } catch (Exception e) { log.debug("获取堆栈跟踪失败: {}", e.getMessage()); return "无法获取堆栈跟踪"; } }
private void storeExceptionDetail(ExceptionDetail detail) { try { log.error("异常详情 - TraceId: {}, SpanId: {}, 方法: {}.{}, 异常: {}", detail.getTraceId(), detail.getSpanId(), detail.getClassName(), detail.getMethodName(), detail.getException().getMessage());
} catch (Exception e) { log.error("存储异常详情失败", e); } }
private void checkExceptionAlert(ExceptionInfo exceptionInfo) { try { if (exceptionInfo.getOccurrenceCount() > 10) { log.warn("异常频率过高: {} - 发生次数: {}", exceptionInfo.getExceptionKey(), exceptionInfo.getOccurrenceCount()); }
if (exceptionInfo.getExceptionType().contains("OutOfMemoryError")) { log.error("内存溢出异常: {}", exceptionInfo.getExceptionKey()); }
if (exceptionInfo.getAffectedLayers().size() > 3) { log.warn("异常影响范围过大: {} - 影响层级: {}", exceptionInfo.getExceptionKey(), exceptionInfo.getAffectedLayers()); }
} catch (Exception e) { log.error("检查异常告警失败", e); } }
public ExceptionStatistics getExceptionStatistics() { try { return ExceptionStatistics.builder() .totalExceptions(totalExceptions.get()) .totalErrors(totalErrors.get()) .exceptionTypesCount(exceptionMap.size()) .exceptionInfos(new ArrayList<>(exceptionMap.values())) .build();
} catch (Exception e) { log.error("获取异常统计失败", e); throw new ExceptionAnalysisException("获取异常统计失败: " + e.getMessage()); } }
@PostConstruct public void startAnalysis() { scheduler.scheduleAtFixedRate(() -> { try { logExceptionStatistics(); } catch (Exception e) { log.error("异常分析失败", e); } }, 0, 300, TimeUnit.SECONDS); }
private void logExceptionStatistics() { try { ExceptionStatistics statistics = getExceptionStatistics();
log.info("异常统计 - 总异常数: {}, 异常类型数: {}", statistics.getTotalExceptions(), statistics.getExceptionTypesCount());
for (ExceptionInfo info : statistics.getExceptionInfos()) { log.debug("异常信息 - {}: 发生次数={}, 影响方法数={}, 影响层级数={}", info.getExceptionKey(), info.getOccurrenceCount(), info.getAffectedMethods().size(), info.getAffectedLayers().size()); }
} catch (Exception e) { log.error("记录异常统计失败", e); } }
@PreDestroy public void cleanup() { try { scheduler.shutdown(); exceptionMap.clear();
log.info("异常分析器清理完成");
} catch (Exception e) { log.error("异常分析器清理失败", e); } } }
|
四、日志格式化与输出
4.1 日志格式化器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
|
@Component @Slf4j public class LogFormatter {
private final ObjectMapper objectMapper; private final LogFormatConfig formatConfig;
public String format(RequestLogInfo logInfo) { try { switch (formatConfig.getFormatType()) { case JSON: return formatAsJson(logInfo); case TEXT: return formatAsText(logInfo); case STRUCTURED: return formatAsStructured(logInfo); default: return formatAsJson(logInfo); } } catch (Exception e) { log.error("格式化请求日志失败", e); return "格式化失败: " + e.getMessage(); } }
public String format(ResponseLogInfo logInfo) { try { switch (formatConfig.getFormatType()) { case JSON: return formatAsJson(logInfo); case TEXT: return formatAsText(logInfo); case STRUCTURED: return formatAsStructured(logInfo); default: return formatAsJson(logInfo); } } catch (Exception e) { log.error("格式化响应日志失败", e); return "格式化失败: " + e.getMessage(); } }
public String format(ExceptionLogInfo logInfo) { try { switch (formatConfig.getFormatType()) { case JSON: return formatAsJson(logInfo); case TEXT: return formatAsText(logInfo); case STRUCTURED: return formatAsStructured(logInfo); default: return formatAsJson(logInfo); } } catch (Exception e) { log.error("格式化异常日志失败", e); return "格式化失败: " + e.getMessage(); } }
public String format(BusinessLogInfo logInfo) { try { switch (formatConfig.getFormatType()) { case JSON: return formatAsJson(logInfo); case TEXT: return formatAsText(logInfo); case STRUCTURED: return formatAsStructured(logInfo); default: return formatAsJson(logInfo); } } catch (Exception e) { log.error("格式化业务日志失败", e); return "格式化失败: " + e.getMessage(); } }
public String format(PerformanceLogInfo logInfo) { try { switch (formatConfig.getFormatType()) { case JSON: return formatAsJson(logInfo); case TEXT: return formatAsText(logInfo); case STRUCTURED: return formatAsStructured(logInfo); default: return formatAsJson(logInfo); } } catch (Exception e) { log.error("格式化性能日志失败", e); return "格式化失败: " + e.getMessage(); } }
private String formatAsJson(Object logInfo) { try { return objectMapper.writeValueAsString(logInfo); } catch (Exception e) { log.error("JSON格式化失败", e); return "JSON格式化失败: " + e.getMessage(); } }
private String formatAsText(Object logInfo) { try { if (logInfo instanceof RequestLogInfo) { return formatRequestLogAsText((RequestLogInfo) logInfo); } else if (logInfo instanceof ResponseLogInfo) { return formatResponseLogAsText((ResponseLogInfo) logInfo); } else if (logInfo instanceof ExceptionLogInfo) { return formatExceptionLogAsText((ExceptionLogInfo) logInfo); } else if (logInfo instanceof BusinessLogInfo) { return formatBusinessLogAsText((BusinessLogInfo) logInfo); } else if (logInfo instanceof PerformanceLogInfo) { return formatPerformanceLogAsText((PerformanceLogInfo) logInfo); }
return logInfo.toString();
} catch (Exception e) { log.error("文本格式化失败", e); return "文本格式化失败: " + e.getMessage(); } }
private String formatAsStructured(Object logInfo) { try { StringBuilder sb = new StringBuilder();
if (logInfo instanceof RequestLogInfo) { RequestLogInfo info = (RequestLogInfo) logInfo; sb.append("[").append(info.getLogType()).append("] "); sb.append("TraceId=").append(info.getTraceId()).append(" "); sb.append("SpanId=").append(info.getSpanId()).append(" "); sb.append("Layer=").append(info.getLayer()).append(" "); sb.append("Method=").append(info.getClassName()).append(".").append(info.getMethodName()).append(" "); sb.append("URL=").append(info.getRequestUrl()).append(" "); sb.append("Method=").append(info.getRequestMethod()).append(" "); sb.append("ClientIP=").append(info.getClientIp()).append(" "); sb.append("StartTime=").append(new Date(info.getStartTime())).append(" "); } else if (logInfo instanceof ResponseLogInfo) { ResponseLogInfo info = (ResponseLogInfo) logInfo; sb.append("[").append(info.getLogType()).append("] "); sb.append("TraceId=").append(info.getTraceId()).append(" "); sb.append("SpanId=").append(info.getSpanId()).append(" "); sb.append("Layer=").append(info.getLayer()).append(" "); sb.append("Method=").append(info.getClassName()).append(".").append(info.getMethodName()).append(" "); sb.append("ExecutionTime=").append(info.getExecutionTime()).append("ms "); sb.append("EndTime=").append(new Date(info.getEndTime())).append(" "); } else if (logInfo instanceof ExceptionLogInfo) { ExceptionLogInfo info = (ExceptionLogInfo) logInfo; sb.append("[").append(info.getLogType()).append("] "); sb.append("TraceId=").append(info.getTraceId()).append(" "); sb.append("SpanId=").append(info.getSpanId()).append(" "); sb.append("Layer=").append(info.getLayer()).append(" "); sb.append("Method=").append(info.getClassName()).append(".").append(info.getMethodName()).append(" "); sb.append("Exception=").append(info.getException().getClass().getSimpleName()).append(" "); sb.append("Message=").append(info.getException().getMessage()).append(" "); sb.append("ExecutionTime=").append(info.getExecutionTime()).append("ms "); }
return sb.toString();
} catch (Exception e) { log.error("结构化格式化失败", e); return "结构化格式化失败: " + e.getMessage(); } }
private String formatRequestLogAsText(RequestLogInfo logInfo) { StringBuilder sb = new StringBuilder(); sb.append("=== 请求开始 ===\n"); sb.append("TraceId: ").append(logInfo.getTraceId()).append("\n"); sb.append("SpanId: ").append(logInfo.getSpanId()).append("\n"); sb.append("Layer: ").append(logInfo.getLayer()).append("\n"); sb.append("Method: ").append(logInfo.getClassName()).append(".").append(logInfo.getMethodName()).append("\n"); sb.append("URL: ").append(logInfo.getRequestUrl()).append("\n"); sb.append("HTTP Method: ").append(logInfo.getRequestMethod()).append("\n"); sb.append("Client IP: ").append(logInfo.getClientIp()).append("\n"); sb.append("User Agent: ").append(logInfo.getUserAgent()).append("\n"); sb.append("Start Time: ").append(new Date(logInfo.getStartTime())).append("\n"); sb.append("Thread: ").append(logInfo.getThreadName()).append("\n"); sb.append("=================="); return sb.toString(); }
private String formatResponseLogAsText(ResponseLogInfo logInfo) { StringBuilder sb = new StringBuilder(); sb.append("=== 请求成功 ===\n"); sb.append("TraceId: ").append(logInfo.getTraceId()).append("\n"); sb.append("SpanId: ").append(logInfo.getSpanId()).append("\n"); sb.append("Layer: ").append(logInfo.getLayer()).append("\n"); sb.append("Method: ").append(logInfo.getClassName()).append(".").append(logInfo.getMethodName()).append("\n"); sb.append("URL: ").append(logInfo.getRequestUrl()).append("\n"); sb.append("HTTP Method: ").append(logInfo.getRequestMethod()).append("\n"); sb.append("Client IP: ").append(logInfo.getClientIp()).append("\n"); sb.append("Execution Time: ").append(logInfo.getExecutionTime()).append("ms\n"); sb.append("Start Time: ").append(new Date(logInfo.getStartTime())).append("\n"); sb.append("End Time: ").append(new Date(logInfo.getEndTime())).append("\n"); sb.append("Thread: ").append(logInfo.getThreadName()).append("\n"); sb.append("=================="); return sb.toString(); }
private String formatExceptionLogAsText(ExceptionLogInfo logInfo) { StringBuilder sb = new StringBuilder(); sb.append("=== 请求异常 ===\n"); sb.append("TraceId: ").append(logInfo.getTraceId()).append("\n"); sb.append("SpanId: ").append(logInfo.getSpanId()).append("\n"); sb.append("Layer: ").append(logInfo.getLayer()).append("\n"); sb.append("Method: ").append(logInfo.getClassName()).append(".").append(logInfo.getMethodName()).append("\n"); sb.append("URL: ").append(logInfo.getRequestUrl()).append("\n"); sb.append("HTTP Method: ").append(logInfo.getRequestMethod()).append("\n"); sb.append("Client IP: ").append(logInfo.getClientIp()).append("\n"); sb.append("Exception: ").append(logInfo.getException().getClass().getSimpleName()).append("\n"); sb.append("Message: ").append(logInfo.getException().getMessage()).append("\n"); sb.append("Execution Time: ").append(logInfo.getExecutionTime()).append("ms\n"); sb.append("Start Time: ").append(new Date(logInfo.getStartTime())).append("\n"); sb.append("End Time: ").append(new Date(logInfo.getEndTime())).append("\n"); sb.append("Thread: ").append(logInfo.getThreadName()).append("\n"); sb.append("=================="); return sb.toString(); }
private String formatBusinessLogAsText(BusinessLogInfo logInfo) { StringBuilder sb = new StringBuilder(); sb.append("=== 业务日志 ===\n"); sb.append("TraceId: ").append(logInfo.getTraceId()).append("\n"); sb.append("SpanId: ").append(logInfo.getSpanId()).append("\n"); sb.append("Business Type: ").append(logInfo.getBusinessType()).append("\n"); sb.append("Business Data: ").append(logInfo.getBusinessData()).append("\n"); sb.append("Timestamp: ").append(new Date(logInfo.getTimestamp())).append("\n"); sb.append("Thread: ").append(logInfo.getThreadName()).append("\n"); sb.append("=================="); return sb.toString(); }
private String formatPerformanceLogAsText(PerformanceLogInfo logInfo) { StringBuilder sb = new StringBuilder(); sb.append("=== 性能日志 ===\n"); sb.append("TraceId: ").append(logInfo.getTraceId()).append("\n"); sb.append("SpanId: ").append(logInfo.getSpanId()).append("\n"); sb.append("Metrics: ").append(logInfo.getMetrics()).append("\n"); sb.append("Timestamp: ").append(new Date(logInfo.getTimestamp())).append("\n"); sb.append("Thread: ").append(logInfo.getThreadName()).append("\n"); sb.append("=================="); return sb.toString(); } }
|
4.2 日志输出器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
@Component @Slf4j public class LogOutputter {
private final LogOutputConfig outputConfig; private final Map<LogLevel, Logger> loggerMap = new HashMap<>();
public void output(String logContent, LogLevel level) { try { switch (outputConfig.getOutputType()) { case CONSOLE: outputToConsole(logContent, level); break; case FILE: outputToFile(logContent, level); break; case DATABASE: outputToDatabase(logContent, level); break; case MQ: outputToMQ(logContent, level); break; case MULTIPLE: outputToMultiple(logContent, level); break; default: outputToConsole(logContent, level); }
} catch (Exception e) { log.error("输出日志失败", e); } }
private void outputToConsole(String logContent, LogLevel level) { try { Logger logger = getLogger(level);
switch (level) { case DEBUG: logger.debug(logContent); break; case INFO: logger.info(logContent); break; case WARN: logger.warn(logContent); break; case ERROR: logger.error(logContent); break; default: logger.info(logContent); }
} catch (Exception e) { log.error("输出到控制台失败", e); } }
private void outputToFile(String logContent, LogLevel level) { try { log.info("文件输出: {} - {}", level, logContent);
} catch (Exception e) { log.error("输出到文件失败", e); } }
private void outputToDatabase(String logContent, LogLevel level) { try { log.info("数据库输出: {} - {}", level, logContent);
} catch (Exception e) { log.error("输出到数据库失败", e); } }
private void outputToMQ(String logContent, LogLevel level) { try { log.info("消息队列输出: {} - {}", level, logContent);
} catch (Exception e) { log.error("输出到消息队列失败", e); } }
private void outputToMultiple(String logContent, LogLevel level) { try { outputToConsole(logContent, level);
outputToFile(logContent, level);
outputToDatabase(logContent, level);
} catch (Exception e) { log.error("输出到多个目标失败", e); } }
private Logger getLogger(LogLevel level) { return loggerMap.computeIfAbsent(level, k -> { switch (k) { case DEBUG: return LoggerFactory.getLogger("DEBUG"); case INFO: return LoggerFactory.getLogger("INFO"); case WARN: return LoggerFactory.getLogger("WARN"); case ERROR: return LoggerFactory.getLogger("ERROR"); default: return LoggerFactory.getLogger("DEFAULT"); } }); } }
|
五、实战应用与最佳实践
5.1 请求链路日志实战示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
@RestController @RequestMapping("/api/request-log") @Slf4j public class RequestLogExample {
private final LogManager logManager; private final TraceManager traceManager;
@PostMapping("/demonstrate") public ResponseEntity<RequestLogDemoResult> demonstrateRequestLog(@RequestBody RequestLogDemoRequest request) { try { String traceId = traceManager.getCurrentTraceId(); String spanId = traceManager.getCurrentSpanId();
logManager.logBusiness(traceId, spanId, "REQUEST_DEMO", request);
String result = processRequest(request);
logManager.logBusiness(traceId, spanId, "RESPONSE_DEMO", result);
RequestLogDemoResult demoResult = RequestLogDemoResult.builder() .traceId(traceId) .spanId(spanId) .result(result) .success(true) .build();
return ResponseEntity.ok(demoResult);
} catch (Exception e) { log.error("请求链路日志演示失败", e); return ResponseEntity.badRequest().build(); } }
@GetMapping("/demonstrate-trace") public ResponseEntity<TraceDemoResult> demonstrateTrace() { try { String traceId = traceManager.getCurrentTraceId(); String spanId = traceManager.getCurrentSpanId();
String childSpanId = traceManager.createChildSpan("CHILD_OPERATION");
String result = processChildOperation();
traceManager.finishSpan();
TraceDemoResult demoResult = TraceDemoResult.builder() .traceId(traceId) .spanId(spanId) .childSpanId(childSpanId) .result(result) .success(true) .build();
return ResponseEntity.ok(demoResult);
} catch (Exception e) { log.error("链路追踪演示失败", e); return ResponseEntity.badRequest().build(); } }
@GetMapping("/demonstrate-performance") public ResponseEntity<PerformanceDemoResult> demonstratePerformance() { try { String traceId = traceManager.getCurrentTraceId(); String spanId = traceManager.getCurrentSpanId();
long startTime = System.currentTimeMillis(); Thread.sleep(1000); long endTime = System.currentTimeMillis();
PerformanceMetrics metrics = PerformanceMetrics.builder() .key("DEMO_OPERATION") .className("RequestLogExample") .methodName("demonstratePerformance") .layer("CONTROLLER") .requestCount(1) .totalExecutionTime(endTime - startTime) .averageExecutionTime(endTime - startTime) .maxExecutionTime(endTime - startTime) .minExecutionTime(endTime - startTime) .successCount(1) .errorCount(0) .lastExecutionTime(startTime) .build();
logManager.logPerformance(traceId, spanId, metrics);
PerformanceDemoResult demoResult = PerformanceDemoResult.builder() .traceId(traceId) .spanId(spanId) .executionTime(endTime - startTime) .success(true) .build();
return ResponseEntity.ok(demoResult);
} catch (Exception e) { log.error("性能监控演示失败", e); return ResponseEntity.badRequest().build(); } }
private String processRequest(RequestLogDemoRequest request) { try { Thread.sleep(100); return "处理结果: " + request.getData(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("处理请求失败", e); } }
private String processChildOperation() { try { Thread.sleep(200); return "子操作结果"; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("处理子操作失败", e); } } }
|
5.2 AOP请求链路日志最佳实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
@Component @Slf4j public class RequestLogBestPractices {
private final LogManager logManager; private final TraceManager traceManager; private final PerformanceMonitor performanceMonitor; private final ExceptionAnalyzer exceptionAnalyzer;
public void demonstrateBestPractices() { log.info("=== AOP请求链路日志最佳实践指南 ===");
demonstrateAOPUsage();
demonstrateTraceBestPractices();
demonstratePerformanceBestPractices();
demonstrateExceptionBestPractices();
demonstrateLogOutputBestPractices(); }
private void demonstrateAOPUsage() { log.info("--- 正确使用AOP切面 ---");
try { @RequestLog("DEMO_OPERATION") public String demoOperation() { return "操作结果"; }
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public Object aroundRequestMapping(ProceedingJoinPoint joinPoint) throws Throwable { return joinPoint.proceed(); }
@Before("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void beforeRequestMapping(JoinPoint joinPoint) { }
@After("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void afterRequestMapping(JoinPoint joinPoint) { }
log.info("AOP切面使用示例完成");
} catch (Exception e) { log.error("AOP切面使用示例失败", e); } }
private void demonstrateTraceBestPractices() { log.info("--- 链路追踪最佳实践 ---");
try { String traceId = traceManager.generateTraceId(); log.info("生成TraceId: {}", traceId);
String spanId = traceManager.generateSpanId(); log.info("生成SpanId: {}", spanId);
RequestLogContext context = RequestLogContext.builder() .layer("DEMO") .className("RequestLogBestPractices") .methodName("demonstrateTraceBestPractices") .startTime(System.currentTimeMillis()) .build();
traceManager.setTraceContext(traceId, spanId, context);
String childSpanId = traceManager.createChildSpan("CHILD_OPERATION"); log.info("创建子Span: {}", childSpanId);
traceManager.finishSpan();
traceManager.clearTraceContext();
log.info("链路追踪最佳实践示例完成");
} catch (Exception e) { log.error("链路追踪最佳实践示例失败", e); } }
private void demonstratePerformanceBestPractices() { log.info("--- 性能监控最佳实践 ---");
try { RequestLogContext context = RequestLogContext.builder() .layer("DEMO") .className("RequestLogBestPractices") .methodName("demonstratePerformanceBestPractices") .startTime(System.currentTimeMillis()) .build();
performanceMonitor.startMonitoring(context);
Thread.sleep(100);
context.setExecutionTime(100); context.setSuccess(true);
performanceMonitor.endMonitoring(context, true);
Map<String, PerformanceMetrics> metrics = performanceMonitor.getMetrics(); log.info("性能指标: {}", metrics);
log.info("性能监控最佳实践示例完成");
} catch (Exception e) { log.error("性能监控最佳实践示例失败", e); } }
private void demonstrateExceptionBestPractices() { log.info("--- 异常处理最佳实践 ---");
try { RequestLogContext context = RequestLogContext.builder() .layer("DEMO") .className("RequestLogBestPractices") .methodName("demonstrateExceptionBestPractices") .startTime(System.currentTimeMillis()) .build();
try { throw new RuntimeException("模拟业务异常"); } catch (Exception e) { exceptionAnalyzer.analyzeException(context, e);
ExceptionStatistics statistics = exceptionAnalyzer.getExceptionStatistics(); log.info("异常统计: {}", statistics); }
log.info("异常处理最佳实践示例完成");
} catch (Exception e) { log.error("异常处理最佳实践示例失败", e); } }
private void demonstrateLogOutputBestPractices() { log.info("--- 日志输出最佳实践 ---");
try { String traceId = traceManager.getCurrentTraceId(); String spanId = traceManager.getCurrentSpanId();
logManager.logBusiness(traceId, spanId, "DEMO_OPERATION", "演示数据");
PerformanceMetrics metrics = PerformanceMetrics.builder() .key("DEMO_OPERATION") .className("RequestLogBestPractices") .methodName("demonstrateLogOutputBestPractices") .layer("DEMO") .requestCount(1) .totalExecutionTime(100) .averageExecutionTime(100) .maxExecutionTime(100) .minExecutionTime(100) .successCount(1) .errorCount(0) .lastExecutionTime(System.currentTimeMillis()) .build();
logManager.logPerformance(traceId, spanId, metrics);
log.info("日志输出最佳实践示例完成");
} catch (Exception e) { log.error("日志输出最佳实践示例失败", e); } } }
|
六、总结
本文深入探讨了SpringBoot中AOP请求链路日志的架构设计与实战应用。通过构建企业级的AOP请求链路日志框架,我们实现了:
- 统一日志打印:通过AOP切面实现统一的请求日志打印,支持Controller、Service、Repository等各层
- 链路追踪:提供完整的TraceId和SpanId生成和管理,支持父子Span关系
- 性能监控:实时监控请求执行时间、成功率等性能指标
- 异常分析:自动分析和统计异常信息,提供异常告警能力
- 灵活输出:支持多种日志格式和输出方式,满足不同场景需求
通过这套企业级的AOP请求链路日志架构,我们能够全面监控和追踪请求链路,提供完整的系统可观测性。随着微服务架构和分布式系统的不断发展,构建可观测、可追踪的日志体系将成为企业级架构师必须掌握的核心技能。
在实际应用中,建议遵循AOP请求链路日志的最佳实践,合理配置日志级别和输出方式,及时分析性能指标和异常信息,并通过监控和告警工具确保系统的稳定运行。只有这样,才能真正发挥AOP请求链路日志的价值,构建高质量的企业级应用系统。