使用OpenTelemeter跟踪部署在Cloud Run上的Python GRPC服务器

问题描述

我在Cloud Run上运行一个Python GRPC服务器,并尝试添加工具来捕获跟踪信息。我目前有基本设置,但在使用OpenTelemeterdocs中所示的传播时遇到问题。

入站请求具有x-cloud-trace-context标头,我可以在我一直使用的GRPC方法中记录标头值,但是由OpenTelemeter库创建的跟踪始终具有与请求标头中的跟踪ID不同的ID。

这是我创建的简单tracing.py模块,用于提供对当前Tracer实例的配置和访问:

"""Utility functions for tracing."""

import opentelemetry.exporter.cloud_trace as cloud_trace
import opentelemetry.propagate as propagate
import opentelemetry.propagators.cloud_trace_propagator as cloud_trace_propagator
import opentelemetry.trace as trace
from opentelemetry.sdk import trace as sdk_trace
from opentelemetry.sdk.trace import export

import app_instance


def get_tracer() -> trace.Tracer:
    """Function that provides an object for tracing.

    Returns:
        trace.Tracer instance.
    """
    return trace.get_tracer(__name__)


def configure_tracing() -> None:
    trace.set_tracer_provider(sdk_trace.TracerProvider())
    if app_instance.IS_LOCAL:
        print("Configuring local tracing.")
        span_exporter: export.SpanExporter = export.ConsoleSpanExporter()
    else:
        print(f"Configuring cloud tracing in environment {app_instance.ENVIRONMENT}.")
        span_exporter = cloud_trace.CloudTraceSpanExporter()
        propagate.set_global_textmap(cloud_trace_propagator.CloudTraceFormatPropagator())

    trace.get_tracer_provider().add_span_processor(export.SimpleSpanProcessor(span_exporter))

configure_tracing函数由容器启动时运行的入口点脚本调用,因此它在处理任何请求之前执行。在Google Cloud中运行时,CloudTraceFormatPropagator应该是确保跟踪传播所必需的,但它似乎不适合我。

这是我用来实现的简单GRPC方法:

import grpc
from opentelemetry import trace
import stripe

from common import cloud_logging, datastore_utils, proto_helpers, tracing
from services.payment_service import payment_service_pb2
from third_party import stripe_client

def GetStripeInvoice(
    self, request: payment_service_pb2.GetStripeInvoiceRequest, context: grpc.ServicerContext
) -> payment_service_pb2.StripeInvoiceResponse:

    tracer: trace.Tracer = tracing.get_tracer()

    with tracer.start_as_current_span('GetStripeInvoice'):
        print(f"trace ID from header: {dict(context.invocation_metadata()).get('x-cloud-trace-context')}")
        cloud_logging.info(f"Getting Stripe invoice.")
        order = datastore_utils.get_pb_with_pb_key(request.order)

        try:
            invoice: stripe.Invoice = stripe_client.get_invoice(
                invoice_id=order.stripe_invoice_id
            )
            cloud_logging.info(f"Retrieved Stripe invoice. Amount due: {invoice['amount_due']}")
        except stripe.error.StripeError as e:
            cloud_logging.error(
                f"Failed to retrieve invoice: {e}"
            )
            context.abort(code=grpc.StatusCode.INTERNAL, details=str(e))

        return payment_service_pb2.StripeInvoiceResponse(
            invoice=proto_helpers.create_struct(invoice)
        )

我甚至还将x-cloud-trace-context头添加到本地客户端请求,但无济于事-启动跟踪时不使用包含的值。

我不确定这里遗漏了什么-我可以在云跟踪仪表板中看到痕迹,因此我相信基本检测是正确的,但是CloudTraceFormatPropagator的配置/使用显然有问题。


解决方案

原来我的配置不正确--或者,我应该说,它不完整。我从Google Cloud OpenTelemeter库的文档中按照this basic example操作,但我没有意识到不需要手动检测。

我在我的GRPC方法中删除了对tracer.start_as_current_span的调用,安装了GRPC工具包(opentelemetry-instrumentation-grpc),并在启动我的GRPC服务器时将其添加到跟踪配置步骤中,现在如下所示:


from opentelemetry.instrumentation import grpc as grpc_instrumentation
from common import tracing # from my original question

def main():
    """Starts up GRPC server."""

    # Set up tracing
    tracing.configure_tracing()
    grpc_instrumentation.GrpcInstrumentorServer().instrument()

    # Set up the gRPC server
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=100))
    # set up services & start

此方法解决了我的问题中描述的问题-我的日志消息现在以预期的方式串接在一起

作为遥测和测量工具的新手,我没有意识到我需要采取额外的步骤,因为我正在跟踪GRPC请求,但现在这是有意义的。

我最终在different set of docs中找到了一些有用的示例-我不确定为什么这些示例与本答案前面链接的文档分开。

编辑:啊,我相信GRPC工具,以及相关的文档,都是一个独立但相关项目的一部分,在该项目中,贡献者可以添加用于工具感兴趣的库(例如,GRPC、REDIS等)的包。如果它是统一的,那将是很有帮助的,这是主OpenTelemeter Python Repo中this issue的主题。

相关文章