Python 插桩示例

本文档介绍了如何修改 Python 应用以使用开源 OpenTelemetry 框架收集跟踪记录和指标数据,以及如何将结构化 JSON 日志写入标准输出。本文档还介绍了您可以安装和运行的示例 Python 应用。该应用使用 Flask Web 框架,并配置为生成指标、跟踪记录和日志。

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

手动和零代码插桩简介

对于此语言,OpenTelemetry 将零代码插桩定义为在不更改代码的情况下从库和框架中收集遥测数据的做法。不过,您必须安装模块并设置环境变量。

本文档不介绍零代码插桩。如需了解该主题,请参阅 Python 零代码插桩

如需了解一般信息,请参阅适用于 Python 的 OpenTelemetry 插桩

准备工作

  1. Install the Google Cloud CLI.

  2. 配置 gcloud CLI 以使用您的联合身份。

    如需了解详情,请参阅使用联合身份登录 gcloud CLI

  3. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  4. Create or select a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.
    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Verify that billing is enabled for your Google Cloud project.

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

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    gcloud services enable logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com
  7. 如果您在 Cloud Shell、 Google Cloud资源或本地开发环境中运行示例,则只需具备本部分中列出的权限即可。对于生产应用,通常由服务账号提供用于写入日志、指标和跟踪记录数据的凭据。

    如需获得让示例应用写入日志、指标和跟踪记录数据所需的权限,请让管理员为您授予项目的以下 IAM 角色:

    如需获得查看日志、指标和跟踪记录数据所需的权限,请让管理员为您授予项目的以下 IAM 角色:

    如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

    您也可以通过自定义角色或其他预定义角色来获取所需的权限。

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

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

    1. 配置 OpenTelemetry
    2. 配置结构化日志记录

    配置 OpenTelemetry

    此示例应用配置为使用 OpenTelemetry Python SDK 通过 OTLP 协议导出跟踪记录和指标。默认情况下,OpenTelemetry Python SDK 使用 W3C 跟踪记录上下文格式来传播跟踪记录上下文,这可确保 span 在跟踪记录中具有正确的父子关系。

    以下代码示例展示了用于设置 OpenTelemetry 的 Python 模块:如需查看完整示例,请点击 更多,然后选择在 GitHub 上查看

    def setup_opentelemetry() -> None:
        resource = Resource.create(
            attributes={
                # Use the PID as the service.instance.id to avoid duplicate timeseries
                # from different Gunicorn worker processes.
                SERVICE_INSTANCE_ID: f"worker-{os.getpid()}",
            }
        )
        # Set up OpenTelemetry Python SDK
        tracer_provider = TracerProvider(resource=resource)
        tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
        trace.set_tracer_provider(tracer_provider)
    
        logger_provider = LoggerProvider(resource=resource)
        logger_provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
        logs.set_logger_provider(logger_provider)
    
        reader = PeriodicExportingMetricReader(OTLPMetricExporter())
        meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
        metrics.set_meter_provider(meter_provider)
    
    

    Flask 应用依靠 Gunicorn 来按照 Flask 的《部署到生产环境》指南中的建议处理 HTTP 请求。Gunicorn 会启动应用的多个副本,这些副本在独立的工作器进程中运行,从而可提高吞吐量。为确保 worker 进程的指标不会相互冲突,我们建议每个工作器进程为 service.instance.id 资源属性设置唯一的值。完成此操作的一种方法是将进程 ID 添加到 service.instance.id 中。如需了解更多信息,请参阅时序冲突

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

    配置结构化日志记录

    如需写入与跟踪记录相关联的结构化日志,请将应用配置为将 JSON 格式日志输出到标准输出,并附带包含跟踪记录信息的键。以下代码示例说明如何配置标准 logging 库以使用 python-json-logger 库输出 JSON 结构化日志,以及如何使用 opentelemetry-instrumentation-logging 软件包包含跟踪信息。

    class JsonFormatter(jsonlogger.JsonFormatter):
        def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None):
            # Format the timestamp as RFC 3339 with microsecond precision
            isoformat = datetime.fromtimestamp(record.created).isoformat()
            return f"{isoformat}Z"
    
    
    def setup_structured_logging() -> None:
        LoggingInstrumentor().instrument()
    
        log_handler = logging.StreamHandler()
        formatter = JsonFormatter(
            "%(asctime)s %(levelname)s %(message)s %(otelTraceID)s %(otelSpanID)s %(otelTraceSampled)s",
            rename_fields={
                "levelname": "severity",
                "asctime": "timestamp",
                "otelTraceID": "logging.googleapis.com/trace",
                "otelSpanID": "logging.googleapis.com/spanId",
                "otelTraceSampled": "logging.googleapis.com/trace_sampled",
            },
        )
        log_handler.setFormatter(formatter)
        logging.basicConfig(
            level=logging.INFO,
            handlers=[log_handler],
        )
    
    

    前面的配置会从日志消息中提取活跃 span 的相关信息,然后将该信息作为属性添加到 JSON 结构化日志中。然后,您可以使用这些属性将日志与跟踪记录相关联:

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

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

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

    示例应用使用不受制于供应商的格式,包括 JSON(用于日志)和 OTLP(用于指标和跟踪记录)。使用配置了 Google 导出器的 OpenTelemetry Collector 将来自应用的遥测数据路由到 Google Cloud 。它使用 Flask 来处理 HTTP 请求,使用请求库来发出 HTTP 请求。为了为 HTTP 客户端和服务器生成指标和跟踪记录,示例应用安装了 opentelemetry-instrumentation-flaskopentelemetry-instrumentation-requests 插桩库:

    logger = logging.getLogger(__name__)
    
    # Initialize OpenTelemetry Python SDK and structured logging
    setup_opentelemetry()
    setup_structured_logging()
    
    app = Flask(__name__)
    
    # Add instrumentation
    FlaskInstrumentor().instrument_app(app)
    RequestsInstrumentor().instrument()

    该应用有两个端点:

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

      @app.route("/multi")
      def multi():
          """Handle an http request by making 3-7 http requests to the /single endpoint."""
          sub_requests = randint(3, 7)
          logger.info("handle /multi request", extra={"subRequests": sub_requests})
          for _ in range(sub_requests):
              requests.get(url_for("single", _external=True))
          return "ok"
      
      
    • /single 端点由 single 函数处理。当此端点收到请求时,它会休眠一小段延迟时间,然后以字符串进行响应。

      @app.route("/single")
      def single():
          """Handle an http request by sleeping for 100-200 ms, and write the number of seconds slept as the response."""
          duration = uniform(0.1, 0.2)
          logger.info("handle /single request", extra={"duration": duration})
          time.sleep(duration)
          return f"slept {duration} seconds"
      
      

    下载并部署应用

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

    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-python
      
    3. 转到示例目录:

      cd opentelemetry-operations-python/samples/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
      
    5. 查看指标

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

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

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

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

        进入 Metrics Explorer

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

      2. 在 Google Cloud 控制台的工具栏中,选择您的 Google Cloud 项目。对于 App Hub 配置,请选择 App Hub 宿主项目或已启用应用的文件夹的管理项目。
      3. 指标元素中,展开选择指标菜单,在过滤条件栏中输入 http_server,然后使用子菜单选择一个特定资源类型和指标:
        1. 活跃资源菜单中,选择 Prometheus 目标
        2. 活跃指标类别菜单中,选择 HTTP
        3. 活跃指标菜单中,选择指标。
        4. 点击应用
      4. 如需添加用于从查询结果中移除时序的过滤条件,请使用过滤条件元素

      5. 配置数据的查看方式。

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

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

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

      查看跟踪记录

      跟踪记录数据可能需要几分钟的时间才可使用。例如,当您的项目收到跟踪记录数据时,Google Cloud Observability 可能需要创建数据库来存储这些数据。创建数据库可能需要几分钟时间,在此期间,您无法查看任何跟踪记录数据。

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

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

        转到 Trace 探索器

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

      2. 在此页面的表部分,选择 span 名称为 /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. 点击包含“处理/多请求”消息的日志条目中的 跟踪记录,然后选择查看跟踪记录详情

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

        您的日志数据可能会比跟踪记录数据早几分钟可用。如果您在通过按 ID 搜索跟踪记录或通过执行此任务中的步骤来查看跟踪记录数据时遇到错误,请等待一两分钟,然后重试相应操作。

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

      后续步骤