对 Google Cloud Python 客户端库进行插桩

本指南演示了如何在 Google Cloud Python 客户端库中配置和使用 OpenTelemetry 分布式跟踪。

概览

Google Cloud Python 客户端库提供内置的 OpenTelemetry 分布式跟踪。此插桩可让您深入了解客户端操作、RPC 调用、重试行为、网络延迟和服务器响应,而无需手动进行猴子补丁。

主要特征:

  • 后端无关:发出标准 OpenTelemetry span。您可以将跟踪记录直接导出到 Cloud Trace、本地 OpenTelemetry 收集器或第三方可观测性平台。
  • 选择启用:分布式跟踪默认处于停用状态,以避免不需要遥测的应用产生运行时开销。
  • 上下文传播:Span 会使用 Python 的内置 contextvars 自动绑定到活跃的 OpenTelemetry 跟踪记录上下文,从而将客户端调用与传入的 HTTP/gRPC 请求相关联。

跟踪的工作原理(Span 层次结构)

启用后, Google Cloud Python 客户端库会发出一个分层的两级 span 架构,该架构可将面向用户的方法执行与底层网络尝试明确区分开来:

[Logical Client Method Span] (Parent: covers full method duration, retries, and backoff)
 │
 ├── [Network Attempt Span #1] (Child: First wire call; e.g. transient 503 UNAVAILABLE)
 └── [Network Attempt Span #2] (Child: Retry wire call; succeeds 200 OK)

1. 逻辑客户端方法 span(父级)

  • Scope:封装从调用到完成的整个客户端库方法调用生命周期。
  • 时长:涵盖所有重试尝试、指数退避算法暂停和超时政策。
  • 身份:以完全限定的 RPC 方法命名(例如 google.cloud.secretmanager.v1.SecretManagerService/GetSecretVersion)。
  • 属性:捕获逻辑 RPC 元数据、目标服务和最终的高级结果。

2. 网络尝试 span(子级)

  • 范围:作为逻辑方法 span 的子 span 创建,表示单个 HTTP/REST 或 gRPC 物理网络尝试。
  • 生命周期:每次网络尝试都会生成一个新的 span。如果操作在第一次尝试时成功,则系统会记录一个子 span。如果操作遇到暂时性错误并重试,则每次尝试都会记录为独立的子 span,并具有自己的时间和状态。
  • 属性:捕获底层网络状态代码、目标服务器地址和传输详情。

前提条件和安装

Google Cloud Python 客户端库跟踪依赖于标准 OpenTelemetry Python 软件包。

1. 安装软件包

  • 客户端库和 gRPC 传输插桩:

    • opentelemetry-api 会随 google-api-core 自动安装,以支持逻辑方法 span 和 HTTP/REST (rest) 传输 span。
    • 如需同时记录低级 gRPC 传输 span,请安装客户端库以及 google-api-core[tracing](会安装 opentelemetry-instrumentation-grpc):
    pip install google-cloud-secret-manager "google-api-core[tracing]"
    
  • OpenTelemetry SDK(适用于独立应用和代码示例):

    • 如果您的环境已提供 TracerProvider,则客户端库不需要 opentelemetry-sdk。
    • 不过,如需在您自己的代码(包括本页面上的示例)中配置 TracerProvider 并导出 span,请安装 opentelemetry-sdk:
    pip install opentelemetry-sdk
    

如需了解如何在生产环境中配置 OpenTelemetry 导出器以将 span 发送到 Cloud Trace 或 OpenTelemetry 收集器,请参阅使用 OpenTelemetry 设置 Python 和选择插桩方法。

启用跟踪

您可以使用环境变量在全局范围内启用分布式跟踪,也可以通过编程方式在每个客户端上启用分布式跟踪。使用 ClientOptions 传递的每个客户端配置优先于环境变量设置。

选项 1:全局配置(环境变量)

如需在环境中为所有受支持的 Google Cloud Python 客户端库启用跟踪功能,请在启动应用之前设置以下环境变量:

export GOOGLE_SDK_PYTHON_TRACING_ENABLED=true

设置为 true 时,客户端库会自动检测全局注册的 OpenTelemetry TracerProvider,并为所有客户端调用发出 span。

选项 2:按客户端配置(代码)

对于精细控制或多租户应用,请通过使用 ClientOptions 传递 tracer_provider 来配置特定客户端实例:

from google.api_core.client_options import ClientOptions
from google.cloud import secretmanager

options = ClientOptions(tracer_provider=my_tracer_provider)
client = secretmanager.SecretManagerServiceClient(client_options=options)

代码示例

客户端库可以使用应用默认凭证轻松进行 Google API 身份验证,并向这些 API 发送请求。借助应用默认凭证,您可以在本地测试应用并部署它,无需更改底层代码。如需了解详情,请参阅使用客户端库时进行身份验证。

1. 快速入门示例 (SecretManagerServiceClient)

以下自包含示例演示了如何配置标准 OpenTelemetry、使用环境变量启用客户端库跟踪,以及使用 ConsoleSpanExporter 将发出的 span 直接以 JSON 格式输出到终端。

import os

from google.cloud import secretmanager
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor


def get_secret_version_with_tracing(
    project_id: str,
    secret_id: str,
    version_id: str = "latest",
) -> secretmanager.SecretVersion:
    """Get a secret version in Secret Manager with OpenTelemetry tracing enabled.

    Args:
        project_id: Google Cloud project ID.
        secret_id: ID of the secret.
        version_id: ID of the secret version (defaults to "latest").

    Returns:
        The SecretVersion metadata object.
    """
    # NOTE: In standalone scripts or CLI tools, initialize and shut down the
    # TracerProvider within the script workflow. In long-running production
    # applications (e.g., web servers), configure the TracerProvider once at
    # application startup.
    #
    # Initialize the OpenTelemetry TracerProvider with a Console exporter
    provider = TracerProvider()
    provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
    trace.set_tracer_provider(provider)

    # Enable Google Cloud client library tracing
    os.environ["GOOGLE_SDK_PYTHON_TRACING_ENABLED"] = "true"

    # Instantiate the client (automatically discovers the active TracerProvider)
    client = secretmanager.SecretManagerServiceClient()

    # Construct the resource name and invoke the RPC
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"

    try:
        print("--- OpenTelemetry Trace Spans (JSON) ---")
        response = client.get_secret_version(request={"name": name})
        state_name = response.state.name
        print(
            f"\nAPI Result: Retrieved secret version {response.name} (state: {state_name})"
        )
        return response
    finally:
        # Flush buffered spans before program completion
        provider.shutdown()

2. 程序化配置 (ClientOptions)

如果您不想使用环境变量,或者需要将不同的 TracerProvider 实例传递给不同的客户端,请通过使用 ClientOptions 传递 tracer_provider,以编程方式配置客户端:

from google.api_core.client_options import ClientOptions
from google.cloud import secretmanager
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor


def get_secret_version_with_client_options(
    project_id: str,
    secret_id: str,
    version_id: str = "latest",
) -> secretmanager.SecretVersion:
    """Get a secret version configuring tracing programmatically via ClientOptions.

    Args:
        project_id: Google Cloud project ID.
        secret_id: ID of the secret.
        version_id: ID of the secret version (defaults to "latest").

    Returns:
        The SecretVersion metadata object.
    """
    # Initialize an independent TracerProvider (no global registration required)
    provider = TracerProvider()
    provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))

    # Pass the TracerProvider directly via ClientOptions
    # Tracing is activated specifically for this client instance, even without
    # setting the GOOGLE_SDK_PYTHON_TRACING_ENABLED environment variable.
    options = ClientOptions(tracer_provider=provider)
    client = secretmanager.SecretManagerServiceClient(client_options=options)

    # Construct resource name and invoke RPC
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"

    try:
        print("--- OpenTelemetry Trace Spans (JSON) ---")
        response = client.get_secret_version(request={"name": name})
        state_name = response.state.name
        print(
            f"\nAPI Result: Retrieved secret version {response.name} (state: {state_name})"
        )
        return response
    finally:
        # Flush buffered spans before program completion
        provider.shutdown()

