当尝试在跨度中添加跨度链接时,直接的方法
我在文档中阅读过,不起作用。我总是得到
AttributeError: 'Context' object has no attribute 'trace_id'
from opentelemetry import trace
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
from shared_functionality.common.observability import make_global_tracer
make_global_tracer()
tracer = trace.get_tracer(__name__)
def message_broker_consumer_runner():
with tracer.start_as_current_span(name=f"consumer_runner.{None}") as consume_span:
carrier = {}
TraceContextTextMapPropagator().inject(carrier)
return carrier
def api_gateway():
with tracer.start_as_current_span(name=f"api_gateway.{None}") as consume_span:
carrier = {}
TraceContextTextMapPropagator().inject(carrier)
return carrier
def update_info(__trace_propagator):
print(f"{__trace_propagator=}")
def end(consumer_runner_carrier):
print(f"{consumer_runner_carrier=}")
consumer_runner_span = trace.NonRecordingSpan(
TraceContextTextMapPropagator().extract(consumer_runner_carrier)
)
consumer_runner_context = consumer_runner_span.get_span_context()
with tracer.start_as_current_span(
name="trigger_update.info",
context=api_gateway(), # to show the API Gateway trigger as causal parent.
links=[
trace.Link(consumer_runner_context)
], # to show consumer running function as co-parent/linked.
) as msg_span:
update_info(
__trace_propagator=msg_span.get_span_context()
) # Trace further passed to track business logic function.
if __name__ == "__main__":
end(message_broker_consumer_runner())
consumer_runner_carrier={'traceparent': '00-624066a40219dec50ed88de4feb4ee7a-1be9a825202fa729-01'}
__trace_propagator=SpanContext(trace_id=0x7ae344302d28c0000ea8d4b5457aff39, span_id=0x851a6758cb28f8b7, trace_flags=0x01, trace_state=[], is_remote=False)
{
"name": "consumer_runner.None",
"context": {
"trace_id": "0x624066a40219dec50ed88de4feb4ee7a",
"span_id": "0x1be9a825202fa729",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": null,
"start_time": "2023-10-10T18:39:20.205144Z",
"end_time": "2023-10-10T18:39:20.205172Z",
"status": {
"status_code": "UNSET"
},
"attributes": {},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.20.0",
"service.name": "data_fetcher",
"service.version": "1.0",
"deployment.environment": "development"
},
"schema_url": ""
}
}
{
"name": "api_gateway.None",
"context": {
"trace_id": "0x565151dfcc4cffbbd44e953d2c8ba27a",
"span_id": "0x3e3e8a6265a6e481",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": null,
"start_time": "2023-10-10T18:39:20.205270Z",
"end_time": "2023-10-10T18:39:20.205287Z",
"status": {
"status_code": "UNSET"
},
"attributes": {},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.20.0",
"service.name": "data_fetcher",
"service.version": "1.0",
"deployment.environment": "development"
},
"schema_url": ""
}
}
Exception while exporting Span batch.
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/export/__init__.py", line 368, in _export_batch
self.span_exporter.export(self.spans_list[:idx]) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/export/__init__.py", line 522, in export
self.out.write(self.formatter(span))
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/export/__init__.py", line 513, in <lambda>
] = lambda span: span.to_json()
^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/__init__.py", line 492, in to_json
f_span["links"] = self._format_links(self._links)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/__init__.py", line 535, in _format_links
] = Span._format_context( # pylint: disable=protected-access
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/opentelemetry/sdk/trace/__init__.py", line 500, in _format_context
x_ctx["trace_id"] = f"0x{trace_api.format_trace_id(context.trace_id)}"
^^^^^^^^^^^^^^^^
AttributeError: 'Context' object has no attribute 'trace_id'
Python 3.11.4
opentelemetry-api 1.20.0
opentelemetry-distro 0.41b0
opentelemetry-exporter-otlp 1.20.0
opentelemetry-exporter-otlp-proto-common 1.20.0
opentelemetry-exporter-otlp-proto-grpc 1.20.0
opentelemetry-exporter-otlp-proto-http 1.20.0
opentelemetry-instrumentation 0.41b0
opentelemetry-instrumentation-aws-lambda 0.41b0
opentelemetry-instrumentation-dbapi 0.41b0
opentelemetry-instrumentation-grpc 0.41b0
opentelemetry-instrumentation-httpx 0.41b0
opentelemetry-instrumentation-logging 0.41b0
opentelemetry-instrumentation-pymongo 0.41b0
opentelemetry-instrumentation-requests 0.41b0
opentelemetry-instrumentation-sqlite3 0.41b0
opentelemetry-instrumentation-tortoiseorm 0.41b0
opentelemetry-instrumentation-urllib 0.41b0
opentelemetry-instrumentation-urllib3 0.41b0
opentelemetry-instrumentation-wsgi 0.41b0
opentelemetry-propagator-aws-xray 1.0.1
opentelemetry-proto 1.20.0
opentelemetry-sdk 1.20.0
opentelemetry-semantic-conventions 0.41b0
opentelemetry-util-http 0.41b0
当我使用Pycharm的调试器暂停执行时,我可以看到
consumer_runner_context
的属性对象,它在其属性树深处有一个trace_id,因此我可以访问它:
consumer_runner_context.get(list(consumer_runner_context.keys())[0]).get_span_context()
这有效:
with tracer.start_as_current_span(
name="trigger_update.info",
context=api_gateway(), # to show the API Gateway trigger as causal parent.
links=[trace.Link(consumer_runner_context.get(list(consumer_runner_context.keys())[0]).get_span_context())]
)
但这似乎不是正确的方法。
正确的做法是什么?
至于答案 - 是的,据我所知,这是目前的做法。
ctx = propagate.extract(carrier)
sctx = next(iter(ctx.values())).get_span_context()
link = trace.Link(sctx)
在尝试了一段时间的 Links 之后,在我看来,它们(仍然)是 Otel 家族中有点被忽视的孩子。 如有错误,请指正。
我现在想到的两个例子:
链接的可视化(例如在 Jaeger、SigNoz...)是非常基础的。人们只看到简单的“引用”(指向某处),这比什么都没有好,但使调试变得困难。
通常仅在当前跨度中才发现要链接的跨度,而事先并不知道。仅仅为了捕获链接而创建新的子跨度通常是不需要的。在这里,我很想看到
span.add_link()
方法。
不知道这是否有帮助,但我注意到 Python OpenTelemetry SDK 生成的跟踪信号的 JSON 模式与(例如)手动上传时 Tempo 接受的 JSON 模式之间存在多个差异。例如:
SDK 会生成用于扫描的 JSON 文件,其中“属性”字段是一个对象(“{}”),但如果我手动将其更改为数组(“[]”),我只能将其导入到 Tempo 中。
正如最初的问题提到的,这个 JSON 片段是由 SDK 生成的,但 Tempo 导入有问题:
“上下文”:{ “trace_id”:“0x624066a40219dec50ed88de4feb4ee7a”, “span_id”:“0x1be9a825202fa729”, “trace_state”:“[]” },
要导入它,我必须将其替换为单独的字段(即消除“上下文”并将 JSON 结构中的 id 字段提升一级),字段名称遵循 Camel 约定:
"traceId": "0x624066a40219dec50ed88de4feb4ee7a",
"spanId": "0x1be9a825202fa729",
对我来说,结果是:在我相当简单的用例中可能会避免使用 Python OpenTelemetry SDK。我将编写自己的代码(“迷你库”)来生成 JSON 有效负载,我通过实验预先验证该负载将被 Tempo 接受。这对我来说很有效,因为我的代码行很小,但对于拥有许多开发人员的更大系统来说显然是行不通的。有点令人失望的是,OpenTelemetry/Grafana 人员无法在他们支持的 JSON 模式方面保持同步。