使用OpenTelemetry跟踪部署在云上的Python gRPC服务器

2024-06-09 20:19:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在云上运行一个Python gRPC服务器,并试图添加捕获跟踪信息的工具。我目前有一个基本的设置,但是我在使用OpenTelemetry docs中显示的传播时遇到了问题

入站请求具有x-cloud-trace-context头,我可以在我使用的gRPC方法中记录头值,但是OpenTelemetry库创建的跟踪始终具有不同于来自请求头的跟踪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函数由容器启动时运行的entrypoint脚本调用,因此它在处理任何请求之前执行。当在谷歌云中运行时,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头添加到本地客户机请求中,但没有用——启动跟踪时不使用包含的值

我不确定我在这里遗漏了什么-我可以在Cloud Trace仪表板中看到跟踪,所以我相信基本的工具是正确的,但是显然在CloudTraceFormatPropagator的配置/使用中发生了一些事情


Tags: fromimportcloudgetasservicecontexttrace
2条回答

在使用Python查看OpenTelemetry的Google文档时,我发现了一些有助于跟踪正确ID的配置。此外,还有一个疑难解答文档,当您希望跟踪数据存在时,可以在Google Cloud Project中查看跟踪

Python OpenTelemetry-https://cloud.google.com/trace/docs/setup/python-ot

谷歌云跟踪疑难解答-https://cloud.google.com/trace/docs/troubleshooting

对于安全通道,您需要传入chanel_type=’secure’。下面的链接对此进行了解释:https://github.com/open-telemetry/opentelemetry-python-contrib/issues/365

您需要使用x-cloud-trace-context头来确保您的跟踪使用与Google Cloud运行中的负载平衡器和AppServer相同的跟踪ID,并且所有跟踪都在Google跟踪中链接

下面的代码可以看到您在Google Trace’s Trace List视图中的跟踪旁边记录日志:

from opentelemetry import trace 
from opentelemetry.trace.span import get_hexadecimal_trace_id, get_hexadecimal_span_id 
       current_span = trace.get_current_span() 
       if current_span: 
          trace_id = current_span.get_span_context().trace_id 
          span_id = current_span.get_span_context().span_id 
          if trace_id and span_id: 
              logging_fields['logging.googleapis.com/trace'] = f"projects/{self.gce_project}/traces/{get_hexadecimal_trace_id(trace_id)}"
              logging_fields['logging.googleapis.com/spanId'] = f"{get_hexadecimal_span_id(span_id)}" 
              logging_fields['logging.googleapis.com/trace_sampled'] = True

上面的文档和代码是使用Flask Framework测试的

结果是我的配置不正确——或者,我应该说,它不完整。我从Google Cloud OpenTelemetry库的文档中找到了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请求,但现在这是有意义的

我最终在adifferent set of docs中找到了一些有用的例子——我不知道为什么这些例子与本答案前面链接的文档是分开的

编辑:啊,我相信gRPC工具,以及相关文档,是一个独立但相关的项目的一部分,贡献者可以添加工具库感兴趣的包(即gRPC、redis等)。如果它是统一的,那会很有帮助,这是主要OpenTelemetry Python repo中this issue的主题

相关问题 更多 >