3. 异步客户端示例 (SecretManagerServiceAsyncClient)

以下示例使用 SecretManagerServiceAsyncClient 定义了异步协程 (async def get_secret_version_with_tracing_async)。由于此客户端是异步的,因此请确保在活跃的 asyncio 事件循环中执行它。

import asyncio
import os

from google.cloud import secretmanager
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor


async def get_secret_version_with_tracing_async(
    project_id: str,
    secret_id: str,
    version_id: str = "latest",
) -> secretmanager.SecretVersion:
    """Get a secret version using SecretManagerServiceAsyncClient with OpenTelemetry tracing.

    Args:
        project_id: Google Cloud project ID.
        secret_id: ID of the secret.
        version_id: ID of the secret version (defaults to "latest").

    Returns:
        The SecretVersion metadata object.
    """
    # NOTE: In standalone scripts or CLI tools, initialize and shut down the
    # TracerProvider within the script workflow. In long-running production
    # applications (e.g., web servers), configure the TracerProvider once at
    # application startup.
    #
    # Initialize the OpenTelemetry TracerProvider with a Console exporter
    provider = TracerProvider()
    provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
    trace.set_tracer_provider(provider)

    # Enable Google Cloud client library tracing
    os.environ["GOOGLE_SDK_PYTHON_TRACING_ENABLED"] = "true"

    # Instantiate the async client
    client = secretmanager.SecretManagerServiceAsyncClient()

    # Construct resource name and invoke the async RPC
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"

    try:
        print("--- OpenTelemetry Trace Spans (JSON) ---")
        response = await client.get_secret_version(request={"name": name})
        state_name = response.state.name
        print(
            f"\nAPI Result: Retrieved secret version {response.name} (state: {state_name})"
        )
        return response
    finally:
        # Flush buffered spans before program completion
        provider.shutdown()

语义属性参考

Google Cloud Python 客户端库发出的 span 遵循标准的 RPC span 的 OpenTelemetry 语义约定,记录传输协议、RPC 方法标识、目标服务器地址、响应状态代码和错误详细信息的标准元数据。您可以使用这些属性在可观测性或 Cloud Trace 信息中心内过滤、汇总和创建提醒政策。

安全和敏感数据

Google Cloud Python 客户端库插桩遵循严格的安全和隐私标准:

  • 载荷保护:客户端库绝不在跟踪 span 上记录请求或响应载荷数据(例如文件内容、密钥值或数据库查询结果)。
  • 标识符可见性:资源名称(例如项目 ID、数据集 ID 和 Secret 名称)会显示在方法参数和网址端点属性中。如果资源标识符包含敏感业务信息,请确保您的跟踪记录存储符合内部合规性政策。
  • 第三方导出器:将跟踪记录导出到第三方可观测性服务或 SaaS 供应商时,请查看贵组织的数据保护协议,并根据需要配置导出器属性过滤。