使用 Java 生成跟踪记录和指标

本文档介绍了如何修改 Java 应用以使用开源 OpenTelemetry 框架收集跟踪记录和指标数据,以及如何将结构化 JSON 日志写入标准输出。本文档还介绍了您可以安装和运行的 Java Spring Boot 示例应用。该应用已配置为生成指标、跟踪记录和日志。无论您是否使用 Spring Boot 框架,步骤都是相同的。

如需详细了解插桩,请参阅以下文档:

手动和自动插桩简介

本文档中所述的插桩依赖 OpenTelemetry 自动插桩将遥测数据发送到您的 Google Cloud 项目。对于 Java,自动插桩是指将字节码动态注入库和框架以捕获遥测数据的做法。自动插桩可以收集呼入和呼出 HTTP 调用等方面的遥测数据。如需了解详情,请参阅 Java 的自动插桩

OpenTelemetry 还提供了一个 API,用于向您自己的代码添加自定义插桩。OpenTelemetry 将其称为手动插桩。本文档未介绍手动插桩。如需查看有关该主题的示例和信息,请参阅手动插桩

准备工作

Enable the Cloud Logging, Cloud Monitoring, and Cloud Trace APIs.

Enable the APIs

对应用进行插桩处理以收集跟踪记录、指标和日志

如需对应用进行插桩处理以收集跟踪记录和指标数据,并将结构化 JSON 写入标准输出,请执行以下步骤,如本文档后续部分所述:

  1. 将您的应用配置为使用 OpenTelemetry Java 代理
  2. 配置 OpenTelemetry
  3. 配置结构化日志记录
  4. 写入结构化日志

将应用配置为使用 OpenTelemetry Java 代理

如需将应用配置为使用 OpenTelemetry 写入结构化日志以及收集指标和跟踪记录数据,请更新应用的调用以使用 OpenTelemetry Java 代理。这种应用插桩方法称为自动插桩,因为它不需要修改应用代码。

以下代码示例演示了一个 Dockerfile,用于下载 OpenTelemetry Java 代理 JAR 文件并更新命令行调用以传递 -javaagent 标志。

如需查看完整示例,请点击 更多,然后选择在 GitHub 上查看

RUN wget -O /opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.31.0/opentelemetry-javaagent.jar
CMD sh -c "java -javaagent:/opentelemetry-javaagent.jar -cp app:app/lib/* com.example.demo.DemoApplication \
	2>&1 | tee /var/log/app.log"

或者,您还可以在 JAVA_TOOL_OPTIONS 环境变量中设置 -javaagent 标志:

export JAVA_TOOL_OPTIONS="-javaagent:PATH/TO/opentelemetry-javaagent.jar"

配置 OpenTelemetry

OpenTelemetry Java 代理的默认配置使用 OTLP 协议导出跟踪记录和指标。它还会将 OpenTelemetry 配置为使用 W3C 跟踪记录上下文格式来传播跟踪记录上下文。此配置可确保 span 在跟踪记录中具有正确的父子关系。

如需了解详情和配置选项,请参阅 OpenTelemetry Java 自动插桩

配置结构化日志记录

如需在写入标准输出的 JSON 格式日志中包含跟踪记录信息,请将应用配置为输出 JSON 格式的结构化日志。我们建议您使用 Log4j2 作为日志记录实现。以下代码示例展示了一个配置为使用 JSON 模板布局输出 JSON 结构化日志的 log4j2.xml 文件:

<!-- Format JSON logs for the Cloud Logging agent
https://cloud.google.com/logging/docs/structured-logging#special-payload-fields -->

