【问题标题】:Tracing a Python gRPC server deployed on Cloud Run with OpenTelemetry使用 OpenTelemetry 跟踪部署在 Cloud Run 上的 Python gRPC 服务器
【发布时间】:2021-10-04 18:45:05
【问题描述】:

我在 Cloud Run 上运行 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 函数由在容器启动时运行的入口点脚本调用,因此它在处理任何请求之前执行。在 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 标头添加到本地客户端请求,但无济于事 - 开始跟踪时不使用包含的值。

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

【问题讨论】:

    标签: python grpc google-cloud-run open-telemetry


    【解决方案1】:

    事实证明我的配置不正确 - 或者,我应该说,它不完整。我关注了 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 请求,但现在这很有意义。

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

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

    【讨论】:

      【解决方案2】:

      在使用 Python 查看 OpenTelemetry 的 Google 文档时,我发现一些配置可以帮助解决跟踪正确 ID 的问题。此外,当您希望跟踪数据存在时,还有一个故障排除文档可以查看您的 Google Cloud Project 中的跟踪。

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

      Google Cloud Trace 疑难解答 - 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 Run 上的负载平衡器和 AppServer 相同的跟踪 ID,并且所有这些都在 Google Trace 中链接。

      下面的代码可以让您在 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测试的。

      【讨论】:

      • 感谢您的回答!我一直在参考您链接的文档,以及有用的 Github 问题。此外,我已经确认 x-cloud-trace-context 标头包含在对我的 gRPC 服务器的请求中 - 我遇到的问题是,在跟踪传播期间尝试提取该标头时,跟踪库似乎没有发现该标头。你粘贴的那个 sn-p 在 Flask 中工作吗?我问是因为opentelemetry.trace.span 的最新版本已经更新了跟踪和跨度 ID 格式函数的名称。
      猜你喜欢
      • 2021-05-27
      • 1970-01-01
      • 2021-05-05
      • 1970-01-01
      • 2019-10-24
      • 2019-10-13
      • 1970-01-01
      • 1970-01-01
      • 2019-02-06
      相关资源
      最近更新 更多