使用 OpenTelemetry 跟踪部署在云 运行 上的 Python gRPC 服务器
Tracing a Python gRPC server deployed on Cloud Run with OpenTelemetry
我正在 运行在云 运行 上安装一个 Python gRPC 服务器,并尝试添加工具来捕获跟踪信息。我目前有一个基本设置,但是我在使用 OpenTelemetry docs.
中所示的传播时遇到了问题
入站请求具有 x-cloud-trace-context
header,我可以在我一直使用的 gRPC 方法中记录 header 值,但是由 OpenTelemetry 库创建的跟踪始终具有与请求 header.
中的跟踪 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
header 添加到本地客户端请求,但无济于事 - 开始跟踪时不使用包含的值。
我不确定我在这里遗漏了什么 - 我可以在 Cloud Trace 仪表板中看到痕迹,所以我相信基本检测是正确的,但是 configuration/usage 的 configuration/usage 显然有问题CloudTraceFormatPropagator
.
在查看 Google 使用 Python 的 OpenTelemetry 文档时,我发现了一些配置可以帮助解决跟踪正确 ID 的问题。此外,当您希望跟踪数据存在时,有一个故障排除文档可用于查看 Google Cloud Project
中的跟踪。
Python-OpenTelemetry - https://cloud.google.com/trace/docs/setup/python-ot
Google 云跟踪故障排除 - https://cloud.google.com/trace/docs/troubleshooting
对于安全通道,您需要传入chanel_type=’secure’
。解释如下link:https://github.com/open-telemetry/opentelemetry-python-contrib/issues/365
您需要使用 x-cloud-trace-context
header 来确保您的跟踪使用与 Google Cloud 运行 上的负载均衡器和 AppServer 相同的跟踪 ID,以及所有link 在 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 请求,但现在这很有意义。
我最终在 different set of docs 中找到了一些有用的示例 - 我不确定为什么这些示例与本答案前面链接的文档是分开的。
编辑:啊,我相信 gRPC 检测以及相关文档是一个单独但相关的项目的一部分,贡献者可以在其中添加检测感兴趣的库(即 gRPC、redis 等)的包。如果它是统一的,那将会很有帮助,这是主要 OpenTelemetry Python 存储库中 this issue 的主题。
我正在 运行在云 运行 上安装一个 Python gRPC 服务器,并尝试添加工具来捕获跟踪信息。我目前有一个基本设置,但是我在使用 OpenTelemetry docs.
中所示的传播时遇到了问题入站请求具有 x-cloud-trace-context
header,我可以在我一直使用的 gRPC 方法中记录 header 值,但是由 OpenTelemetry 库创建的跟踪始终具有与请求 header.
这是我创建的简单 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
header 添加到本地客户端请求,但无济于事 - 开始跟踪时不使用包含的值。
我不确定我在这里遗漏了什么 - 我可以在 Cloud Trace 仪表板中看到痕迹,所以我相信基本检测是正确的,但是 configuration/usage 的 configuration/usage 显然有问题CloudTraceFormatPropagator
.
在查看 Google 使用 Python 的 OpenTelemetry 文档时,我发现了一些配置可以帮助解决跟踪正确 ID 的问题。此外,当您希望跟踪数据存在时,有一个故障排除文档可用于查看 Google Cloud Project
中的跟踪。
Python-OpenTelemetry - https://cloud.google.com/trace/docs/setup/python-ot
Google 云跟踪故障排除 - https://cloud.google.com/trace/docs/troubleshooting
对于安全通道,您需要传入chanel_type=’secure’
。解释如下link:https://github.com/open-telemetry/opentelemetry-python-contrib/issues/365
您需要使用 x-cloud-trace-context
header 来确保您的跟踪使用与 Google Cloud 运行 上的负载均衡器和 AppServer 相同的跟踪 ID,以及所有link 在 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 请求,但现在这很有意义。
我最终在 different set of docs 中找到了一些有用的示例 - 我不确定为什么这些示例与本答案前面链接的文档是分开的。
编辑:啊,我相信 gRPC 检测以及相关文档是一个单独但相关的项目的一部分,贡献者可以在其中添加检测感兴趣的库(即 gRPC、redis 等)的包。如果它是统一的,那将会很有帮助,这是主要 OpenTelemetry Python 存储库中 this issue 的主题。