如何在GAE标准环境(Java)中传递trace id聚合请求与应用日志
在GAE Java标准环境中聚合请求日志与应用日志
实现原理
Google Cloud日志探索器通过trace字段关联请求日志和应用日志,需要将projects/PROJECT_ID/traces/TRACE_ID格式的标识注入到java.util.logging.Logger输出的日志元数据中。可以通过自定义日志格式化器+请求上下文注入trace ID的方式实现。
具体步骤
1. 工具类:处理Trace ID格式
从请求头读取Trace ID并转换为要求的格式:
import jakarta.servlet.http.HttpServletRequest; public class TraceUtils { private static final String TRACE_HEADER = "X-Cloud-Trace-Context"; // 从GAE环境变量自动获取项目ID private static final String GCP_PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); public static String getFormattedTraceId(HttpServletRequest request) { String traceHeader = request.getHeader(TRACE_HEADER); if (traceHeader == null || traceHeader.isBlank()) { return null; } // 拆分header值,提取核心trace ID部分 String rawTraceId = traceHeader.split("/")[0]; return String.format("projects/%s/traces/%s", GCP_PROJECT_ID, rawTraceId); } }
2. 自定义日志格式化器
扩展java.util.logging.Formatter,将trace ID作为元数据写入日志:
import java.util.logging.Formatter; import java.util.logging.LogRecord; public class GcpTraceLogFormatter extends Formatter { @Override public String format(LogRecord record) { // 从日志上下文获取trace ID String traceId = (String) record.getLogger().getContext().get("traceId"); // 输出JSON格式日志,便于Cloud Logging识别元数据 return String.format( "{\"message\":\"%s\",\"severity\":\"%s\",\"trace\":\"%s\"}", formatMessage(record), record.getLevel().getName(), traceId != null ? traceId : "" ); } }
3. 注入Trace ID到请求上下文
通过Spring过滤器拦截所有请求,将格式化后的trace ID存入日志上下文:
import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.logging.ConsoleHandler; import java.util.logging.Logger; public class TraceIdInjectionFilter implements Filter { static { // 给全局日志处理器配置自定义格式化器 Logger rootLogger = Logger.getLogger(""); for (Handler handler : rootLogger.getHandlers()) { if (handler instanceof ConsoleHandler) { handler.setFormatter(new GcpTraceLogFormatter()); } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) request; String formattedTraceId = TraceUtils.getFormattedTraceId(httpReq); if (formattedTraceId != null) { // 将trace ID存入日志上下文 Logger.getGlobal().getContext().put("traceId", formattedTraceId); } try { chain.doFilter(request, response); } finally { // 清理上下文,避免内存泄漏 Logger.getGlobal().getContext().remove("traceId"); } } }
注册过滤器到Spring Boot:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class WebConfig { @Bean public FilterRegistrationBean<TraceIdInjectionFilter> traceIdFilter() { FilterRegistrationBean<TraceIdInjectionFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new TraceIdInjectionFilter()); registrationBean.addUrlPatterns("/*"); return registrationBean; } }
4. 业务日志使用
保持原有日志代码不变,日志会自动带上trace ID:
import java.util.logging.Logger; public class MyLogClass { private static final Logger logger = Logger.getLogger(MyLogClass.class.getName()); public void doBusiness() { logger.info("业务操作执行"); } }
验证
部署后在Cloud控制台日志探索器中,可通过trace ID关联查看请求日志和应用日志的完整链路,实现聚合展示。
内容的提问来源于stack exchange,提问作者Micro
相关产品推荐
相关产品推荐

