news 2026/4/16 14:14:41

Spring Cloud Alibaba 微服务整合自定义日志注解完整教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Cloud Alibaba 微服务整合自定义日志注解完整教程

Spring Cloud 2025.0.0 整合自定义日志注解完整教程

本教程基于 Spring Cloud 2025.0.0 新特性,包含虚拟线程、响应式编程、AOT 原生编译支持

一、技术栈和架构

1.1 技术选型

  • Spring Cloud 2025.0.0(代号: 2025.0.0)
  • Spring Boot 4.0
  • Java 21+(支持虚拟线程)
  • 响应式编程(WebFlux + R2DBC)
  • AOT 原生编译(GraalVM 23.0+)
  • 分布式追踪(Micrometer + Brave)
  • 日志收集(Logback + ELK)

1.2 架构图

┌─────────────────────────────────────────────────────────┐ │ 自定义日志注解系统 │ ├─────────────────────────────────────────────────────────┤ │ Log注解 → 切面处理 → 日志上下文 → 异步存储 → 分布式追踪 │ │ ↑ ↑ ↑ ↑ ↑ │ │ 业务方法 虚拟线程 MDC/Reactor Elasticsearch Sleuth│ └─────────────────────────────────────────────────────────┘

二、项目初始化

2.1 父工程 pom.xml

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-parent</artifactId><version>2025.0.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>spring-cloud-logging</artifactId><version>1.0.0</version><packaging>pom</packaging><modules><module>logging-annotation</module><module>logging-starter</module><module>user-service</module><module>order-service</module><module>gateway-service</module></modules><properties><java.version>21</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!-- Spring Cloud 版本 --><spring-cloud.version>2025.0.0</spring-cloud.version><spring-cloud-alibaba.version>2025.0.0.0</spring-cloud-alibaba.version><!-- 工具版本 --><lombok.version>1.18.30</lombok.version><mapstruct.version>1.5.5.Final</mapstruct.version><guava.version>33.0.0-jre</guava.version><!-- 日志版本 --><logstash-logback.version>8.0</logstash-logback.version><micrometer-tracing.version>1.2.0</micrometer-tracing.version><!-- GraalVM --><graalvm.version>23.0.0</graalvm.version></properties><dependencyManagement><dependencies><!-- Spring Cloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version><configuration><source>21</source><target>21</target><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></path><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${mapstruct.version}</version></path></annotationProcessorPaths></configuration></plugin><!-- Spring Boot AOT Plugin --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder-jammy-tiny:latest</builder><env><BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE></env></image></configuration></plugin><!-- GraalVM Native Image Plugin --><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>${graalvm.version}</version></plugin></plugins></pluginManagement></build><profiles><profile><id>native</id><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><executions><execution><id>build-native</id><goals><goal>compile-no-fork</goal></goals><phase>package</phase></execution></executions></plugin></plugins></build></profile></profiles></project>

三、日志注解模块 (logging-annotation)

3.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>com.example</groupId><artifactId>spring-cloud-logging</artifactId><version>1.0.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>logging-annotation</artifactId><dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- 响应式 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- 分布式追踪 --><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-tracing</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies></project>

3.2 基础注解定义