<!-- Log4j2's JsonTemplateLayout includes a template for Cloud Logging's special JSON fields
https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-templates -->
<JsonTemplateLayout eventTemplateUri="classpath:GcpLayout.json">
  <!-- Extend the included GcpLayout to include the trace and span IDs from Mapped
  Diagnostic Context (MDC) so that Cloud Logging can correlate Logs and Spans

  Since log4j2 2.24.0, GcpLayout.json already includes trace context logging from MDC and
  the below additional fields are no longer needed -->
  <EventTemplateAdditionalField
    key="logging.googleapis.com/trace"
    format="JSON"
    value='{"$resolver": "mdc", "key": "trace_id"}'
  />
  <EventTemplateAdditionalField
    key="logging.googleapis.com/spanId"
    format="JSON"
    value='{"$resolver": "mdc", "key": "span_id"}'
  />
  <EventTemplateAdditionalField
    key="logging.googleapis.com/trace_sampled"
    format="JSON"
    value="true"
  />
</JsonTemplateLayout>

上述配置会从 SLF4J 的映射诊断上下文中提取有关活跃 span 的信息,并将该信息作为属性添加到日志中。然后,您可以使用这些属性将日志与跟踪记录相关联:

  • logging.googleapis.com/trace:与日志条目关联的跟踪记录的资源名称。
  • logging.googleapis.com/spanId:与日志条目关联的跟踪记录的 span ID。
  • logging.googleapis.com/trace_sampled:此字段的值必须是 truefalse

如需详细了解这些字段,请参阅 LogEntry 结构。

写入结构化日志

如需写入链接到跟踪记录的结构化日志,请使用 SLF4J 日志记录 API。例如,以下语句展示了如何调用 Logger.info() 方法:

logger.info("handle /multi request with subRequests={}", subRequests);

OpenTelemetry Java 代理使用 OpenTelemetry 上下文中当前活跃 span 的 span 上下文自动填充 SLF4J 的映射诊断上下文。然后,映射的诊断上下文会包含在 JSON 日志中,如配置结构化日志记录中所述。

运行配置为收集遥测数据的示例应用

示例应用使用不受制于供应商的格式,包括 JSON(用于日志)和 OTLP(用于指标和跟踪记录)以及 Spring Boot 框架。为了将遥测数据路由到 Google Cloud,此示例使用配置了 Google 导出器的 OpenTelemetry Collector。该应用有两个端点:

  • /multi 端点由 handleMulti 函数处理。该应用中的负载生成器会向 /multi 端点发出请求。此端点收到请求时,它会向本地服务器上的 /single 端点发送 3 到 7 个请求。

    /**
     * handleMulti handles an http request by making 3-7 http requests to the /single endpoint.
     *
     * <p>OpenTelemetry instrumentation requires no changes here. It will automatically generate a
     * span for the controller body.
     */
    @GetMapping("/multi")
    public Mono<String> handleMulti() throws Exception {
      int subRequests = ThreadLocalRandom.current().nextInt(3, 8);
    
      // Write a structured log with the request context, which allows the log to
      // be linked with the trace for this request.
      logger.info("handle /multi request with subRequests={}", subRequests);
    
      // Make 3-7 http requests to the /single endpoint.
      return Flux.range(0, subRequests)
          .concatMap(
              i -> client.get().uri("http://localhost:8080/single").retrieve().bodyToMono(Void.class))
          .then(Mono.just("ok"));
    }
  • /single 端点由 handleSingle 函数处理。当此端点收到请求时,它会休眠一小段延迟时间,然后以字符串进行响应。

    /**
     * handleSingle handles an http request by sleeping for 100-200 ms. It writes the number of
     * milliseconds slept as its response.
     *
     * <p>OpenTelemetry instrumentation requires no changes here. It will automatically generate a
     * span for the controller body.
     */
    @GetMapping("/single")
    public String handleSingle() throws InterruptedException {
      int sleepMillis = ThreadLocalRandom.current().nextInt(100, 200);
      logger.info("Going to sleep for {}", sleepMillis);
      Thread.sleep(sleepMillis);
      logger.info("Finishing the request");
      return String.format("slept %s\n", sleepMillis);
    }

下载并部署应用

