You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:52:41