3.2.1 核心日志注解
packagecom.example.logging.annotation;importorg.springframework.core.annotation.AliasFor;importjava.lang.annotation.*;importjava.util.concurrent.TimeUnit;/** * 自定义日志注解 * Spring Cloud 2025.0.0 新特性支持 */@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic@interfaceLoggable{/** * 模块名称 */Stringmodule()default"";/** * 操作类型 */LogTypetype()defaultLogType.OTHER;/** * 操作描述 */@AliasFor("value")Stringdescription()default"";@AliasFor("description")Stringvalue()default"";/** * 是否记录方法参数 */booleanlogParams()defaulttrue;/** * 是否记录返回值 */booleanlogResult()defaulttrue;/** * 是否记录执行时间 */booleanlogTime()defaulttrue;/** * 慢查询阈值(毫秒) */longslowThreshold()default1000;/** * 是否启用异步记录 */booleanasync()defaulttrue;/** * 异步记录策略 */AsyncStrategyasyncStrategy()defaultAsyncStrategy.VIRTUAL_THREAD;/** * 是否启用虚拟线程 */booleanvirtualThread()defaulttrue;/** * 日志级别 */LogLevellevel()defaultLogLevel.INFO;/** * 是否推送到日志中心 */booleanpushToCenter()defaulttrue;/** * 业务标识表达式(SpEL) */StringbizNo()default"";/** * 是否记录堆栈信息 */booleanstackTrace()defaultfalse;/** * 日志分组 */Stringgroup()default"default";/** * 操作类型枚举 */enumLogType{SELECT,// 查询INSERT,// 新增UPDATE,// 更新DELETE,// 删除IMPORT,// 导入EXPORT,// 导出LOGIN,// 登录LOGOUT,// 登出UPLOAD,// 上传DOWNLOAD,// 下载EXECUTE,// 执行CALL,// 调用OTHER// 其他}/** * 异步策略枚举 */enumAsyncStrategy{VIRTUAL_THREAD,// 虚拟线程THREAD_POOL,// 线程池REACTIVE,// 响应式IMMEDIATE// 立即执行}/** * 日志级别枚举 */enumLogLevel{TRACE,DEBUG,INFO,WARN,ERROR}}
3.2.2 响应式日志注解
packagecom.example.logging.annotation;importorg.springframework.core.annotation.AliasFor;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;importjava.lang.annotation.*;importjava.util.concurrent.CompletableFuture;/** * 响应式日志注解 * 支持 Reactive Streams */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Loggablepublic@interfaceReactiveLog{/** * 是否跟踪响应式流 */booleantraceStream()defaultfalse;/** * 最大跟踪元素数量 */intmaxTraceElements()default10;/** * 响应式超时时间(毫秒) */longtimeout()default30000;/** * 是否记录背压 */booleanlogBackpressure()defaultfalse;/** * 支持的类型 */Class<?>[]supportTypes()default{Mono.class,Flux.class,CompletableFuture.class};/** * 响应式操作类型 */ReactiveOperationoperation()defaultReactiveOperation.UNKNOWN;enumReactiveOperation{MONO_CREATE,MONO_TRANSFORM,MONO_DELAY,FLUX_STREAM,FLUX_WINDOW,FLUX_BUFFER,FUTURE_ASYNC,FUTURE_COMPLETABLE,UNKNOWN}}
3.2.3 分布式追踪注解
packagecom.example.logging.annotation;importorg.springframework.core.annotation.AliasFor;importjava.lang.annotation.*;/** * 分布式追踪注解 * 集成 Micrometer Tracing */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Loggablepublic@interfaceTraceLog{/** * Span名称 */StringspanName()default"";/** * 是否创建新Span */booleannewSpan()defaulttrue;/** * Span类型 */SpanKindkind()defaultSpanKind.INTERNAL;/** * 是否记录异常栈 */booleanlogStackTrace()defaulttrue;/** * 标签(key=value格式) */String[]tags()default{};/** * 事件 */String[]events()default{};/** * 是否远程调用 */booleanremote()defaultfalse;/** * 远程服务名称 */StringremoteService()default"";/** * Span类型枚举 */enumSpanKind{CLIENT,// 客户端SERVER,// 服务端PRODUCER,// 生产者CONSUMER,// 消费者INTERNAL// 内部调用}}
3.2.4 性能监控注解
packagecom.example.logging.annotation;importorg.springframework.core.annotation.AliasFor;importjava.lang.annotation.*;importjava.util.concurrent.TimeUnit;/** * 性能监控注解 * 集成 Micrometer Metrics */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Loggablepublic@interfaceMetricLog{/** * 指标名称 */Stringname()default"";/** * 指标描述 */Stringdescription()default"";/** * 指标类型 */MetricTypetype()defaultMetricType.TIMER;/** * 是否记录分位数 */booleanpercentiles()defaulttrue;/** * 分位数值 */double[]percentileValues()default{0.5,0.95,0.99};/** * 是否记录直方图 */booleanhistogram()defaultfalse;/** * SLA配置(毫秒) */long[]slas()default{10,50,100,500,1000};/** * 指标类型枚举 */enumMetricType{COUNTER,// 计数器TIMER,// 计时器GAUGE,// 仪表LONG_TASK_TIMER,// 长任务计时器DISTRIBUTION_SUMMARY// 分布摘要}}

3.3 日志上下文

3.3.1 日志上下文实体
packagecom.example.logging.context;importlombok.Data;importlombok.experimental.Accessors;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.web.server.ServerWebExchange;importreactor.util.context.Context;importreactor.util.context.ContextView;importjava.io.Serializable;importjava.time.Duration;importjava.time.LocalDateTime;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.atomic.AtomicReference;/** * 日志上下文 * 支持虚拟线程和响应式编程 */@Data@Accessors(chain=true)publicclassLogContextimplementsSerializable{privatestaticfinallongserialVersionUID=1L;// 基本信息privateStringlogId;privateStringtraceId;privateStringspanId;privateStringparentSpanId;// 方法信息privateStringclassName;privateStringmethodName;privateStringsignature;// 业务信息privateStringmodule;privateStringoperation;privateStringdescription;privateStringbizNo;// 参数和结果privateObject[]args;privateObjectresult;privateThrowablethrowable;// 时间信息privateLocalDateTimestartTime;privateLocalDateTimeendTime;privateDurationduration;// 线程信息privateThreadInfothreadInfo;// 请求信息privateRequestInforequestInfo;// 扩展属性privateMap<String,Object>attributes=newConcurrentHashMap<>();// 响应式上下文privateReactiveContextreactiveContext;// 追踪信息privateTraceInfotraceInfo;/** * 创建日志上下文 */publicstaticLogContextcreate(){LogContextcontext=newLogContext();context.setLogId(generateLogId());context.setStartTime(LocalDateTime.now());context.setThreadInfo(ThreadInfo.capture());context.setTraceInfo(TraceInfo.capture());returncontext;}/** * 从ServerWebExchange创建 */publicstaticLogContextfromExchange(ServerWebExchangeexchange){LogContextcontext=create();ServerHttpRequestrequest=exchange.getRequest();RequestInforequestInfo=newRequestInfo();requestInfo.setRequestId(request.getId());requestInfo.setMethod(request.getMethod().name());requestInfo.setUri(request.getURI().toString());requestInfo.setHeaders(newConcurrentHashMap<>(request.getHeaders().toSingleValueMap()));requestInfo.setQueryParams(newConcurrentHashMap<>(request.getQueryParams().toSingleValueMap()));requestInfo.setRemoteAddress(request.getRemoteAddress()!=null?request.getRemoteAddress().getAddress().getHostAddress():"");requestInfo.setUserAgent(request.getHeaders().getFirst("User-Agent"));context.setRequestInfo(requestInfo);// 设置追踪信息StringtraceId=request.getHeaders().getFirst("X-B3-TraceId");StringspanId=request.getHeaders().getFirst("X-B3-SpanId");if(traceId!=null){context.setTraceId(traceId);}if(spanId!=null){context.setSpanId(spanId);}returncontext;}/** * 计算持续时间 */publicLogContextcalculateDuration(){if(startTime!=null){this.endTime=LocalDateTime.now();this.duration=Duration.between(startTime,endTime);}returnthis;}/** * 设置异常 */publicLogContextwithException(Throwablethrowable){this.throwable=throwable;returnthis;}/** * 设置结果 */publicLogContextwithResult(Objectresult){this.result=result;returnthis;}/** * 设置响应式上下文 */publicLogContextwithReactiveContext(ContextViewcontextView){this.reactiveContext=ReactiveContext.from(contextView);returnthis;}/** * 添加属性 */publicLogContextaddAttribute(Stringkey,Objectvalue){this.attributes.put(key,value);returnthis;}/** * 生成日志ID */privatestaticStringgenerateLogId(){returnjava.util.UUID.randomUUID().toString().replace("-","");}/** * 线程信息 */@Data@Accessors(chain=true)publicstaticclassThreadInfoimplementsSerializable{privatelongthreadId;privateStringthreadName;privatebooleanvirtual;privateThread.Statestate;privateintpriority;privatelongthreadGroupId;privateStringthreadGroupName;publicstaticThreadInfocapture(){Threadthread=Thread.currentThread();ThreadInfoinfo=newThreadInfo();info.setThreadId(thread.threadId());info.setThreadName(thread.getName());info.setVirtual(thread.isVirtual());info.setState(thread.getState());info.setPriority(thread.getPriority());ThreadGroupgroup=thread.getThreadGroup();if(group!=null){info.setThreadGroupId(group.getId());info.setThreadGroupName(group.getName());}returninfo;}}/** * 请求信息 */@Data@Accessors(chain=true)publicstaticclassRequestInfoimplementsSerializable{privateStringrequestId;privateStringmethod;privateStringuri;privateMap<String,String>headers;privateMap<String,String>queryParams;privateStringremoteAddress;privateStringuserAgent;privateMap<String,String>pathVariables;}/** * 响应式上下文 */@Data@Accessors(chain=true)publicstaticclassReactiveContextimplementsSerializable{privateContextViewcontextView;privatebooleanonVirtualThread;privateStringscheduler;privatelongsubscriptionTime;privatelongrequestTime;publicstaticReactiveContextfrom(ContextViewcontextView){ReactiveContextcontext=newReactiveContext();context.setContextView(contextView);context.setOnVirtualThread(Thread.currentThread().isVirtual());context.setSubscriptionTime(System.currentTimeMillis());returncontext;}}/** * 追踪信息 */@Data@Accessors(chain=true)publicstaticclassTraceInfoimplementsSerializable{privateStringtraceId;privateStringspanId;privateStringparentSpanId;privatebooleansampled;privateStringbaggage;publicstaticTraceInfocapture(){// 这里可以从MDC或ThreadLocal获取追踪信息TraceInfoinfo=newTraceInfo();// 实际实现中会从Tracing API获取returninfo;}}}

四、日志切面实现

4.1 基础切面处理器

packagecom.example.logging.aspect;importcom.example.logging.annotation.Loggable;importcom.example.logging.context.LogContext;importcom.example.logging.event.LogEvent;importcom.example.logging.service.LogService;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.context.ApplicationEventPublisher;importorg.springframework.core.annotation.AnnotationUtils;importorg.springframework.expression.Expression;importorg.springframework.expression.ExpressionParser;importorg.springframework.expression.spel.standard.SpelExpressionParser;importorg.springframework.expression.spel.support.StandardEvaluationContext;importorg.springframework.stereotype.Component;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;importjava.lang.reflect.Method;importjava.time.Duration;importjava.time.Instant;importjava.util.concurrent.CompletableFuture;importjava.util.concurrent.Executors;importjava.util.concurrent.atomic.AtomicReference;/** * 日志切面处理器 * Spring Cloud 2025.0.0 新特性支持 */@Slf4j@Aspect@Component@RequiredArgsConstructorpublicclassLogAspectHandler{privatefinalApplicationEventPublishereventPublisher;privatefinalLogServicelogService;privatefinalLogExpressionEvaluatorexpressionEvaluator;// 虚拟线程执行器privatestaticfinaljava.util.concurrent.ExecutorServiceVIRTUAL_EXECUTOR=Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("log-virtual-",0).factory());/** * 切入点:所有@Loggable注解的方法 */@Pointcut("@annotation(com.example.logging.annotation.Loggable)")publicvoidloggablePointcut(){}/** * 环绕通知 */@Around("loggablePointcut()")publicObjecthandleLog(ProceedingJoinPointjoinPoint)throwsThrowable{MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();// 获取注解配置Loggableloggable=AnnotationUtils.findAnnotation(method,Loggable.class);if(loggable==null){returnjoinPoint.proceed();}// 创建日志上下文LogContextlogContext=createLogContext(joinPoint,loggable);// 记录开始logStart(logContext,loggable);InstantstartTime=Instant.now();Objectresult=null;Throwablethrowable=null;try{// 执行目标方法result=joinPoint.proceed();// 根据返回类型处理if(resultinstanceofMono){returnhandleMonoResult((Mono<?>)result,logContext,loggable,startTime);}elseif(resultinstanceofFlux){returnhandleFluxResult((Flux<?>)result,logContext,loggable,startTime);}elseif(resultinstanceofCompletableFuture){returnhandleFutureResult((CompletableFuture<?>)result,logContext,loggable,startTime);}returnresult;}catch(Throwablet){throwable=t;throwt;}finally{// 处理同步结果handleSyncResult(result,throwable,logContext,loggable,startTime);}}/** * 创建日志上下文 */privateLogContextcreateLogContext(ProceedingJoinPointjoinPoint,Loggableloggable){MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();LogContextcontext=LogContext.create();context.setClassName(joinPoint.getTarget().getClass().getName());context.setMethodName(method.getName());context.setSignature(signature.toString());context.setModule(loggable.module());context.setOperation(loggable.value());context.setDescription(loggable.description());// 解析业务编号if(!loggable.bizNo().isEmpty()){StringbizNo=expressionEvaluator.evaluateBizNo(joinPoint,loggable.bizNo());context.setBizNo(bizNo);}// 记录参数if(loggable.logParams()){context.setArgs(joinPoint.getArgs());}returncontext;}/** * 处理Mono结果 */privateMono<?>handleMonoResult(Mono<?>mono,LogContextlogContext,Loggableloggable,InstantstartTime){AtomicReference<Instant>subscribeTime=newAtomicReference<>(Instant.now());returnmono.contextWrite(ctx->{// 在Reactor Context中存储日志信息returnctx.put("log.context",logContext).put("log.startTime",startTime).put("log.subscribeTime",subscribeTime.get());}).doOnSubscribe(subscription->{subscribeTime.set(Instant.now());if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncStart(logContext));}else{logService.logSyncStart(logContext);}}).doOnSuccess(result->{Durationduration=Duration.between(startTime,Instant.now());logContext.withResult(result).calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncSuccess(logContext,duration));}else{logService.logSyncSuccess(logContext,duration);}// 检查慢查询if(duration.toMillis()>loggable.slowThreshold()){logService.logSlowQuery(logContext,duration,loggable.slowThreshold());}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}}).doOnError(throwable->{Durationduration=Duration.between(startTime,Instant.now());logContext.withException(throwable).calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncError(logContext,duration,throwable));}else{logService.logSyncError(logContext,duration,throwable);}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}}).doOnCancel(()->{Durationduration=Duration.between(startTime,Instant.now());if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncCancel(logContext,duration));}});}/** * 处理Flux结果 */privateFlux<?>handleFluxResult(Flux<?>flux,LogContextlogContext,Loggableloggable,InstantstartTime){AtomicReference<Instant>subscribeTime=newAtomicReference<>(Instant.now());AtomicReference<Integer>elementCount=newAtomicReference<>(0);returnflux.contextWrite(ctx->{returnctx.put("log.context",logContext).put("log.startTime",startTime);}).doOnSubscribe(subscription->{subscribeTime.set(Instant.now());if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncStart(logContext));}}).doOnNext(element->{intcount=elementCount.updateAndGet(c->c+1);if(loggable.async()&&count<=10){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncElement(logContext,element,count));}}).doOnComplete(()->{Durationduration=Duration.between(startTime,Instant.now());logContext.calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncComplete(logContext,duration,elementCount.get()));}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}}).doOnError(throwable->{Durationduration=Duration.between(startTime,Instant.now());logContext.withException(throwable).calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncError(logContext,duration,throwable));}else{logService.logSyncError(logContext,duration,throwable);}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}});}/** * 处理Future结果 */privateCompletableFuture<?>handleFutureResult(CompletableFuture<?>future,LogContextlogContext,Loggableloggable,InstantstartTime){if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncStart(logContext));}else{logService.logSyncStart(logContext);}returnfuture.whenComplete((result,throwable)->{Durationduration=Duration.between(startTime,Instant.now());if(throwable!=null){logContext.withException(throwable).calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncError(logContext,duration,throwable));}else{logService.logSyncError(logContext,duration,throwable);}}else{logContext.withResult(result).calculateDuration();if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncSuccess(logContext,duration));}else{logService.logSyncSuccess(logContext,duration);}// 检查慢查询if(duration.toMillis()>loggable.slowThreshold()){logService.logSlowQuery(logContext,duration,loggable.slowThreshold());}}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}});}/** * 处理同步结果 */privatevoidhandleSyncResult(Objectresult,Throwablethrowable,LogContextlogContext,Loggableloggable,InstantstartTime){Durationduration=Duration.between(startTime,Instant.now());logContext.calculateDuration();if(throwable!=null){logContext.withException(throwable);if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncError(logContext,duration,throwable));}else{logService.logSyncError(logContext,duration,throwable);}}else{logContext.withResult(result);if(loggable.async()){VIRTUAL_EXECUTOR.submit(()->logService.logAsyncSuccess(logContext,duration));}else{logService.logSyncSuccess(logContext,duration);}// 检查慢查询if(duration.toMillis()>loggable.slowThreshold()){logService.logSlowQuery(logContext,duration,loggable.slowThreshold());}}// 发布事件if(loggable.pushToCenter()){eventPublisher.publishEvent(newLogEvent(logContext,loggable));}}/** * 记录开始日志 */privatevoidlogStart(LogContextcontext,Loggableloggable){switch(loggable.level()){caseTRACE:log.trace("【开始】{} - {}#{} [线程:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),Thread.currentThread().getName());break;caseDEBUG:log.debug("【开始】{} - {}#{}",context.getOperation(),context.getClassName(),context.getMethodName());break;caseINFO:log.info("【开始】{} - {}#{}",context.getOperation(),context.getClassName(),context.getMethodName());break;caseWARN:caseERROR:// 不记录开始日志break;}}}

4.2 响应式切面处理器

packagecom.example.logging.aspect;importcom.example.logging.annotation.ReactiveLog;importcom.example.logging.context.LogContext;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.core.annotation.AnnotationUtils;importorg.springframework.stereotype.Component;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;importreactor.core.scheduler.Schedulers;importjava.lang.reflect.Method;importjava.time.Duration;importjava.time.Instant;importjava.util.concurrent.atomic.AtomicInteger;importjava.util.concurrent.atomic.AtomicLong;/** * 响应式日志切面 * Spring Cloud 2025.0.0 响应式增强 */@Slf4j@Aspect@Component@RequiredArgsConstructorpublicclassReactiveLogAspect{/** * 处理响应式日志 */@Around("@annotation(com.example.logging.annotation.ReactiveLog)")publicObjecthandleReactiveLog(ProceedingJoinPointjoinPoint)throwsThrowable{MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();ReactiveLogreactiveLog=AnnotationUtils.findAnnotation(method,ReactiveLog.class);if(reactiveLog==null){returnjoinPoint.proceed();}// 创建日志上下文LogContextlogContext=LogContext.create();logContext.setClassName(joinPoint.getTarget().getClass().getName());logContext.setMethodName(method.getName());Objectresult=joinPoint.proceed();if(resultinstanceofMono){returnwrapMono((Mono<?>)result,logContext,reactiveLog);}elseif(resultinstanceofFlux){returnwrapFlux((Flux<?>)result,logContext,reactiveLog);}returnresult;}/** * 包装Mono */privateMono<?>wrapMono(Mono<?>mono,LogContextcontext,ReactiveLogreactiveLog){AtomicLongsubscribeTime=newAtomicLong();AtomicLongrequestTime=newAtomicLong();returnmono.doOnSubscribe(subscription->{subscribeTime.set(System.currentTimeMillis());logReactiveStart(context,"MONO",reactiveLog);}).doOnRequest(n->{requestTime.set(System.currentTimeMillis());if(reactiveLog.logBackpressure()){log.debug("【MONO】请求元素: {}",n);}}).doOnNext(value->{longprocessTime=System.currentTimeMillis()-requestTime.get();if(reactiveLog.traceStream()){log.debug("【MONO】处理元素: {}, 耗时: {}ms",value,processTime);}}).doOnSuccess(value->{longtotalTime=System.currentTimeMillis()-subscribeTime.get();logReactiveSuccess(context,"MONO",value,totalTime,reactiveLog);}).doOnError(throwable->{longtotalTime=System.currentTimeMillis()-subscribeTime.get();logReactiveError(context,"MONO",throwable,totalTime,reactiveLog);}).timeout(Duration.ofMillis(reactiveLog.timeout())).onErrorResume(throwable->{log.error("【MONO】响应式超时: {}ms",reactiveLog.timeout(),throwable);returnMono.error(throwable);}).doFinally(signalType->{if(reactiveLog.traceStream()){log.debug("【MONO】最终信号: {}",signalType);}});}/** * 包装Flux */privateFlux<?>wrapFlux(Flux<?>flux,LogContextcontext,ReactiveLogreactiveLog){AtomicLongsubscribeTime=newAtomicLong();AtomicIntegerelementCount=newAtomicInteger(0);AtomicLongtotalElements=newAtomicLong(0);returnflux.doOnSubscribe(subscription->{subscribeTime.set(System.currentTimeMillis());logReactiveStart(context,"FLUX",reactiveLog);}).doOnRequest(n->{if(reactiveLog.logBackpressure()){log.debug("【FLUX】请求元素: {}",n);}}).doOnNext(element->{intcount=elementCount.incrementAndGet();totalElements.incrementAndGet();if(reactiveLog.traceStream()&&count<=reactiveLog.maxTraceElements()){log.debug("【FLUX】元素[{}]: {}",count,element);}}).doOnComplete(()->{longtotalTime=System.currentTimeMillis()-subscribeTime.get();logReactiveComplete(context,"FLUX",elementCount.get(),totalTime,reactiveLog);}).doOnError(throwable->{longtotalTime=System.currentTimeMillis()-subscribeTime.get();logReactiveError(context,"FLUX",throwable,totalTime,reactiveLog);}).timeout(Duration.ofMillis(reactiveLog.timeout())).onErrorResume(throwable->{log.error("【FLUX】响应式超时: {}ms",reactiveLog.timeout(),throwable);returnFlux.error(throwable);}).doFinally(signalType->{if(reactiveLog.traceStream()){log.debug("【FLUX】最终信号: {}, 总元素: {}",signalType,totalElements.get());}});}privatevoidlogReactiveStart(LogContextcontext,Stringtype,ReactiveLogreactiveLog){log.info("【响应式{}】开始 - {}#{} [线程:{}]",type,context.getClassName(),context.getMethodName(),Thread.currentThread().getName());}privatevoidlogReactiveSuccess(LogContextcontext,Stringtype,Objectvalue,longtotalTime,ReactiveLogreactiveLog){log.info("【响应式{}】成功 - {}#{}, 结果: {}, 耗时: {}ms",type,context.getClassName(),context.getMethodName(),value!=null?value.toString():"null",totalTime);}privatevoidlogReactiveComplete(LogContextcontext,Stringtype,intelementCount,longtotalTime,ReactiveLogreactiveLog){log.info("【响应式{}】完成 - {}#{}, 元素数: {}, 耗时: {}ms",type,context.getClassName(),context.getMethodName(),elementCount,totalTime);}privatevoidlogReactiveError(LogContextcontext,Stringtype,Throwablethrowable,longtotalTime,ReactiveLogreactiveLog){log.error("【响应式{}】失败 - {}#{}, 耗时: {}ms, 错误: {}",type,context.getClassName(),context.getMethodName(),totalTime,throwable.getMessage(),throwable);}}

五、日志服务实现

5.1 日志服务接口

packagecom.example.logging.service;importcom.example.logging.context.LogContext;importreactor.core.publisher.Mono;importjava.time.Duration;/** * 日志服务接口 */publicinterfaceLogService{// 同步日志voidlogSyncStart(LogContextcontext);voidlogSyncSuccess(LogContextcontext,Durationduration);voidlogSyncError(LogContextcontext,Durationduration,Throwablethrowable);// 异步日志voidlogAsyncStart(LogContextcontext);voidlogAsyncSuccess(LogContextcontext,Durationduration);voidlogAsyncError(LogContextcontext,Durationduration,Throwablethrowable);voidlogAsyncCancel(LogContextcontext,Durationduration);voidlogAsyncComplete(LogContextcontext,Durationduration,intelementCount);voidlogAsyncElement(LogContextcontext,Objectelement,intindex);// 慢查询日志voidlogSlowQuery(LogContextcontext,Durationduration,longthreshold);// 虚拟线程日志voidlogVirtualThreadMetrics();// 响应式日志Mono<Void>logReactiveStart(LogContextcontext);Mono<Void>logReactiveSuccess(LogContextcontext,Durationduration);Mono<Void>logReactiveError(LogContextcontext,Durationduration,Throwablethrowable);}

5.2 日志服务实现

packagecom.example.logging.service.impl;importcom.example.logging.context.LogContext;importcom.example.logging.service.LogService;importio.micrometer.core.instrument.MeterRegistry;importio.micrometer.core.instrument.Timer;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.cloud.sleuth.Tracer;importorg.springframework.stereotype.Service;importreactor.core.publisher.Mono;importreactor.core.scheduler.Schedulers;importjava.time.Duration;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;importjava.util.concurrent.atomic.AtomicLong;/** * 日志服务实现 * 集成分布式追踪和性能指标 */@Slf4j@Service@RequiredArgsConstructorpublicclassDefaultLogServiceImplimplementsLogService{privatefinalMeterRegistrymeterRegistry;privatefinalObjectProvider<Tracer>tracerProvider;// 虚拟线程执行器privatefinaljava.util.concurrent.ExecutorServicevirtualExecutor=Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("log-virtual-",0).factory());// 统计信息privatefinalAtomicLongtotalLogs=newAtomicLong(0);privatefinalAtomicLongerrorLogs=newAtomicLong(0);privatefinalAtomicLongslowLogs=newAtomicLong(0);privatefinalAtomicIntegervirtualThreadCount=newAtomicInteger(0);// 计时器缓存privatefinalMap<String,Timer>timerCache=newConcurrentHashMap<>();@OverridepublicvoidlogSyncStart(LogContextcontext){totalLogs.incrementAndGet();log.debug("【同步开始】{} - {}#{} [追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),getTraceId());// 记录指标meterRegistry.counter("log.sync.start","module",context.getModule(),"operation",context.getOperation()).increment();}@OverridepublicvoidlogSyncSuccess(LogContextcontext,Durationduration){log.info("【同步成功】{} - {}#{}, 耗时: {}ms [追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),getTraceId());// 记录计时指标Timertimer=getOrCreateTimer(context.getModule(),context.getOperation());timer.record(duration);// 记录成功率meterRegistry.counter("log.sync.success","module",context.getModule()).increment();}@OverridepublicvoidlogSyncError(LogContextcontext,Durationduration,Throwablethrowable){errorLogs.incrementAndGet();log.error("【同步失败】{} - {}#{}, 耗时: {}ms, 错误: {} [追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),throwable.getMessage(),getTraceId(),throwable);// 记录错误指标meterRegistry.counter("log.sync.error","module",context.getModule(),"exception",throwable.getClass().getSimpleName()).increment();}@OverridepublicvoidlogAsyncStart(LogContextcontext){virtualExecutor.submit(()->{try{totalLogs.incrementAndGet();virtualThreadCount.incrementAndGet();log.debug("【异步开始】{} - {}#{} [虚拟线程:{}, 追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),Thread.currentThread().threadId(),getTraceId());// 记录虚拟线程指标meterRegistry.gauge("log.virtual.thread.count",virtualThreadCount);meterRegistry.counter("log.async.start","module",context.getModule()).increment();}catch(Exceptione){log.error("异步开始日志记录失败",e);}});}@OverridepublicvoidlogAsyncSuccess(LogContextcontext,Durationduration){virtualExecutor.submit(()->{try{log.info("【异步成功】{} - {}#{}, 耗时: {}ms [虚拟线程:{}, 追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),Thread.currentThread().threadId(),getTraceId());// 记录计时指标Timertimer=getOrCreateTimer(context.getModule(),context.getOperation());timer.record(duration);// 记录成功率meterRegistry.counter("log.async.success","module",context.getModule()).increment();}catch(Exceptione){log.error("异步成功日志记录失败",e);}finally{virtualThreadCount.decrementAndGet();}});}@OverridepublicvoidlogAsyncError(LogContextcontext,Durationduration,Throwablethrowable){virtualExecutor.submit(()->{try{errorLogs.incrementAndGet();log.error("【异步失败】{} - {}#{}, 耗时: {}ms, 错误: {} [虚拟线程:{}, 追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),throwable.getMessage(),Thread.currentThread().threadId(),getTraceId(),throwable);// 记录错误指标meterRegistry.counter("log.async.error","module",context.getModule(),"exception",throwable.getClass().getSimpleName()).increment();}catch(Exceptione){log.error("异步错误日志记录失败",e);}finally{virtualThreadCount.decrementAndGet();}});}@OverridepublicvoidlogAsyncCancel(LogContextcontext,Durationduration){virtualExecutor.submit(()->{try{log.warn("【异步取消】{} - {}#{}, 耗时: {}ms [虚拟线程:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),Thread.currentThread().threadId());meterRegistry.counter("log.async.cancel","module",context.getModule()).increment();}catch(Exceptione){log.error("异步取消日志记录失败",e);}finally{virtualThreadCount.decrementAndGet();}});}@OverridepublicvoidlogAsyncComplete(LogContextcontext,Durationduration,intelementCount){virtualExecutor.submit(()->{try{log.info("【异步完成】{} - {}#{}, 元素数: {}, 耗时: {}ms [虚拟线程:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),elementCount,duration.toMillis(),Thread.currentThread().threadId());meterRegistry.counter("log.async.complete","module",context.getModule(),"elementCount",String.valueOf(elementCount)).increment();}catch(Exceptione){log.error("异步完成日志记录失败",e);}finally{virtualThreadCount.decrementAndGet();}});}@OverridepublicvoidlogAsyncElement(LogContextcontext,Objectelement,intindex){virtualExecutor.submit(()->{try{if(index<=10){// 只记录前10个元素log.debug("【异步元素】{} - {}#{}, 元素[{}]: {} [虚拟线程:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),index,element!=null?element.toString():"null",Thread.currentThread().threadId());}}catch(Exceptione){// 忽略元素记录错误}});}@OverridepublicvoidlogSlowQuery(LogContextcontext,Durationduration,longthreshold){slowLogs.incrementAndGet();virtualExecutor.submit(()->{try{log.warn("【慢查询】{} - {}#{}, 耗时: {}ms, 阈值: {}ms [追踪ID:{}]",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),threshold,getTraceId());// 记录慢查询指标meterRegistry.counter("log.slow.query","module",context.getModule(),"duration",String.valueOf(duration.toMillis()),"threshold",String.valueOf(threshold)).increment();}catch(Exceptione){log.error("慢查询日志记录失败",e);}});}@OverridepublicvoidlogVirtualThreadMetrics(){virtualExecutor.submit(()->{try{intcount=virtualThreadCount.get();intactiveThreads=Thread.activeCount();log.debug("【虚拟线程统计】活跃虚拟线程: {}, 总线程: {}, 总日志: {}, 错误日志: {}, 慢查询: {}",count,activeThreads,totalLogs.get(),errorLogs.get(),slowLogs.get());// 记录到指标meterRegistry.gauge("log.metrics.total",totalLogs);meterRegistry.gauge("log.metrics.errors",errorLogs);meterRegistry.gauge("log.metrics.slow",slowLogs);}catch(Exceptione){log.error("虚拟线程指标记录失败",e);}});}@OverridepublicMono<Void>logReactiveStart(LogContextcontext){returnMono.fromRunnable(()->{log.debug("【响应式开始】{} - {}#{}",context.getOperation(),context.getClassName(),context.getMethodName());}).subscribeOn(Schedulers.boundedElastic()).then();}@OverridepublicMono<Void>logReactiveSuccess(LogContextcontext,Durationduration){returnMono.fromRunnable(()->{log.info("【响应式成功】{} - {}#{}, 耗时: {}ms",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis());}).subscribeOn(Schedulers.boundedElastic()).then();}@OverridepublicMono<Void>logReactiveError(LogContextcontext,Durationduration,Throwablethrowable){returnMono.fromRunnable(()->{log.error("【响应式失败】{} - {}#{}, 耗时: {}ms, 错误: {}",context.getOperation(),context.getClassName(),context.getMethodName(),duration.toMillis(),throwable.getMessage(),throwable);}).subscribeOn(Schedulers.boundedElastic()).then();}/** * 获取或创建计时器 */privateTimergetOrCreateTimer(Stringmodule,Stringoperation){Stringkey=module+"."+operation;returntimerCache.computeIfAbsent(key,k->Timer.builder("log.execution.time").description("Method execution time").tag("module",module).tag("operation",operation).publishPercentiles(0.5,0.95,0.99).publishPercentileHistogram().register(meterRegistry));}/** * 获取追踪ID */privateStringgetTraceId(){try{Tracertracer=tracerProvider.getIfAvailable();if(tracer!=null&&tracer.currentSpan()!=null){returntracer.currentSpan().context().traceId();}}catch(Exceptione){// 忽略异常}return"N/A";}/** * 关闭资源 */publicvoidshutdown(){virtualExecutor.shutdown();try{if(!virtualExecutor.awaitTermination(5,TimeUnit.SECONDS)){virtualExecutor.shutdownNow();}}catch(InterruptedExceptione){virtualExecutor.shutdownNow();Thread.currentThread().interrupt();}log.info("日志服务关闭完成");}}

六、日志自动配置

6.1 自动配置类

packagecom.example.logging.autoconfigure;importcom.example.logging.aspect.LogAspectHandler;importcom.example.logging.aspect.ReactiveLogAspect;importcom.example.logging.service.LogService;importcom.example.logging.service.impl.DefaultLogServiceImpl;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;importorg.springframework.boot.autoconfigure.condition.ConditionalOnProperty;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.EnableAspectJAutoProxy;importorg.springframework.scheduling.annotation.EnableAsync;importorg.springframework.scheduling.annotation.EnableScheduling;importjava.util.concurrent.Executors;/** * 日志自动配置 */@Slf4j@Configuration@EnableAspectJAutoProxy@EnableAsync@EnableScheduling@EnableConfigurationProperties(LogProperties.class)@ConditionalOnProperty(prefix="spring.logging",name="enabled",havingValue="true",matchIfMissing=true)publicclassLogAutoConfiguration{/** * 日志切面处理器 */@Bean@ConditionalOnMissingBeanpublicLogAspectHandlerlogAspectHandler(){returnnewLogAspectHandler();}/** * 响应式日志切面 */@Bean@ConditionalOnMissingBeanpublicReactiveLogAspectreactiveLogAspect(){returnnewReactiveLogAspect();}/** * 日志服务 */@Bean@ConditionalOnMissingBeanpublicLogServicelogService(){returnnewDefaultLogServiceImpl();}/** * 虚拟线程执行器 */@Bean(destroyMethod="shutdown")publicjava.util.concurrent.ExecutorServicevirtualThreadExecutor(){returnExecutors.newThreadPerTaskExecutor(Thread.ofVirtual().name("global-virtual-",0).factory());}/** * 日志指标调度器 */@BeanpublicLogMetricsSchedulerlogMetricsScheduler(LogServicelogService){returnnewLogMetricsScheduler(logService);}}

6.2 配置属性

packagecom.example.logging.autoconfigure;importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;importjava.time.Duration;importjava.util.HashMap;importjava.util.Map;/** * 日志配置属性 */@Data@ConfigurationProperties(prefix="spring.logging")publicclassLogProperties{/** * 是否启用日志功能 */privatebooleanenabled=true;/** * 是否启用异步日志 */privatebooleanasyncEnabled=true;/** * 是否启用虚拟线程 */privatebooleanvirtualThreadEnabled=true;/** * 是否启用响应式日志 */privatebooleanreactiveEnabled=true;/** * 默认日志级别 */privateStringdefaultLevel="INFO";/** * 慢查询阈值(毫秒) */privatelongslowThreshold=1000;/** * 是否记录方法参数 */privatebooleanlogParams=true;/** * 是否记录返回值 */privatebooleanlogResult=true;/** * 是否记录执行时间 */privatebooleanlogTime=true;/** * 是否推送到日志中心 */privatebooleanpushToCenter=true;/** * 日志中心地址 */privateStringcenterUrl="http://localhost:8080";/** * 连接超时时间 */privateDurationconnectTimeout=Duration.ofSeconds(5);/** * 读取超时时间 */privateDurationreadTimeout=Duration.ofSeconds(10);/** * 重试次数 */privateintretryTimes=3;/** * 批量发送大小 */privateintbatchSize=100;/** * 批量发送间隔 */privateDurationbatchInterval=Duration.ofSeconds(5);/** * 模块配置 */privateMap<String,ModuleConfig>modules=newHashMap<>();@DatapublicstaticclassModuleConfig{privatebooleanenabled=true;privateStringlevel="INFO";privatebooleanasync=true;privatebooleanvirtualThread=true;privatebooleanlogParams=true;privatebooleanlogResult=true;privatelongslowThreshold=1000;}}

七、日志 Starter 模块

7.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>com.example</groupId><artifactId>spring-cloud-logging</artifactId><version>1.0.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>logging-starter</artifactId><dependencies><!-- 内部模块依赖 --><dependency><groupId>com.example</groupId><artifactId>logging-annotation</artifactId><version>${project.version}</version></dependency><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- 配置处理器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- Micrometer 追踪 --><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-tracing</artifactId></dependency><!-- Micrometer 指标 --><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-core</artifactId></dependency><!-- Reactor --><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId></dependency></dependencies></project>

7.2 spring.factories

# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.logging.autoconfigure.LogAutoConfiguration # Spring Boot Configuration Processor org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.logging.autoconfigure.LogPropertiesConfiguration

八、使用示例

8.1 用户服务示例

packagecom.example.user.service;importcom.example.logging.annotation.Loggable;importcom.example.logging.annotation.ReactiveLog;importcom.example.logging.annotation.TraceLog;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;importjava.util.List;importjava.util.concurrent.CompletableFuture;importjava.util.concurrent.Executors;/** * 用户服务示例 */@Slf4j@Service@RequiredArgsConstructorpublicclassUserService{/** * 同步方法日志 */@Loggable(module="用户管理",value="创建用户",type=Loggable.LogType.INSERT,bizNo="#user.username")@TransactionalpublicUsercreateUser(Useruser){// 业务逻辑returnuserRepository.save(user);}/** * 异步方法日志 */@Loggable(module="用户管理",value="批量导入用户",async=true,virtualThread=true,slowThreshold=5000)publicCompletableFuture<List<User>>batchImport(List<User>users){returnCompletableFuture.supplyAsync(()->{// 批量处理逻辑returnuserRepository.saveAll(users);},Executors.newVirtualThreadPerTaskExecutor());}/** * 响应式方法日志 */@ReactiveLog(traceStream=true,maxTraceElements=20,timeout=30000)@Loggable(module="用户管理",value="查询用户列表",async=true)publicFlux<User>listUsers(intpage,intsize){returnuserRepository.findAll().skip(page*size).take(size).doOnNext(user->log.debug("处理用户: {}",user.getUsername()));}/** * 分布式追踪日志 */@TraceLog(spanName="user.update",tags={"userId=#userId","operation=update"},remote=false)@Loggable(module="用户管理",value="更新用户信息",level=Loggable.LogLevel.INFO)publicMono<User>updateUser(LonguserId,UserDTOdto){returnuserRepository.findById(userId).flatMap(user->{user.setEmail(dto.getEmail());user.setPhone(dto.getPhone());returnuserRepository.save(user);});}/** * 错误处理示例 */@Loggable(module="用户管理",value="删除用户",level=Loggable.LogLevel.ERROR,stackTrace=true)publicvoiddeleteUser(LonguserId){try{userRepository.deleteById(userId);}catch(Exceptione){log.error("删除用户失败: {}",userId,e);thrownewBusinessException("删除用户失败");}}/** * 虚拟线程示例 */@Loggable(module="用户管理",value="并发处理用户",async=true,virtualThread=true,asyncStrategy=Loggable.AsyncStrategy.VIRTUAL_THREAD)publicvoidprocessUsersConcurrently(List<Long>userIds){try(varscope=newjava.util.concurrent.StructuredTaskScope.ShutdownOnFailure()){for(LonguserId:userIds){scope.fork(()->processUser(userId));}scope.join();}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException("处理中断",e);}}privateUserprocessUser(LonguserId){// 处理单个用户returnuserRepository.findById(userId).map(user->{// 业务处理returnuser;}).orElseThrow(()->newRuntimeException("用户不存在"));}}

九、配置示例

9.1 application.yml

spring:application:name:user-service# 日志配置logging:enabled:trueasync-enabled:truevirtual-thread-enabled:truereactive-enabled:truedefault-level:INFOslow-threshold:1000log-params:truelog-result:truepush-to-center:truecenter-url:http://log-center:8080# 模块配置modules:user-service:enabled:truelevel:INFOasync:truevirtual-thread:trueslow-threshold:500order-service:enabled:truelevel:DEBUGasync:false# 虚拟线程配置threads:virtual:enabled:truename-prefix:"app-virtual-"# 响应式配置webflux:base-path:/api# 分布式追踪sleuth:enabled:truesampler:probability:1.0propagation:type:B3baggage:enabled:true# Micrometermicrometer:tracing:enabled:truemetrics:export:prometheus:enabled:true# 日志级别配置logging:level:com.example:INFOcom.example.logging:DEBUGorg.springframework.web:INFOreactor.netty:INFOpattern:console:"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"file:"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"# Logback配置logback:rollingpolicy:max-file-size:10MBmax-history:30total-size-cap:1GB# Logstashlogstash:enabled:truedestination:logstash:5044# 管理端点management:endpoints:web:exposure:include:"health,info,metrics,prometheus,loggers"endpoint:health:show-details:alwaysloggers:enabled:truemetrics:tags:application:${spring.application.name}tracing:sampling:probability:0.1

十、网关集成

十一、Spring Cloud Gateway 集成

11.1 网关全局过滤器

packagecom.example.gateway.filter;importcom.example.logging.annotation.Loggable;importcom.example.logging.context.LogContext;importio.micrometer.tracing.Span;importio.micrometer.tracing.Tracer;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.cloud.gateway.route.Route;importorg.springframework.cloud.gateway.support.ServerWebExchangeUtils;importorg.springframework.core.Ordered;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.core.io.buffer.DataBufferFactory;importorg.springframework.core.io.buffer.DataBufferUtils;importorg.springframework.http.HttpHeaders;importorg.springframework.http.MediaType;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.http.server.reactive.ServerHttpRequestDecorator;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.http.server.reactive.ServerHttpResponseDecorator;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;importjava.net.InetSocketAddress;importjava.net.URI;importjava.nio.charset.StandardCharsets;importjava.time.Duration;importjava.time.Instant;importjava.util.List;importjava.util.Map;importjava.util.concurrent.atomic.AtomicLong;importjava.util.concurrent.atomic.AtomicReference;importstaticorg.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;/** * Spring Cloud Gateway 全局日志过滤器 * 支持响应式日志记录和虚拟线程 */@Slf4j@Component@RequiredArgsConstructorpublicclassGatewayGlobalLogFilterimplementsGlobalFilter,Ordered{privatefinalTracertracer;// 请求计数器privatefinalAtomicLongrequestCounter=newAtomicLong(0);@OverridepublicintgetOrder(){returnOrdered.HIGHEST_PRECEDENCE+1;}@Loggable(module="网关",value="网关请求处理",async=true,virtualThread=true,level=Loggable.LogLevel.INFO,pushToCenter=true)@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){longrequestId=requestCounter.incrementAndGet();InstantstartTime=Instant.now();ServerHttpRequestrequest=exchange.getRequest();// 创建日志上下文LogContextlogContext=createLogContext(exchange,requestId);// 获取路由信息Routeroute=exchange.getAttribute(GATEWAY_ROUTE_ATTR);if(route!=null){logContext.addAttribute("route.id",route.getId());logContext.addAttribute("route.uri",route.getUri().toString());}// 创建SpanSpangatewaySpan=createGatewaySpan(request,logContext);// 包装请求和响应ServerHttpRequestdecoratedRequest=decorateRequest(request,logContext);ServerHttpResponsedecoratedResponse=decorateResponse(exchange.getResponse(),logContext);ServerWebExchangedecoratedExchange=exchange.mutate().request(decoratedRequest).response(decoratedResponse).build();// 记录请求开始logRequestStart(logContext,request);returnchain.filter(decoratedExchange).contextWrite(ctx->{// 在上下文中存储日志信息returnctx.put("gateway.logContext",logContext).put("gateway.startTime",startTime).put("gateway.requestId",requestId).put("gateway.span",gatewaySpan);}).doOnSuccess(v->{// 记录请求成功longduration=Duration.between(startTime,Instant.now()).toMillis();logRequestSuccess(logContext,duration,gatewaySpan);}).doOnError(throwable->{// 记录请求失败longduration=Duration.between(startTime,Instant.now()).toMillis();logRequestError(logContext,throwable,duration,gatewaySpan);}).doFinally(signalType->{// 结束Spanif(gatewaySpan!=null){gatewaySpan.tag("signal.type",signalType.name());gatewaySpan.end();}// 清理资源DataBufferUtils.release(logContext.getAttributes().remove("request.body"));DataBufferUtils.release(logContext.getAttributes().remove("response.body"));log.debug("【网关】请求处理完成: {},信号: {}",requestId,signalType);});}/** * 创建日志上下文 */privateLogContextcreateLogContext(ServerWebExchangeexchange,longrequestId){ServerHttpRequestrequest=exchange.getRequest();LogContextcontext=LogContext.create();// 请求信息LogContext.RequestInforequestInfo=newLogContext.RequestInfo();requestInfo.setRequestId(String.valueOf(requestId));requestInfo.setMethod(request.getMethod().name());requestInfo.setUri(request.getURI().toString());// 请求头Map<String,String>headers=request.getHeaders().toSingleValueMap();requestInfo.setHeaders(headers);// 查询参数Map<String,String>queryParams=request.getQueryParams().toSingleValueMap();requestInfo.setQueryParams(queryParams);// 远程地址InetSocketAddressremoteAddress=request.getRemoteAddress();if(remoteAddress!=null){requestInfo.setRemoteAddress(remoteAddress.getAddress().getHostAddress());}// User AgentrequestInfo.setUserAgent(request.getHeaders().getFirst("User-Agent"));context.setRequestInfo(requestInfo);// 追踪信息StringtraceId=request.getHeaders().getFirst("X-B3-TraceId");StringspanId=request.getHeaders().getFirst("X-B3-SpanId");if(traceId!=null){context.setTraceId(traceId);}if(spanId!=null){context.setSpanId(spanId);}returncontext;}/** * 创建网关Span */privateSpancreateGatewaySpan(ServerHttpRequestrequest,LogContextlogContext){if(tracer==null){returnnull;}StringspanName="gateway:"+request.getMethod().name()+":"+request.getURI().getPath();Spanspan=tracer.nextSpan().name(spanName).kind(Span.Kind.SERVER).tag("http.method",request.getMethod().name()).tag("http.path",request.getURI().getPath()).tag("gateway.request.id",String.valueOf(logContext.getRequestInfo().getRequestId())).tag("thread.virtual",String.valueOf(Thread.currentThread().isVirtual())).start();// 设置追踪ID到上下文logContext.setTraceId(span.context().traceId());logContext.setSpanId(span.context().spanId());returnspan;}/** * 包装请求(记录请求体) */privateServerHttpRequestdecorateRequest(ServerHttpRequestrequest,LogContextlogContext){// 如果不是JSON请求,直接返回原请求MediaTypecontentType=request.getHeaders().getContentType();if(contentType==null||!contentType.includes(MediaType.APPLICATION_JSON)){returnrequest;}returnnewServerHttpRequestDecorator(request){privatefinalAtomicReference<byte[]>cachedBody=newAtomicReference<>();@OverridepublicFlux<DataBuffer>getBody(){returnsuper.getBody().doOnNext(dataBuffer->{// 缓存请求体用于日志记录byte[]bytes=newbyte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer);// 限制大小,避免大请求体导致内存问题if(bytes.length<=1024*10){// 10KB以内cachedBody.set(bytes);logContext.addAttribute("request.body",newString(bytes,StandardCharsets.UTF_8));}// 重新创建DataBufferDataBufferFactoryfactory=request.bufferFactory();DataBuffernewBuffer=factory.wrap(bytes);cachedBody.set(bytes);}).thenMany(Flux.defer(()->{byte[]body=cachedBody.get();if(body!=null){returnFlux.just(request.bufferFactory().wrap(body));}returnFlux.empty();}));}};}/** * 包装响应(记录响应体) */privateServerHttpResponsedecorateResponse(ServerHttpResponseresponse,LogContextlogContext){returnnewServerHttpResponseDecorator(response){privatefinalAtomicReference<byte[]>cachedBody=newAtomicReference<>();@OverridepublicMono<Void>writeWith(Publisher<?extendsDataBuffer>body){returnsuper.writeWith(Flux.from(body).doOnNext(dataBuffer->{// 缓存响应体用于日志记录byte[]bytes=newbyte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer);// 限制大小,避免大响应体导致内存问题if(bytes.length<=1024*10){// 10KB以内cachedBody.set(bytes);logContext.addAttribute("response.body",newString(bytes,StandardCharsets.UTF_8));logContext.addAttribute("response.status",String.valueOf(getStatusCode().value()));}// 重新创建DataBufferDataBufferFactoryfactory=response.bufferFactory();DataBuffernewBuffer=factory.wrap(bytes);cachedBody.set(bytes);}).thenMany(Flux.defer(()->{byte[]bodyBytes=cachedBody.get();if(bodyBytes!=null){returnFlux.just(response.bufferFactory().wrap(bodyBytes));}returnFlux.empty();})));}@OverridepublicMono<Void>writeAndFlushWith(Publisher<?extendsPublisher<?extendsDataBuffer>>body){returnwriteWith(Flux.from(body).flatMapSequential(p->p));}};}/** * 记录请求开始 */privatevoidlogRequestStart(LogContextcontext,ServerHttpRequestrequest){log.info("【网关请求开始】ID: {}, 方法: {}, 路径: {}, 来源IP: {}, 虚拟线程: {}",context.getRequestInfo().getRequestId(),request.getMethod().name(),request.getURI().getPath(),context.getRequestInfo().getRemoteAddress(),Thread.currentThread().isVirtual());}/** * 记录请求成功 */privatevoidlogRequestSuccess(LogContextcontext,longduration,Spanspan){StringtraceId=span!=null?span.context().traceId():"N/A";log.info("【网关请求成功】ID: {}, 耗时: {}ms, 追踪ID: {}, 线程: {}",context.getRequestInfo().getRequestId(),duration,traceId,Thread.currentThread().getName());// 记录到指标io.micrometer.core.instrument.Metrics.counter("gateway.requests.success","method",context.getRequestInfo().getMethod(),"path",context.getRequestInfo().getUri()).increment();io.micrometer.core.instrument.Timer.builder("gateway.request.duration").tag("status","success").register(io.micrometer.core.instrument.Metrics.globalRegistry).record(Duration.ofMillis(duration));}/** * 记录请求失败 */privatevoidlogRequestError(LogContextcontext,Throwablethrowable,longduration,Spanspan){StringtraceId=span!=null?span.context().traceId():"N/A";log.error("【网关请求失败】ID: {}, 耗时: {}ms, 追踪ID: {}, 错误: {}",context.getRequestInfo().getRequestId(),duration,traceId,throwable.getMessage(),throwable);// 记录到指标io.micrometer.core.instrument.Metrics.counter("gateway.requests.error","method",context.getRequestInfo().getMethod(),"path",context.getRequestInfo().getUri(),"exception",throwable.getClass().getSimpleName()).increment();}}

11.2 网关虚拟线程配置

packagecom.example.gateway.config;importorg.springframework.boot.autoconfigure.web.ServerProperties;importorg.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;importorg.springframework.boot.web.reactive.server.ReactiveWebServerFactory;importorg.springframework.cloud.gateway.config.HttpClientCustomizer;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.client.reactive.ReactorClientHttpConnector;importorg.springframework.http.client.reactive.ReactorResourceFactory;importreactor.netty.http.client.HttpClient;importreactor.netty.resources.ConnectionProvider;importreactor.netty.resources.LoopResources;importjava.time.Duration;importjava.util.concurrent.Executors;/** * 网关虚拟线程配置 */@ConfigurationpublicclassGatewayVirtualThreadConfig{/** * 配置Netty使用虚拟线程 */@BeanpublicReactiveWebServerFactoryreactiveWebServerFactory(ServerPropertiesserverProperties){NettyReactiveWebServerFactoryfactory=newNettyReactiveWebServerFactory();factory.addServerCustomizers(httpServer->{// 使用虚拟线程的事件循环组httpServer.runOn(LoopResources.create("gateway-virtual",1,// 线程数true,// 守护线程true// 选择最佳事件循环));returnhttpServer;});returnfactory;}/** * 配置HTTP客户端使用虚拟线程 */@BeanpublicHttpClientCustomizerhttpClientVirtualThreadCustomizer(){returnfactory->{ConnectionProviderprovider=ConnectionProvider.builder("gateway-virtual-connection").maxConnections(500).pendingAcquireTimeout(Duration.ofSeconds(60)).evictInBackground(Duration.ofSeconds(120)).build();HttpClienthttpClient=HttpClient.create(provider).runOn(LoopResources.create("client-virtual",2,true,true)).responseTimeout(Duration.ofSeconds(30)).compress(true);returnhttpClient;};}/** * Reactor资源工厂 */@BeanpublicReactorResourceFactoryreactorResourceFactory(){ReactorResourceFactoryfactory=newReactorResourceFactory();factory.setUseGlobalResources(false);factory.setLoopResourcesSupplier(()->LoopResources.create("gateway-global-virtual",Runtime.getRuntime().availableProcessors()*2,true,true));returnfactory;}/** * 虚拟线程执行器 */@Beanpublicjava.util.concurrent.ExecutorServicegatewayVirtualExecutor(){returnExecutors.newThreadPerTaskExecutor(Thread.ofVirtual().name("gateway-vt-",0).factory());}}

十二、服务间调用日志

12.1 OpenFeign 日志拦截器

packagecom.example.feign.interceptor;importcom.example.logging.annotation.Loggable;importcom.example.logging.context.LogContext;importfeign.RequestInterceptor;importfeign.RequestTemplate;importio.micrometer.tracing.Span;importio.micrometer.tracing.Tracer;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.openfeign.FeignClient;importorg.springframework.stereotype.Component;importjava.time.Instant;importjava.util.concurrent.Executors;importjava.util.concurrent.atomic.AtomicLong;/** * OpenFeign 日志拦截器 * 自动传播追踪信息和记录调用日志 */@Slf4j@Component@RequiredArgsConstructorpublicclassFeignLoggingInterceptorimplementsRequestInterceptor{privatefinalTracertracer;// Feign调用计数器privatefinalAtomicLongfeignCallCounter=newAtomicLong(0);// 虚拟线程执行器privatefinaljava.util.concurrent.ExecutorServicevirtualExecutor=Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("feign-log-",0).factory());@Loggable(module="服务调用",value="Feign远程调用",async=true,virtualThread=true,level=Loggable.LogLevel.INFO)@Overridepublicvoidapply(RequestTemplatetemplate){longcallId=feignCallCounter.incrementAndGet();InstantstartTime=Instant.now();// 创建日志上下文LogContextlogContext=LogContext.create();logContext.setOperation("Feign远程调用");logContext.setClassName(template.feignTarget().type().getName());logContext.setMethodName(template.methodMetadata().configKey());logContext.addAttribute("feign.call.id",String.valueOf(callId));logContext.addAttribute("service.url",template.feignTarget().url());logContext.addAttribute("request.url",template.url());logContext.addAttribute("request.headers",template.headers());// 记录开始日志logFeignCallStart(logContext,template);// 注入追踪头injectTraceHeaders(template);// 注入自定义头template.header("X-Feign-Call-Id",String.valueOf(callId));template.header("X-Feign-Start-Time",String.valueOf(startTime.toEpochMilli()));template.header("X-Is-Virtual-Thread",String.valueOf(Thread.currentThread().isVirtual()));// 记录请求体(如果有)if(template.body()!=null){Stringbody=newString(template.body());if(body.length()<=1024){// 只记录1KB以内的请求体logContext.addAttribute("request.body",body);}}// 异步记录调用信息virtualExecutor.submit(()->{try{// 记录到指标io.micrometer.core.instrument.Metrics.counter("feign.calls.initiated","service",extractServiceName(template),"method",template.method()).increment();// 创建Feign调用的SpancreateFeignSpan(template,logContext);}catch(Exceptione){log.error("Feign日志记录失败",e);}});// 存储日志上下文,供响应拦截器使用template.header("X-Log-Context",serializeLogContext(logContext));}/** * 注入追踪头 */privatevoidinjectTraceHeaders(RequestTemplatetemplate){if(tracer==null){return;}SpancurrentSpan=tracer.currentSpan();if(currentSpan!=null){// 传播追踪上下文tracer.inject(currentSpan.context(),template,(carrier,key,value)->{if(carrierinstanceofRequestTemplate){((RequestTemplate)carrier).header(key,value);}});// 记录追踪信息到日志上下文template.header("X-Parent-Trace-Id",currentSpan.context().traceId());template.header("X-Parent-Span-Id",currentSpan.context().spanId());}}/** * 创建Feign调用Span */privatevoidcreateFeignSpan(RequestTemplatetemplate,LogContextlogContext){if(tracer==null){return;}StringspanName="feign:"+extractServiceName(template)+":"+template.methodMetadata().method().getName();Spanspan=tracer.nextSpan().name(spanName).kind(Span.Kind.CLIENT).tag("rpc.system","feign").tag("rpc.service",extractServiceName(template)).tag("rpc.method",template.methodMetadata().method().getName()).tag("http.method",template.method()).tag("http.url",template.url()).tag("feign.call.id",String.valueOf(logContext.getAttributes().get("feign.call.id"))).start();// 设置追踪ID到日志上下文logContext.setTraceId(span.context().traceId());logContext.setSpanId(span.context().spanId());// 在Span上下文中执行try(Tracer.SpanInScopescope=tracer.withSpan(span)){log.debug("【Feign调用Span创建】名称: {}, 追踪ID: {}",spanName,span.context().traceId());}}/** * 提取服务名称 */privateStringextractServiceName(RequestTemplatetemplate){Class<?>targetType=template.feignTarget().type();FeignClientfeignClient=targetType.getAnnotation(FeignClient.class);if(feignClient!=null){returnfeignClient.name();}returntargetType.getSimpleName();}/** * 记录Feign调用开始 */privatevoidlogFeignCallStart(LogContextcontext,RequestTemplatetemplate){log.info("【Feign调用开始】服务: {}, 方法: {}, URL: {}, 虚拟线程: {}",extractServiceName(template),template.methodMetadata().method().getName(),template.url(),Thread.currentThread().isVirtual());}/** * 序列化日志上下文 */privateStringserializeLogContext(LogContextcontext){try{// 简单序列化,实际可以使用JSONreturnString.format("%s|%s|%s",context.getLogId(),context.getTraceId(),context.getSpanId());}catch(Exceptione){return"";}}}

12.2 Feign 响应拦截器

packagecom.example.feign.interceptor;importcom.example.logging.context.LogContext;importfeign.Response;importfeign.codec.ErrorDecoder;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjava.time.Duration;importjava.time.Instant;importjava.util.concurrent.Executors;/** * Feign 响应拦截器 * 记录响应日志和处理错误 */@Slf4j@Component@RequiredArgsConstructorpublicclassFeignResponseInterceptorimplementsErrorDecoder{// 虚拟线程执行器privatefinaljava.util.concurrent.ExecutorServicevirtualExecutor=Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("feign-resp-",0).factory());@OverridepublicExceptiondecode(StringmethodKey,Responseresponse){// 解析日志上下文StringlogContextHeader=response.request().headers().get("X-Log-Context");if(logContextHeader!=null&&!logContextHeader.isEmpty()){LogContextlogContext=deserializeLogContext(logContextHeader);StringstartTimeHeader=response.request().headers().get("X-Feign-Start-Time");if(startTimeHeader!=null){try{longstartTime=Long.parseLong(startTimeHeader);longduration=System.currentTimeMillis()-startTime;// 异步记录错误响应virtualExecutor.submit(()->{logFeignError(logContext,methodKey,response,duration);});}catch(NumberFormatExceptione){log.warn("解析Feign开始时间失败",e);}}}// 使用默认错误解码器returnnewErrorDecoder.Default().decode(methodKey,response);}/** * 记录Feign调用成功 */publicvoidlogFeignSuccess(Responseresponse,Durationduration){virtualExecutor.submit(()->{try{StringlogContextHeader=response.request().headers().get("X-Log-Context");if(logContextHeader!=null&&!logContextHeader.isEmpty()){LogContextlogContext=deserializeLogContext(logContextHeader);log.info("【Feign调用成功】服务: {}, 方法: {}, 耗时: {}ms, 状态码: {}",extractServiceNameFromRequest(response.request()),extractMethodNameFromRequest(response.request()),duration.toMillis(),response.status());// 记录到指标io.micrometer.core.instrument.Metrics.counter("feign.calls.success","service",extractServiceNameFromRequest(response.request()),"status",String.valueOf(response.status())).increment();io.micrometer.core.instrument.Timer.builder("feign.call.duration").tag("status","success").register(io.micrometer.core.instrument.Metrics.globalRegistry).record(duration);}}catch(Exceptione){log.error("Feign成功日志记录失败",e);}});}/** * 记录Feign调用错误 */privatevoidlogFeignError(LogContextcontext,StringmethodKey,Responseresponse,longduration){log.error("【Feign调用失败】服务: {}, 方法: {}, 耗时: {}ms, 状态码: {}, 错误: {}",extractServiceName(context),methodKey,duration,response.status(),response.reason());// 记录到指标io.micrometer.core.instrument.Metrics.counter("feign.calls.error","service",extractServiceName(context),"status",String.valueOf(response.status())).increment();io.micrometer.core.instrument.Timer.builder("feign.call.duration").tag("status","error").register(io.micrometer.core.instrument.Metrics.globalRegistry).record(Duration.ofMillis(duration));}/** * 反序列化日志上下文 */privateLogContextdeserializeLogContext(Stringheader){try{String[]parts=header.split("\\|");if(parts.length>=3){LogContextcontext=newLogContext();context.setLogId(parts[0]);context.setTraceId(parts[1]);context.setSpanId(parts[2]);returncontext;}}catch(Exceptione){log.warn("反序列化日志上下文失败",e);}returnLogContext.create();}/** * 从请求中提取服务名称 */privateStringextractServiceNameFromRequest(feign.Requestrequest){// 从URL或header中提取服务名称Stringurl=request.url();// 简化的提取逻辑if(url.contains("/api/")){String[]parts=url.split("/");for(inti=0;i<parts.length;i++){if("api".equals(parts[i])&&i+1<parts.length){returnparts[i+1];}}}return"unknown";}/** * 从请求中提取方法名称 */privateStringextractMethodNameFromRequest(feign.Requestrequest){Stringurl=request.url();String[]parts=url.split("/");returnparts.length>0?parts[parts.length-1]:"unknown";}/** * 从上下文中提取服务名称 */privateStringextractServiceName(LogContextcontext){ObjectserviceName=context.getAttributes().get("service.url");returnserviceName!=null?serviceName.toString():"unknown";}}

十三、服务网格集成

13.1 Istio 边车日志集成

packagecom.example.mesh.integration;importcom.example.logging.annotation.Loggable;importcom.example.logging.context.LogContext;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.client.loadbalancer.LoadBalanced;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.HttpHeaders;importorg.springframework.http.HttpRequest;importorg.springframework.http.client.ClientHttpRequestExecution;importorg.springframework.http.client.ClientHttpRequestInterceptor;importorg.springframework.http.client.ClientHttpResponse;importorg.springframework.web.client.RestTemplate;importorg.springframework.web.reactive.function.client.ClientRequest;importorg.springframework.web.reactive.function.client.ClientResponse;importorg.springframework.web.reactive.function.client.ExchangeFilterFunction;importorg.springframework.web.reactive.function.client.WebClient;importreactor.core.publisher.Mono;importjava.io.IOException;importjava.time.Duration;importjava.time.Instant;importjava.util.Collections;importjava.util.List;importjava.util.concurrent.Executors;/** * 服务网格集成配置 * 支持Istio边车日志和追踪 */@Slf4j@Configuration@RequiredArgsConstructorpublicclassServiceMeshIntegrationConfig{/** * RestTemplate 拦截器(支持Istio) */@Bean@LoadBalancedpublicRestTemplatemeshAwareRestTemplate(){RestTemplaterestTemplate=newRestTemplate();restTemplate.setInterceptors(Collections.singletonList(newMeshRestTemplateInterceptor()));returnrestTemplate;}/** * WebClient 过滤器(支持Istio) */@Bean@LoadBalancedpublicWebClientmeshAwareWebClient(){returnWebClient.builder().filter(meshExchangeFilterFunction()).build();}/** * Mesh感知的ExchangeFilterFunction */@BeanpublicExchangeFilterFunctionmeshExchangeFilterFunction(){returnExchangeFilterFunction.ofRequestProcessor(clientRequest->{// 在请求前添加Mesh相关headerClientRequestnewRequest=ClientRequest.from(clientRequest).header("X-Request-Id",generateRequestId()).header("X-Mesh-Service",getCurrentServiceName()).header("X-Envoy-Timeout","30s").build();returnMono.just(newRequest);});}/** * RestTemplate拦截器实现 */@Loggable(module="服务网格",value="Mesh服务调用",async=true,virtualThread=true)privatestaticclassMeshRestTemplateInterceptorimplementsClientHttpRequestInterceptor{privatefinaljava.util.concurrent.ExecutorServicevirtualExecutor=Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("mesh-rest-",0).factory());@OverridepublicClientHttpResponseintercept(HttpRequestrequest,byte[]body,ClientHttpRequestExecutionexecution)throwsIOException{longstartTime=System.currentTimeMillis();StringrequestId=generateRequestId();// 添加Mesh headerrequest.getHeaders().add("X-Request-Id",requestId);request.getHeaders().add("X-Mesh-Service",getCurrentServiceName());request.getHeaders().add("X-Caller-Service",System.getProperty("spring.application.name","unknown"));// 记录调用开始logMeshCallStart(request,requestId);try{ClientHttpResponseresponse=execution.execute(request,body);longduration=System.currentTimeMillis()-startTime;// 异步记录调用成功virtualExecutor.submit(()->{logMeshCallSuccess(request,response,requestId,duration);});returnresponse;}catch(IOExceptione){longduration=System.currentTimeMillis()-startTime;// 异步记录调用失败virtualExecutor.submit(()->{logMeshCallError(request,e,requestId,duration);});throwe;}}privatevoidlogMeshCallStart(HttpRequestrequest,StringrequestId){log.info("【Mesh调用开始】ID: {}, 服务: {}, 路径: {}, 虚拟线程: {}",requestId,extractServiceName(request.getURI().toString()),request.getURI().getPath(),Thread.currentThread().isVirtual());}privatevoidlogMeshCallSuccess(HttpRequestrequest,ClientHttpResponseresponse,StringrequestId,longduration){try{log.info("【Mesh调用成功】ID: {}, 服务: {}, 耗时: {}ms, 状态码: {}",requestId,extractServiceName(request.getURI().toString()),duration,response.getStatusCode().value());// 记录到指标io.micrometer.core.instrument.Metrics.counter("mesh.calls.success","service",extractServiceName(request.getURI().toString()),"status",String.valueOf(response.getStatusCode().value())).increment();}catch(IOExceptione){log.error("记录Mesh调用成功日志失败",e);}}privatevoidlogMeshCallError(HttpRequestrequest,IOExceptionexception,StringrequestId,longduration){log.error("【Mesh调用失败】ID: {}, 服务: {}, 耗时: {}ms, 错误: {}",requestId,extractServiceName(request.getURI().toString()),duration,exception.getMessage(),exception);// 记录到指标io.micrometer.core.instrument.Metrics.counter("mesh.calls.error","service",extractServiceName(request.getURI().toString()),"exception",exception.getClass().getSimpleName()).increment();}}/** * 生成请求ID */privatestaticStringgenerateRequestId(){returnjava.util.UUID.randomUUID().toString().replace("-","").substring(0,16);}/** * 获取当前服务名称 */privatestaticStringgetCurrentServiceName(){returnSystem.getProperty("spring.application.name","unknown-service");}/** * 从URL中提取服务名称 */privatestaticStringextractServiceName(Stringurl){// 简化的服务名称提取逻辑// 实际中可能根据服务发现机制来提取if(url.contains("://")){url=url.substring(url.indexOf("://")+3);}if(url.contains("/")){url=url.substring(0,url.indexOf("/"));}if(url.contains(":")){url=url.substring(0,url.indexOf(":"));}returnurl;}}

十四、监控与告警

14.1 Actuator 端点扩展

packagecom.example.logging.actuator;importcom.example.logging.context.LogContext;importcom.example.logging.service.LogService;importio.micrometer.core.instrument.MeterRegistry;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.actuate.endpoint.annotation.Endpoint;importorg.springframework.boot.actuate.endpoint.annotation.ReadOperation;importorg.springframework.boot.actuate.endpoint.annotation.WriteOperation;importorg.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;importorg.springframework.stereotype.Component;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;importjava.util.*;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.atomic.AtomicLong;/** * 自定义Actuator端点 * 提供日志相关的监控和管理功能 */@Slf4j@Component@WebEndpoint(id="logging")@RequiredArgsConstructorpublicclassLoggingActuatorEndpoint{privatefinalMeterRegistrymeterRegistry;privatefinalLogServicelogService;// 日志统计缓存privatefinalMap<String,LogStatistics>statisticsCache=newConcurrentHashMap<>();privatefinalAtomicLonglastResetTime=newAtomicLong(System.currentTimeMillis());privatestaticfinalDateTimeFormatterTIME_FORMATTER=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");/** * 获取日志统计信息 */@ReadOperationpublicMap<String,Object>getLoggingStats(){Map<String,Object>stats=newLinkedHashMap<>();// 基本统计stats.put("timestamp",LocalDateTime.now().format(TIME_FORMATTER));stats.put("lastReset",formatTime(lastResetTime.get()));// 线程统计stats.put("threads",getThreadStats());// 日志量统计stats.put("volume",getLogVolumeStats());// 性能统计stats.put("performance",getPerformanceStats());// 虚拟线程统计stats.put("virtualThreads",getVirtualThreadStats());// 错误统计stats.put("errors",getErrorStats());returnstats;}/** * 重置统计信息 */@WriteOperationpublicMap<String,Object>resetStats(){statisticsCache.clear();lastResetTime.set(System.currentTimeMillis());Map<String,Object>result=newHashMap<>();result.put("message","统计信息已重置");result.put("resetTime",formatTime(lastResetTime.get()));log.info("日志统计信息已重置");returnresult;}/** * 获取线程统计 */privateMap<String,Object>getThreadStats(){Map<String,Object>threadStats=newHashMap<>();ThreadGrouprootGroup=Thread.currentThread().getThreadGroup();while(rootGroup.getParent()!=null){rootGroup=rootGroup.getParent();}Thread[]threads=newThread[rootGroup.activeCount()];rootGroup.enumerate(threads,true);longvirtualThreadCount=Arrays.stream(threads).filter(Thread::isVirtual).count();longplatformThreadCount=Arrays.stream(threads).filter(t->!t.isVirtual()).count();threadStats.put("total",threads.length);threadStats.put("virtual",virtualThreadCount);threadStats.put("platform",platformThreadCount);threadStats.put("daemon",Arrays.stream(threads).filter(Thread::isDaemon).count());returnthreadStats;}/** * 获取日志量统计 */privateMap<String,Object>getLogVolumeStats(){Map<String,Object>volumeStats=newHashMap<>();// 从MeterRegistry获取指标Collection<io.micrometer.core.instrument.Counter>counters=meterRegistry.find("log.").counters();Map<String,Double>counterValues=newHashMap<>();counters.forEach(counter->{Stringname=counter.getId().getName();doublevalue=counter.count();counterValues.put(name,value);});volumeStats.put("counters",counterValues);volumeStats.put("totalLogs",counterValues.values().stream().mapToDouble(Double::doubleValue).sum());returnvolumeStats;}/** * 获取性能统计 */privateMap<String,Object>getPerformanceStats(){Map<String,Object>perfStats=newHashMap<>();// 收集计时器信息Collection<io.micrometer.core.instrument.Timer>timers=meterRegistry.find("log.execution.time").timers();List<Map<String,Object>>timerInfos=newArrayList<>();timers.forEach(timer->{Map<String,Object>timerInfo=newHashMap<>();timerInfo.put("name",timer.getId().getName());timerInfo.put("count",timer.count());timerInfo.put("mean",timer.mean(io.micrometer.core.instrument.TimeUnit.MILLISECONDS));timerInfo.put("max",timer.max(io.micrometer.core.instrument.TimeUnit.MILLISECONDS));timerInfo.put("p95",timer.percentile(0.95,io.micrometer.core.instrument.TimeUnit.MILLISECONDS));timerInfos.add(timerInfo);});perfStats.put("timers",timerInfos);returnperfStats;}/** * 获取虚拟线程统计 */privateMap<String,Object>getVirtualThreadStats(){Map<String,Object>vtStats=newHashMap<>();// 虚拟线程创建率io.micrometer.core.instrument.CountervtCounter=meterRegistry.find("log.virtual.thread.count").counter();if(vtCounter!=null){vtStats.put("created",vtCounter.count());}// 活跃虚拟线程数longactiveVirtualThreads=Thread.getAllStackTraces().keySet().stream().filter(Thread::isVirtual).count();vtStats.put("active",activeVirtualThreads);vtStats.put("carrierThreads",getCarrierThreadCount());returnvtStats;}/** * 获取载体线程数 */privateintgetCarrierThreadCount(){// 估算载体线程数returnRuntime.getRuntime().availableProcessors()*2;}/** * 获取错误统计 */privateMap<String,Object>getErrorStats(){Map<String,Object>errorStats=newHashMap<>();// 错误计数器Collection<io.micrometer.core.instrument.Counter>errorCounters=meterRegistry.find("log.*.error").counters();Map<String,Double>errors=newHashMap<>();errorCounters.forEach(counter->{Stringname=counter.getId().getName();doublevalue=counter.count();errors.put(name,value);});errorStats.put("counters",errors);errorStats.put("totalErrors",errors.values().stream().mapToDouble(Double::doubleValue).sum());returnerrorStats;}/** * 格式化时间戳 */privateStringformatTime(longtimestamp){returnLocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(timestamp),java.time.ZoneId.systemDefault()).format(TIME_FORMATTER);}/** * 日志统计数据结构 */@DataprivatestaticclassLogStatistics{privateStringmodule;privatelongtotalRequests;privatelongsuccessfulRequests;privatelongfailedRequests;privatelongslowQueries;privatedoubleavgResponseTime;privatedoublep95ResponseTime;privatedoublep99ResponseTime;privateLocalDateTimelastUpdated;publicvoidincrementTotal(){totalRequests++;lastUpdated=LocalDateTime.now();}publicvoidincrementSuccess(){successfulRequests++;lastUpdated=LocalDateTime.now();}publicvoidincrementFailure(){failedRequests++;lastUpdated=LocalDateTime.now();}publicvoidincrementSlowQuery(){slowQueries++;lastUpdated=LocalDateTime.now();}}}

十五、测试与验证

15.1 集成测试配置

packagecom.example.test;importcom.example.logging.annotation.Loggable;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.boot.test.mock.mockito.MockBean;importorg.springframework.test.context.ActiveProfiles;importreactor.core.publisher.Mono;importreactor.test.StepVerifier;importjava.util.concurrent.CompletableFuture;importjava.util.concurrent.TimeUnit;importstaticorg.assertj.core.api.Assertions.assertThat;importstaticorg.mockito.ArgumentMatchers.any;importstaticorg.mockito.Mockito.*;/** * 日志注解集成测试 */@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)@ActiveProfiles("test")classLoggingIntegrationTest{@AutowiredprivateTestServicetestService;@MockBeanprivateLoggingMetricsCollectormetricsCollector;@TestvoidtestSyncMethodLogging(){// 执行测试方法Stringresult=testService.syncMethod("test-input");// 验证结果assertThat(result).isEqualTo("SYNC-test-input");// 验证指标被记录verify(metricsCollector,times(1)).recordLogCount(eq("test-module"),eq("INFO"),eq(true));}@TestvoidtestAsyncMethodLogging()throwsException{// 执行异步方法CompletableFuture<String>future=testService.asyncMethod("async-input");Stringresult=future.get(5,TimeUnit.SECONDS);// 验证结果assertThat(result).isEqualTo("ASYNC-async-input");// 验证虚拟线程指标verify(metricsCollector,atLeastOnce()).recordVirtualThreadMetrics();}@TestvoidtestReactiveMethodLogging(){// 执行响应式方法Mono<String>mono=testService.reactiveMethod("reactive-input");StepVerifier.create(mono).expectNext("REACTIVE-reactive-input").verifyComplete();// 验证响应式指标verify(metricsCollector,atLeastOnce()).recordReactiveMetrics(anyString(),anyInt(),any());}@TestvoidtestErrorLogging(){// 验证异常日志try{testService.errorMethod();}catch(RuntimeExceptione){assertThat(e.getMessage()).isEqualTo("Test error");}// 验证错误指标verify(metricsCollector,times(1)).recordLogCount(eq("test-module"),eq("ERROR"),eq(false));}@TestvoidtestVirtualThreadMethod()throwsException{// 执行虚拟线程方法CompletableFuture<String>future=testService.virtualThreadMethod("virtual-input");Stringresult=future.get(5,TimeUnit.SECONDS);assertThat(result).isEqualTo("VIRTUAL-virtual-input");// 验证虚拟线程使用情况verify(metricsCollector,atLeastOnce()).recordVirtualThreadMetrics();}/** * 测试服务 */@ComponentstaticclassTestService{@Loggable(module="test-module",value="同步方法测试",level=Loggable.LogLevel.INFO)publicStringsyncMethod(Stringinput){return"SYNC-"+input;}@Loggable(module="test-module",value="异步方法测试",async=true,virtualThread=true)publicCompletableFuture<String>asyncMethod(Stringinput){returnCompletableFuture.supplyAsync(()->{try{Thread.sleep(100);// 模拟耗时操作}catch(InterruptedExceptione){Thread.currentThread().interrupt();}return"ASYNC-"+input;});}@Loggable(module="test-module",value="响应式方法测试",async=true)publicMono<String>reactiveMethod(Stringinput){returnMono.fromCallable(()->"REACTIVE-"+input).delayElement(java.time.Duration.ofMillis(100));}@Loggable(module="test-module",value="错误方法测试",level=Loggable.LogLevel.ERROR)publicvoiderrorMethod(){thrownewRuntimeException("Test error");}@Loggable(module="test-module",value="虚拟线程方法测试",async=true,virtualThread=true,asyncStrategy=Loggable.AsyncStrategy.VIRTUAL_THREAD)publicCompletableFuture<String>virtualThreadMethod(Stringinput){returnCompletableFuture.supplyAsync(()->{// 在虚拟线程中执行try{Thread.sleep(50);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}return"VIRTUAL-"+input;});}}}

十六、部署配置

16.1 Dockerfile (原生镜像)

# 第一阶段:构建原生镜像 FROM ghcr.io/graalvm/native-image:ol8-java21 AS builder # 安装必要的构建工具 RUN microdnf install -y gcc gcc-c++ zlib-devel # 设置工作目录 WORKDIR /app # 复制构建文件 COPY mvnw . COPY .mvn .mvn COPY pom.xml . COPY src src # 下载依赖 RUN ./mvnw dependency:go-offline -B # 构建原生镜像 RUN ./mvnw clean package -DskipTests -Pnative # 第二阶段:运行镜像 FROM oraclelinux:9-slim # 安装必要的运行时库 RUN microdnf install -y \ ca-certificates \ tzdata \ glibc-langpack-en \ && microdnf clean all # 设置时区 ENV TZ=Asia/Shanghai # 创建非root用户 RUN groupadd -r appuser && useradd -r -g appuser appuser # 设置工作目录 WORKDIR /app # 复制原生可执行文件 COPY --from=builder /app/target/*-runner /app/application COPY --from=builder /app/src/main/resources /app/resources # 设置权限 RUN chown -R appuser:appuser /app USER appuser # 暴露端口 EXPOSE 8080 EXPOSE 8081 # 监控端口 # 设置健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # 运行应用 ENTRYPOINT ["/app/application"] CMD []

16.2 docker-compose.yml

version:'3.8'services:# 日志中心服务log-center:build:context:.dockerfile:Dockerfileports:-"8080:8080"-"8081:8081"environment:-SPRING_PROFILES_ACTIVE=production-JAVA_OPTS=-XX:+UseZGC-Xmx512m-Xms512m-LOGGING_CONFIG_ENABLED=true-LOGGING_VIRTUAL_THREAD_ENABLED=true-MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics,prometheus,loggingnetworks:-logging-networkdepends_on:-elasticsearch-redisdeploy:resources:limits:memory:768Mreservations:memory:512Mreplicas:3placement:constraints:-node.role==managerhealthcheck:test:["CMD","curl","-f","http://localhost:8080/actuator/health"]interval:30stimeout:10sretries:3start_period:40s# Elasticsearchelasticsearch:image:elasticsearch:8.12.0environment:-discovery.type=single-node-xpack.security.enabled=false-ES_JAVA_OPTS=-Xms512m-Xmx512mports:-"9200:9200"volumes:-esdata:/usr/share/elasticsearch/datanetworks:-logging-networkdeploy:resources:limits:memory:1G# Redis缓存redis:image:redis:7.2-alpineports:-"6379:6379"command:redis-server--appendonly yesvolumes:-redisdata:/datanetworks:-logging-network# Prometheus监控prometheus:image:prom/prometheus:latestports:-"9090:9090"volumes:-./prometheus.yml:/etc/prometheus/prometheus.yml-prometheus-data:/prometheusnetworks:-logging-networkcommand:-'--config.file=/etc/prometheus/prometheus.yml'-'--storage.tsdb.path=/prometheus'-'--web.console.libraries=/etc/prometheus/console_libraries'-'--web.console.templates=/etc/prometheus/consoles'-'--storage.tsdb.retention.time=200h'-'--web.enable-lifecycle'# Grafana可视化grafana:image:grafana/grafana:latestports:-"3000:3000"environment:-GF_SECURITY_ADMIN_PASSWORD=adminvolumes:-grafana-data:/var/lib/grafana-./grafana/provisioning:/etc/grafana/provisioningnetworks:-logging-networkdepends_on:-prometheusnetworks:logging-network:driver:bridgevolumes:esdata:driver:localredisdata:driver:localprometheus-data:driver:localgrafana-data:driver:local

十七、最佳实践总结

17.1 使用建议

/** * 日志注解最佳实践示例 */@Slf4j@ServicepublicclassLoggingBestPracticeService{// 1. 明确的模块划分@Loggable(module="用户管理",value="创建用户账户",type=Loggable.LogType.INSERT,bizNo="#userDto.username")publicUsercreateUser(UserDTOuserDto){// 业务逻辑}// 2. 敏感信息脱敏@Loggable(module="认证授权",value="用户登录认证",logParams=false,// 不记录密码参数logResult=false// 不记录令牌结果)publicAuthTokenlogin(Stringusername,Stringpassword){// 认证逻辑}// 3. 大文件/大结果集处理@Loggable(module="文件服务",value="下载文件",logParams=true,logResult=false// 不记录文件内容)publicbyte[]downloadFile(StringfileId){// 文件下载逻辑}// 4. 性能敏感操作使用异步@Loggable(module="报表服务",value="生成年度报表",async=true,virtualThread=true,slowThreshold=30000// 30秒超时标记为慢查询)publicCompletableFuture<Report>generateAnnualReport(ReportRequestrequest){// 报表生成逻辑}// 5. 分布式追踪集成@TraceLog(spanName="payment.process",tags={"orderId=#orderId","amount=#amount"},remote=true,remoteService="payment-service")@Loggable(module="支付服务",value="处理支付订单")publicPaymentResultprocessPayment(StringorderId,BigDecimalamount){// 支付处理逻辑}// 6. 响应式方法@ReactiveLog(traceStream=true,maxTraceElements=50,timeout=15000)@Loggable(module="消息服务",value="流式处理消息")publicFlux<Message>processMessageStream(Flux<Message>messages){returnmessages.doOnNext(msg->log.debug("Processing message: {}",msg.getId())).buffer(10).flatMap(this::processBatch);}// 7. 错误处理和降级@Loggable(module="外部服务",value="调用第三方API",level=Loggable.LogLevel.WARN,stackTrace=true)publicExternalResponsecallExternalApi(ExternalRequestrequest){try{returnexternalClient.call(request);}catch(ExternalServiceExceptione){log.error("外部服务调用失败",e);returnfallbackResponse();}}// 8. 虚拟线程并发处理@Loggable(module="数据处理",value="批量数据处理",async=true,virtualThread=true,asyncStrategy=Loggable.AsyncStrategy.VIRTUAL_THREAD)publicvoidbatchProcess(List<DataItem>items){try(varscope=newStructuredTaskScope.ShutdownOnFailure()){List<Future<ProcessResult>>futures=items.stream().map(item->scope.fork(()->processItem(item))).toList();scope.join();scope.throwIfFailed();futures.forEach(future->{try{ProcessResultresult=future.resultNow();log.debug("Processed item: {}",result.getItemId());}catch(Exceptione){log.warn("Item processing failed",e);}});}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException("批量处理被中断",e);}}// 9. 审计日志@Loggable(module="审计日志",value="重要业务操作",level=Loggable.LogLevel.WARN,pushToCenter=true,group="audit")@TransactionalpublicAuditRecordauditImportantOperation(AuditOperationoperation){// 审计记录逻辑}}

17.2 性能优化配置

# application-performance.ymlspring:logging:# 生产环境推荐配置enabled:trueasync-enabled:truevirtual-thread-enabled:truereactive-enabled:truedefault-level:INFOslow-threshold:1000log-params:truelog-result:truepush-to-center:true# 模块特定配置modules:gateway-service:enabled:truelevel:INFOasync:truevirtual-thread:trueslow-threshold:500user-service:enabled:truelevel:INFOasync:truevirtual-thread:trueslow-threshold:800order-service:enabled:truelevel:INFOasync:false# 订单服务追求强一致性,使用同步日志log-params:false# 订单数据量大,不记录参数# 虚拟线程配置threads:virtual:enabled:trueexecutor:core-pool-size:10max-pool-size:100queue-capacity:1000keep-alive-seconds:60thread-name-prefix:"app-vt-"# 响应式配置webflux:max-in-memory-size:256KB# 限制内存使用# 监控配置management:metrics:export:prometheus:step:1mdistribution:percentiles-histogram:http.server.requests:truesla:http.server.requests:10ms,50ms,100ms,500ms,1s,5s# Sleuth配置sleuth:enabled:truesampler:probability:0.1# 生产环境采样率propagation:type:B3,W3C# 日志级别控制logging:level:root:INFOcom.example:DEBUGcom.example.logging:INFOorg.springframework.web:WARNreactor.netty:WARNio.netty:WARN# 异步日志配置async:enabled:truequeue-size:10000discarding-threshold:0include-caller-data:falsenever-block:true

十八、故障排查指南

18.1 常见问题解决方案

packagecom.example.logging.troubleshooting;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjava.lang.management.ManagementFactory;importjava.lang.management.ThreadMXBean;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;/** * 日志系统故障排查工具 */@Slf4j@ComponentpublicclassLoggingTroubleshooter{// 问题计数器privatefinalMap<String,AtomicInteger>issueCounters=newConcurrentHashMap<>();// 最后检测时间privatevolatilelonglastCheckTime=System.currentTimeMillis();/** * 诊断日志系统健康状况 */publicHealthDiagnosisdiagnoseHealth(){HealthDiagnosisdiagnosis=newHealthDiagnosis();diagnosis.setTimestamp(System.currentTimeMillis());// 检查线程状态diagnoseThreadIssues(diagnosis);// 检查内存状态diagnoseMemoryIssues(diagnosis);// 检查死锁diagnoseDeadlocks(diagnosis);// 检查虚拟线程泄漏diagnoseVirtualThreadLeaks(diagnosis);// 检查日志积压diagnoseLogBacklog(diagnosis);// 生成诊断报告generateDiagnosticReport(diagnosis);returndiagnosis;}/** * 诊断线程问题 */privatevoiddiagnoseThreadIssues(HealthDiagnosisdiagnosis){ThreadMXBeanthreadMXBean=ManagementFactory.getThreadMXBean();// 活跃线程数intactiveThreadCount=Thread.activeCount();diagnosis.setActiveThreads(activeThreadCount);// 虚拟线程比例longvirtualThreadCount=getAllThreads().stream().filter(Thread::isVirtual).count();diagnosis.setVirtualThreadRatio((double)virtualThreadCount/activeThreadCount);// 线程峰值diagnosis.setPeakThreadCount(threadMXBean.getPeakThreadCount());// 检查线程泄漏if(activeThreadCount>1000){diagnosis.addIssue("THREAD_LEAK","活跃线程数过多: "+activeThreadCount);incrementIssueCounter("THREAD_LEAK");}}/** * 诊断内存问题 */privatevoiddiagnoseMemoryIssues(HealthDiagnosisdiagnosis){Runtimeruntime=Runtime.getRuntime();longtotalMemory=runtime.totalMemory();longfreeMemory=runtime.freeMemory();longusedMemory=totalMemory-freeMemory;longmaxMemory=runtime.maxMemory();diagnosis.setTotalMemory(totalMemory);diagnosis.setUsedMemory(usedMemory);diagnosis.setFreeMemory(freeMemory);diagnosis.setMaxMemory(maxMemory);diagnosis.setMemoryUsage((double)usedMemory/totalMemory);// 检查内存使用率if(diagnosis.getMemoryUsage()>0.8){// 80%阈值diagnosis.addIssue("HIGH_MEMORY_USAGE","内存使用率过高: "+(diagnosis.getMemoryUsage()*100)+"%");incrementIssueCounter("HIGH_MEMORY_USAGE");}// 检查内存泄漏迹象if(usedMemory>maxMemory*0.9){// 90%阈值diagnosis.addIssue("MEMORY_LEAK_SUSPECTED","疑似内存泄漏,使用内存接近最大值");incrementIssueCounter("MEMORY_LEAK_SUSPECTED");}}/** * 诊断死锁 */privatevoiddiagnoseDeadlocks(HealthDiagnosisdiagnosis){ThreadMXBeanthreadMXBean=ManagementFactory.getThreadMXBean();long[]deadlockedThreads=threadMXBean.findDeadlockedThreads();if(deadlockedThreads!=null&&deadlockedThreads.length>0){diagnosis.addCriticalIssue("DEADLOCK_DETECTED","检测到死锁,涉及线程: "+deadlockedThreads.length);incrementIssueCounter("DEADLOCK_DETECTED");}}/** * 诊断虚拟线程泄漏 */privatevoiddiagnoseVirtualThreadLeaks(HealthDiagnosisdiagnosis){longvirtualThreadCount=getAllThreads().stream().filter(Thread::isVirtual).count();longplatformThreadCount=getAllThreads().stream().filter(t->!t.isVirtual()).count();// 虚拟线程与平台线程比例异常if(platformThreadCount>0&&virtualThreadCount/platformThreadCount>100){diagnosis.addIssue("VIRTUAL_THREAD_LEAK","虚拟线程与平台线程比例异常: "+virtualThreadCount+"/"+platformThreadCount);incrementIssueCounter("VIRTUAL_THREAD_LEAK");}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!