如需运行示例,请执行以下操作:

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. 克隆代码库:

    git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-java
    
  3. 转到示例目录:

    cd opentelemetry-operations-java/examples/instrumentation-quickstart
    
  4. 构建并运行示例:

    docker compose up --abort-on-container-exit
    

    如果您未在 Cloud Shell 上运行,请使用指向凭据文件的 GOOGLE_APPLICATION_CREDENTIALS 环境变量运行应用。应用默认凭据提供了一个凭据文件 ($HOME/.config/gcloud/application_default_credentials.json)。

    # Set environment variables
    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/application_default_credentials.json"
    export USERID="$(id -u)"
    
    # Run
    docker compose -f docker-compose.yaml -f docker-compose.creds.yaml up --abort-on-container-exit
    

查看指标

示例应用中的 OpenTelemetry 插桩生成 Prometheus 指标,您可以使用 Metrics Explorer 查看这些指标:

  • Prometheus/http_server_duration_milliseconds/histogram 会记录服务器请求的持续时间,并将结果存储在直方图中。

  • Prometheus/http_client_duration_milliseconds/histogram 会记录客户端请求的持续时间,并将结果存储在直方图中。

如需查看示例应用生成的指标,请执行以下操作:
  1. 在 Google Cloud 控制台中,转到 Metrics Explorer 页面:

    进入 Metrics Explorer

    如果您使用搜索栏查找此页面,请选择子标题为监控的结果。

  2. 指标元素中,展开选择指标菜单,在过滤栏中输入 http_server,然后使用子菜单选择一个特定资源类型和指标:
    1. 活跃资源菜单中,选择 Prometheus 目标
    2. 活跃指标类别菜单中,选择 HTTP
    3. 活跃指标菜单中,选择指标。
    4. 点击应用
  3. 配置数据的查看方式。

    如果指标的测量结果是累积的,则 Metrics Explorer 会自动按校准时间段对测量数据进行归一化,从而使图表显示速率。如需了解详情,请参阅种类、类型和转换

    测量整数或双精度值时(例如使用两个 counter 指标),Metrics Explorer 会自动对所有时序求和。如需查看 /multi/single HTTP 路由的数据,请将聚合条目的第一个菜单设置为

    如需详细了解如何配置图表,请参阅使用 Metrics Explorer 时选择指标

查看跟踪记录

如需查看跟踪记录数据,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到 Trace 探索器页面:

    转到 Trace 探索器

    您也可以使用搜索栏查找此页面。

  2. 在散点图中,选择 URI 为 /multi 的跟踪记录。
  3. 跟踪记录详情面板的甘特图中,选择标记为 /multi 的 span。

    此时会打开一个面板,其中显示 HTTP 请求的相关信息。这些详细信息包括方法、状态代码、字节数以及调用方的用户代理。

  4. 如需查看与此跟踪记录关联的日志,请选择日志和事件标签页。

    该标签页会显示各个日志。如需查看日志条目的详细信息,请展开日志条目。您还可以点击查看日志,并使用 Logs Explorer 查看日志。

如需详细了解如何使用 Cloud Trace 探索器,请参阅查找和探索跟踪记录

查看日志

在 Logs Explorer 中,您可以检查日志,还可以查看关联的跟踪记录(如果存在)。

  1. 在 Google Cloud 控制台中,转到 Logs Explorer 页面。

    前往 Logs Explorer

    如果您使用搜索栏查找此页面,请选择子标题为 Logging 的结果。

  2. 找到具有 handle /multi request 说明的日志。

    如需查看日志的详细信息,请展开日志条目。

  3. 点击包含“处理/多请求”消息的日志条目中的 跟踪记录,然后选择查看跟踪记录详情

    跟踪记录详情面板随即会打开并显示所选跟踪记录。

如需详细了解如何使用 Logs Explorer,请参阅使用 Logs Explorer 查看日志

后续步骤