[RFC] OpenViking OpenTelemetry Unified Management Design/OpenViking OpenTelemetry 统一管理设计 #1602
baojun-zhang
started this conversation in
RFC
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
OpenViking OpenTelemetry Unified Management Design
English Version / 英文版
🔍 Review Items
server.observability.{metrics,traces,logs}.1. Background and Goals
This document analyzes the current observability design in OpenViking, excluding
bot, and discusses how to integrate the OpenTelemetry SDK without overturning the existing architecture, so that the three signal types ofmetric,trace, andlogcan be unified.2. Current-State Analysis
2.1 Existing observability entry points
/api/v1/observer/*/health/api/v1/debug/health/api/v1/stats/*/metricsDataSource -> Collector -> MetricRegistryExport side:
PrometheusExporterOTelMetricExporteras an additional export pathOperationTelemetry(response summary + summary events)run_operation(), summarized atfinish()openviking.telemetry.tracer(OTel Trace)trace_idcorrelationTracerProvider+OTLPSpanExporter(enabled by config)OTLPSpanExporteralready exist; the missing piece in the original analysis context is a unified HTTP tracing middleware on the root-span main path2.2 Existing architecture
2.2.1 Metrics
flowchart TB A1["HTTP Request"] --> A2["HTTP Metrics Middleware"] A2 --> A3["HttpRequestLifecycleDataSource"] A3 --> A4["HTTPCollector"] A4 --> A5["MetricRegistry"] B1["Business Events / State"] --> B2["Event / State DataSources"] B2 --> B3["Embedding / Retrieval / VLM / Session / Resource Collectors"] B3 --> A5 C1["OperationTelemetry.finish()"] --> C2["telemetry.summary"] C2 --> C3["TelemetryBridgeEventDataSource"] C3 --> C4["TelemetryBridgeCollector"] C4 --> A5 D1["Observer / Probe State"] --> D2["Observer State / Probe DataSources"] D2 --> D3["State / Probe Collectors"] D3 --> A5 A5 --> E1["PrometheusExporter"] E1 --> E2["/metrics"]HTTP metrics middleware, business-eventDataSource, state/probeDataSource, and thetelemetry.summarybridge event.MetricRegistry, thenPrometheusExporterrenders them to/metrics.openviking.metrics.*design, nottracer.py.DataSource -> Collector -> MetricRegistry -> Exporterlayering has already been formed.OperationTelemetryalready enters the metrics pipeline throughTelemetryBridgeCollector, which gives it strong bridge value.2.2.2 Trace
flowchart TB A1["HTTP Request Handling (Runtime)"] --> A2["Localized Instrumentation\n@tracer(...) / start_as_current_span()"] A2 --> A3["Tracer (OTel API)"] A3 --> A6["OTLPSpanExporter"] A6 --> A7["OTLP Trace Backend\n(Jaeger / Collector / APM)"] B1["AsyncioInstrumentor"] --> A3openviking.telemetry.traceralready introduces the OTel Trace SDK and can initializeTracerProvider,OTLPSpanExporter, andBatchSpanProcessor.@tracer(...),start_as_current_span(),set(), andinfo().AsyncioInstrumentoris also supported.2.2.3 Current log architecture
flowchart TB A1["Business Code logger.info/debug/error"] A2["logging.Logger / get_logger()"] A3["loguru logger"] A1 --> A2 A1 --> A3 B1["tracer._setup_logging()"] B2["TraceIdLoggingFilter"] A2 --> B2 B1 --> B2 B1 --> B3["loguru patcher(trace_id)"] A3 --> B3 B2 --> C1["stdout / stderr / file handlers"] B3 --> C2["loguru output"]loggingandloguru.tracer.pyon the logging side istrace_idinjection, which lets logs correlate with the current trace.2.2.4 Operation Telemetry
OperationTelemetryhas strong bridge value and already settles the core summary of a request into structured fields such as:duration_mstokens.totaltokens.llm.*tokens.embedding.*vector.searches/scanned/returnedmemory.extractedresource.process.*errors.stage/error_code/messageThese fields have two layers of value:
This means OpenViking already has a ready-made domain-summary model.
2.3 Problem analysis
2.3.1 Current state
OpenViking currently has four parallel but not yet fully unified observability paths:
Observerpath: instant component-state queries for human troubleshooting.Statspath: re-query interfaces for business analysis and diagnostic statistics.Metricspath: a low-cardinality metric system for Prometheus scraping.Telemetry/Tracingpath: structured summaries and localized OTel Trace capability for a single request.2.3.2 Problems
These paths are still individually usable but collectively fragmented:
operation telemetryandtrace/log/metrichas not yet been systematized.Therefore, the current goal is to evolve the existing system into a unified OTel-friendly observability architecture.
2.3.3 Trace, Metric, and Log do not share one unified context
MetricRegistry ->PrometheusExporter ->/metricstracer.pylocalized decorator/helper
logging+loguru(primarily
trace_idinjection)operation/status/method/route(depends on metric definition)account_id(restricted by allowlist / dimension policy)trace_id(locally available)span_id(locally available)trace_id(already injectable)missing an HTTP entry path;
missing a unified structured field standard:
request_id
account_id
user_id
agent_id
operation
telemetry_id
request_id
account_id
user_id
agent_id
operation
telemetry_id
missing an OTLP logs pipeline
Therefore, the core goals are:
2.3.4 Coverage gaps of
operationinopenviking_operation_tokens_totalThe
operationdimension inopenviking_operation_tokens_totalcurrently comes fromtelemetry.summary.operation, andtelemetry.summaryis only produced on HTTP routes that explicitly integraterun_operation(). Therefore, the operations that can stably enter theopenviking_operation_*metric family are fundamentally limited by which routers have already integrated operation telemetry.Current state
openviking_operation_tokens_totalare mainly:search.findsearch.searchresources.add_resourcerun_operation(), and that token usage is indeed generated inside those operations.run_operation(), so it does not producetelemetry.summaryopenviking_operation_tokens_totalRoot cause
run_operation(operation="...")currently include:search.findsearch.searchresources.temp_uploadresources.add_resourceresources.add_skillcontent.writeresources.temp_uploadusually does not generate model tokens, so even if it entersopenviking_operation_requests_total, it may still not appear inopenviking_operation_tokens_totalresources.add_skillandcontent.writeonly produce token samples when they actually trigger model callscontent.reindexsessions.commitmemory_extract,resource_summarize, andembed_resource, they do not enter the operation-level metric path oftelemetry.summary -> telemetry bridge -> openviking_operation_*because the router layer has not uniformly integratedrun_operation().Backlog to fill
It is recommended to prioritize router-level operation telemetry integration for the following operations:
content.reindexregenerate=true, it is a critical operation for observingresource_summarize,embed_resource, and possiblevlmattribution.sessions.commitmemory_extract.openviking_operation_tokens_total{operation="sessions.commit", ...}.Other session/memory-related operations
Conclusion
operationgap inopenviking_operation_tokens_totalshould not simply be attributed to missing collector instrumentation.run_operation()coverage should be treated as an explicit refactoring goal, rather than continuing patch-style extensions only at the metrics-bridge layer.3. OTel Integration Goals
The goal is not simply to add an SDK, but to form a unified observability architecture:
metriccontinues to preserve its current low-cardinality, low-cost, and scrape-friendly characteristics.tracebecomes part of the server-side main request path.logcan be correlated with trace and metric through shared context fields.operation telemetrycontinues to exist, but as a cross-signal semantic bridge.4. Key Design
4.1 Scheme Description
The current OpenViking state is: highly modular Metrics, flexibly usable OTel Trace, request-level Telemetry, and Logs containing partial context. Based on this, the core path of this scheme is:
OTelMetricExporterto export data from the registry.OperationTelemetryas the unified semantic bridge.This scheme can reduce dependency on external components for flexible deployment (low dependency on OTel Collector), and can adapt well to heterogeneous environments (for example, commercial environments using Jaeger + Prometheus + Loki).
4.2 Data Flow Diagram
sequenceDiagram participant Client as Caller / HTTP Request participant Middleware as HTTP Tracing Middleware participant Router as FastAPI Router / run_operation() participant Trace as Trace (Root / Operation / Child Spans) participant Exec as Business Execution participant Telemetry as Telemetry (Operation Summary) participant Metrics as Metrics participant Logs as Logs participant Export as OTLP Exporters Client->>Middleware: Send request Middleware->>Trace: Create Root Span Middleware->>Router: Forward request Router->>Trace: Create Operation Span Router->>Telemetry: Bind Telemetry Router->>Exec: Execute business logic Exec->>Trace: Write Child Span / Event Exec->>Telemetry: Accumulate token / vector / memory / stage Exec->>Logs: Emit business logs Trace->>Logs: Inject trace_id / span_id / request_id / operation Router->>Telemetry: finish() Telemetry->>Trace: Backfill low-cardinality summary Telemetry->>Metrics: Bridge operation summary Trace->>Export: Export Trace Metrics->>Export: Export Metrics Logs->>Export: Export LogsOnce a single HTTP request enters OpenViking,
trace / telemetry / metrics / logare processed on the runtime main path as shown above. The flow is:HTTP Tracing Middlewarecreates theRoot Spanand establishes the trace context for this request.FastAPI Router / run_operation(), anOperation Spanis created for the current business operation, and theTelemetryaggregation context for that operation is bound.Trace, while continuously accumulating summaries such as token, vector, memory, and stage intoTelemetry.trace_id/span_id/request_id/operationfrom the current trace context, thereby associating the log with the request path.Telemetry.finish()generates an operation summary; low-cardinality fields can be backfilled intoTraceand further bridged intoMetrics, forming metrics for aggregate query.Trace,Metrics, andLogsare exported to the backend throughOTLP Exporters;Telemetryitself is not an independently exported signal, but serves as a business-summary bridge layer that provides unified operation-level semantics for Trace and Metrics.4.3 Unified Context Specification
By applying unified context constraints to
metric/trace/log, the three signals can form an observability loop around dimensions such as account and operation.4.3.1 Field Naming Conventions
request_id/account_id/operation/telemetry_id/tokens.total.http.method/http.route/http.status_code/url.path.4.3.2 Field Description
4.3.2.1 Root Span Context Fields
http.methodhttp.routehttp.status_codeurl.pathrequest_idsource_type/source_versionaccount_idaccount_id(allowlist metrics only)user_idagent_idtrace_id/span_id4.3.2.2 Operation Span Context Fields
operationsearch.find)operation(aggregatable dimension)telemetry_idstatusok/error, etc.)status(when low cardinality)tokens.*/vector.*/memory.*OperationTelemetrytrace_id/span_id4.3.3 Injection Timing and Sources
The end-to-end observability path can take the HTTP request entry as the main insertion point:
tracing middleware):request_idtraceparent/baggagecontextvarsfor subsequent business usageRequestContextis formed):account_id/user_id/agent_id/auth_modeinto the current span and log contextrun_operation()(inside theOperationTelemetrylifecycle):operation/telemetry_id/statusOperationTelemetryinto span attributes/events4.3.4 Context Rules
operation, controlledaccount_id, and optionalstatus.4.4 Metrics Implementation Design
4.4.1 Design Notes
The OTel SDK itself supports implementing metrics through OTel Meter, but this is not used as the default metrics implementation in OpenViking for the following reasons:
MetricRegistry; if business code writes directly to Meter, high-cardinality labels become even harder to govern uniformly.4.4.2 Design Approach
The metrics design is as follows:
MetricRegistryremains the unified metric store.Collectorremains the only layer allowed to write into the registry.OTelMetricExporterto support OTel-format export./metricsfully compatible while allowing the same registry data to be exported to OTel.4.4.3 Configuration
The metrics configuration adds an
exporterssection to enable multiple exporters, for example:{ "server": { "observability": { "metrics": { "enabled": true, "account_dimension": { "enabled": true, "max_active_accounts": 100, "metric_allowlist": [ "openviking_*" ] }, "exporters": { "prometheus": { "enabled": true }, "otel": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server", "export_interval_ms": 10000 } } }, "traces": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" }, "logs": { "enabled": false, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" } } } }Benefits:
4.5 Trace Implementation Design
4.5.1 Overall Approach
The trace design is as follows:
tracer.py, but elevate them into service-level infrastructure.tracer.pyshould be further converged so that provider/resource/exporter are initialized by one runtime path, reducing repeated logging patching and avoiding duplicated injection paths betweenloguruand standardlogging.@tracer(...)decorator, but position it as a child-span helper rather than the root entry.OperationTelemetrywith the current span insiderun_operation(); at the same time create an operation span for each logical operation, directly reusing the current operation naming.4.5.2 Relationship with Telemetry
Telemetryis positioned as the business summary of trace. The boundary of responsibilities is:At the end of a request, execute the following logic:
Telemetry.finish()generates the summary.Telemetryfields into the operation span as attributes/events, using the telemetry summary model as the source of truth to avoid high cardinality and privacy leakage.At present there is coupling between Metrics and Telemetry through bridge references. When
telemetry=false, some metrics will not appear, such asopenviking_operation_duration_seconds. Proposed solutions are:OperationTelemetry, but decouple response payload from collectionOperationTelemetrystill executesfinish()by default, continues bridging to metrics, and backfills low-cardinality fields into the operation spanOperationTelemetryOperationTelemetryto business summaries and additional trace/log context, rather than the only source for metrics4.6 Log Implementation Design
4.6.1 Design Notes
Logs are not simply about sending current logs to OTLP; three goals must be achieved:
4.6.2 Design Approach
Based on the current usage pattern of
openviking_cli.utils.logger.get_logger(), add the following internally:LoggingHandler.Log context should come from a unified logger context injector, automatically injected from the current span and carrying fields such as
trace_id/span_id/request_id/operation, so business code no longer needs to explicitly write strings like"trace_id={...}"just to build correlation.4.6.3 Logs as the Default Observability Entry Point (Linked with Trace)
In real development, developers often have to write repetitive code to satisfy both trace and log observability needs by calling both
tracer.xxx()andlog.xxx(). To simplify development, it is recommended to use logs as the default observability entry point, and let them link with trace through context injection and optional bridge capabilities.Core idea: developers should default to writing only
log.xxx(); trace linkage is filled in automatically by the infrastructure, and only a small number of scenarios that require fine-grained control over span hierarchy, events, attributes, or status should explicitly use trace APIs.Specific approach:
trace_id/span_id/request_id/operation/...Effects:
Applicable scenarios:
Code example comparison:
Automatic log output with trace context:
{ "timestamp": "2026-04-17T10:00:00Z", "level": "INFO", "message": "Start do_something", "trace_id": "abc123...", "span_id": "def456...", "request_id": "req-789...", "operation": "chat_completion", "key": "value" }Files that need to change:
openviking_cli/utils/logger.py: add a trace context filteropenviking/observability/log_trace_bridge.pyto provide helpers such aslog_and_add_eventFiles that do not need to change:
tracer.py; it remains lower-level infrastructure4.7 Configuration Design Recommendations
4.7.1 Current Configuration State
The current
server.configalready contains:server.observability.metrics.enabledAt the same time, initialization in
tracer.pydepends on higher-level configuration entries such as:telemetry.tracer.enabledtelemetry.tracer.endpointtelemetry.tracer.service_nametelemetry.tracer.topictelemetry.tracer.aktelemetry.tracer.skThis means the project currently has two parallel config entry points: a server-side metrics config and a global telemetry tracer config.
4.7.2 Recommended Configuration Convergence Direction
It is recommended to converge to a unified observability config block in the future, for example:
{ "server": { "observability": { "metrics": { "enabled": true, "exporters": { "prometheus": { "enabled": true }, "otel": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server", "export_interval_ms": 10000 } } }, "traces": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" }, "logs": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" } } } }中文版 / Chinese Version
待评审项
server.observability.{metrics,traces,logs}。OpenViking OpenTelemetry 统一管理设计
1. 背景与目标
本文分析 OpenViking 当前工程中除
bot外的现有可观测性设计,并讨论在不推翻现有体系的前提下,如何接入 OpenTelemetry SDK,完成metric、trace、log三类观测信号的统一落地。2. 现状分析
2.1 现有工程中的观测入口
/api/v1/observer/*/health/api/v1/debug/health/api/v1/stats/*/metricsDataSource -> Collector -> MetricRegistry导出侧:
PrometheusExporterOTelMetricExporter作为“多出口导出”OperationTelemetry(响应摘要 + summary 事件)run_operation()过程中采集,finish()产出 summaryopenviking.telemetry.tracer(OTel Trace)2.2 现有架构
2.2.1 Metrics
flowchart TB A1["HTTP Request"] --> A2["HTTP Metrics Middleware"] A2 --> A3["HttpRequestLifecycleDataSource"] A3 --> A4["HTTPCollector"] A4 --> A5["MetricRegistry"] B1["Business Events / State"] --> B2["Event / State DataSources"] B2 --> B3["Embedding / Retrieval / VLM / Session / Resource Collectors"] B3 --> A5 C1["OperationTelemetry.finish()"] --> C2["telemetry.summary"] C2 --> C3["TelemetryBridgeEventDataSource"] C3 --> C4["TelemetryBridgeCollector"] C4 --> A5 D1["Observer / Probe State"] --> D2["Observer State / Probe DataSources"] D2 --> D3["State / Probe Collectors"] D3 --> A5 A5 --> E1["PrometheusExporter"] E1 --> E2["/metrics"]HTTP metrics middleware、业务事件DataSource、状态/探针DataSource以及telemetry.summary桥接事件。MetricRegistry,再由PrometheusExporter输出到/metrics。openviking.metrics.*这套分层,而不是tracer.py。DataSource -> Collector -> MetricRegistry -> Exporter的清晰分层。OperationTelemetry已经通过TelemetryBridgeCollector进入指标链路,具备良好的桥接价值。2.2.2 Trace
flowchart TB A1["HTTP 请求处理 (运行期)"] --> A2["局部埋点\n@tracer(...) / start_as_current_span()"] A2 --> A3["Tracer (OTel API)"] A3 --> A6["OTLPSpanExporter"] A6 --> A7["OTLP Trace Backend\n(Jaeger/Collector/APM)"] B1["AsyncioInstrumentor"] --> A3openviking.telemetry.tracer已经引入 OTel Trace SDK,并可初始化TracerProvider、OTLPSpanExporter和BatchSpanProcessor。@tracer(...)、start_as_current_span()、set()、info()等 helper 局部接入 tracing。AsyncioInstrumentor。2.2.3 Log 现状架构图
flowchart TB A1["业务代码 logger.info/debug/error"] A2["logging.Logger / get_logger()"] A3["loguru logger"] A1-->A2 A1 --> A3 B1["tracer._setup_logging()"] B2["TraceIdLoggingFilter"] A2 --> B2 B1 --> B2 B1 --> B3["loguru patcher(trace_id)"] A3 --> B3 B2 --> C1["stdout / stderr / file handlers"] B3 --> C2["loguru output"]logging和loguru的组合。tracer.py对日志的主要增强注入了trace_id,让日志与当前 trace 产生关联。2.2.4 Operation Telemetry
OperationTelemetry具备很强的“桥接价值”,而且已经把一次请求的核心摘要沉淀为结构化字段,例如:duration_mstokens.totaltokens.llm.*tokens.embedding.*vector.searches/scanned/returnedmemory.extractedresource.process.*errors.stage/error_code/message这些字段具备两层价值:
意味着 OpenViking 已经有了一层现成的领域摘要模型。
2.3 现状问题分析
2.3.1 现状
OpenViking 当前形成了 4 条并行但尚未完全统一的观测链路:
Observer链路:面向人工排障的组件瞬时状态查询;Stats链路:面向业务分析和诊断统计的重查询接口;Metrics链路:面向 Prometheus 抓取的低基数指标体系;Telemetry/Tracing链路:面向单次请求的结构化摘要与局部 OTel Trace 能力。2.3.2 问题
这几条链路目前仍然存在“各自可用、整体割裂”的问题:
operation telemetry与trace/log/metric的映射关系还没有系统化;因此,当前的目标是将现有体系演进为 OTel-friendly 的统一可观测性架构。
2.3.3 Trace、Metric、Log 三个信号源没有统一上下文
PrometheusExporter ->
/metrics
tracer.py局部 decorator/helper
logging+loguru(trace_id 注入为主)
operation/status/method/route(取决于指标定义)account_id(受 allowlist/维度策略约束)trace_id(局部可得)span_id(局部可得)trace_id(已可注入)缺少 HTTP 入口链路
缺少统一结构化字段标准:
request_id
account_id
user_id
agent_id
operation
telemetry_id
request_id
account_id
user_id
agent_id
operation
telemetry_id
缺少 OTLP logs pipeline
因此,我们的目标核心是:
2.3.4
openviking_operation_tokens_total的 operation 覆盖缺口当前
openviking_operation_tokens_total的operation维度来自telemetry.summary.operation,而telemetry.summary只会在显式接入run_operation()的 HTTP 路由上产生。因此,当前能稳定进入openviking_operation_*指标族的 operation,本质上受限于“哪些 router 已经接入了 operationtelemetry”。
现状
openviking_operation_tokens_total中稳定看到的 operation 主要是:search.findsearch.searchresources.add_resourcerun_operation(),并且在这些 operation 内部确实产生了 token 使用。
run_operation(),因此不会产出telemetry.summaryopenviking_operation_tokens_total根因
run_operation(operation=\"...\")的 router operation 包括:search.findsearch.searchresources.temp_uploadresources.add_resourceresources.add_skillcontent.writeresources.temp_upload通常不会产生模型 token,因此即使进入openviking_operation_requests_total,也不一定会出现在
openviking_operation_tokens_totalresources.add_skill/content.write只有在实际触发模型调用时才会出现 token 样本content.reindexsessions.commitmemory_extract、resource_summarize、embed_resource等 stage,但由于router 层没有统一接入
run_operation(),因此不会进入telemetry.summary -> telemetry bridge -> openviking_operation_*这一套 operation 级指标链路。待补齐清单
建议优先补齐下列 operation 的 router 级 operation telemetry 接入:
content.reindexregenerate=true时,它是观察resource_summarize、embed_resource,以及可能出现的vlm归因的重要 operation。sessions.commitmemory_extract最关键的入口。openviking_operation_tokens_total{operation=\"sessions.commit\", ...}。其它 session/memory 相关 operation
结论
openviking_operation_tokens_total的 operation 缺口,不应简单归因于“collector 漏埋点”。run_operation()覆盖”作为一个明确的改造目标,而不是仅在 metrics bridge 层继续补丁式扩展。
3. OTel 集成目标
目标不是简单“加一个 SDK”,而是形成统一观测架构:
metric继续保留当前低基数、低成本、可抓取特征;trace成为服务端请求主链路的一部分;log能够和 trace、metric 通过上下文字段关联;operation telemetry继续存在,但作为跨信号的语义桥接层;4. 关键方案设计
4.1 方案说明
OpenViking 现状为高度模块化的 Metric、具备可灵活使用的 Otel Trace、请求维度的Telemetry、包含了部分上下文的 Log,基于上述现状,本方案核心路径如下:
OTelMetricExporter,把 registry 中的数据进行导出;OperationTelemetry作为统一语义桥。该方案可以降低对外部组件的依赖进行灵活部署(对 Otel-Collector 的依赖性低),针对于异构环境可以有很好的适配(比如 Jaeger+Prometheus+Loki 的商业化环境)。
4.2 数据流向图
sequenceDiagram participant Client as 调用方 / HTTP 请求 participant Middleware as HTTP Tracing Middleware participant Router as FastAPI Router / run_operation() participant Trace as Trace (Root / Operation / Child Spans) participant Exec as Business Execution participant Telemetry as Telemetry (Operation Summary) participant Metrics as Metrics participant Logs as Logs participant Export as OTLP Exporters Client->>Middleware: 发起请求 Middleware->>Trace: 创建 Root Span Middleware->>Router: 转发请求 Router->>Trace: 创建 Operation Span Router->>Telemetry: 绑定 Telemetry Router->>Exec: 执行业务逻辑 Exec->>Trace: 写入 Child Span / Event Exec->>Telemetry: 累加 token / vector / memory / stage Exec->>Logs: 输出业务日志 Trace->>Logs: 注入 trace_id / span_id / request_id / operation Router->>Telemetry: finish() Telemetry->>Trace: 回填低基数摘要 Telemetry->>Metrics: 桥接 operation summary Trace->>Export: 导出 Trace Metrics->>Export: 导出 Metrics Logs->>Export: 导出 Logs一次 HTTP 请求进入 OpenViking 后,
trace / telemetry / metrics / log在运行时主链路上如上进行处理,步骤如下:HTTP Tracing Middleware作为入口创建Root Span,建立本次请求的 trace 上下文。FastAPI Router / run_operation()后,为当前业务操作创建Operation Span,并绑定本次 operation 对应的Telemetry聚合上下文。Business Execution在执行业务逻辑过程中,一方面向Trace写入 child span 或 event,另一方面持续向Telemetry累加 token、vector、memory、stage 等业务摘要。trace_id/span_id/request_id/operation等字段,从而与本次请求链路建立关联。Telemetry.finish()生成 operation summary;其中低基数字段可回填到Trace,并继续桥接到Metrics,形成面向聚合查询的指标。Trace、Metrics、Logs分别通过OTLP Exporters输出到后端;其中Telemetry自身不是独立导出信号,而是作为业务摘要桥接层,为 Trace 和 Metrics 提供统一的 operation 级语义。4.3 统一上下文规范
我们可以通过对
metric/trace/log进行统一的上下文规范约束,让三者可以在任何环境下都形成一定维度(account、 operation)观测闭环。4.3.1 字段命名约定
request_id/account_id/operation/telemetry_id/tokens.total。http.method/http.route/http.status_code/url.path)。4.3.2 字段说明
4.3.2.1 Root Span 上下文字段
http.methodhttp.routehttp.status_codeurl.pathrequest_idsource_type/source_versionaccount_idaccount_id(仅 allowlist 指标)user_idagent_idtrace_id/span_id4.3.2.2 Operation Span 上下文字段
operationsearch.find)operation(可聚合维度)telemetry_idstatusstatus(低基数时可用)tokens.*/vector.*/memory.*OperationTelemetry的低基数字段摘要trace_id/span_id4.3.3 注入时机与来源
整体链路式可观测行可以以 HTTP 请求入口作为切入点
request_idtraceparent/baggageaccount_id/user_id/agent_id/auth_mode到当前 span 与日志上下文run_operation()(OperationTelemetry 生命周期内):operation/telemetry_id/statusOperationTelemetry的低基数字段映射为 span attributes/events4.3.4 上下文规则
operation、受控account_id、可选status)4.4 Metrics 落地设计
4.4.1 设计说明
Otel SDK 本身支持 OTel Meter 的 Metric 实现方式, 但这里不作为 OpenViking Metric 的默认实现,其理由如下:
4.4.2 设计思路
Metric 的设计思路如下:
MetricRegistry继续作为统一指标存储;Collector仍然是唯一允许写 registry 的层;OtelExporter用于支持 Otel格式导出;/metrics完整兼容,同时允许将同一份 registry 数据导出到 OTel。4.4.3 配置
metrics 配置增加
exporters配置项,用于启用多个 exporter,例如:{ "server": { "observability": { "metrics": { "enabled": true, "account_dimension": { "enabled": true, "max_active_accounts": 100, "metric_allowlist": [ "openviking_*" ] }, "exporters": { "prometheus": { "enabled": true }, "otel": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server", "export_interval_ms": 10000 } } }, "traces": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" }, "logs": { "enabled": false, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" } } } }这样做的好处是:
4.5 Trace 落地设计
4.5.1 总体思路
Trace 设计如下:
tracer.py中已有的 OTel 初始化能力与 decorator 的便捷能力,但把它提升为服务级基础设施;tracer.py相关逻辑应进一步收敛:统一由一处 runtime 初始化 provider/resource/exporter,减少重复 patch logging,避免loguru与标准logging在注入链路上重复工作。@tracer(...)decorator 保留,但定位为子 span 辅助能力,而不是根入口。run_operation()内将OperationTelemetry与当前 span 绑定;同时为每个逻辑操作创建 operation span,名称可直接复用当前 operation 命名。4.5.2 与 Telemetry 的关系
Telemetry定位为 trace 的业务摘要,两者职责边界如下:在请求结束时,执行以下逻辑:
Telemetry.finish()生成摘要;Telemetry的低基数字段以 attributes/events 方式写入 operation span,具体字段以Telemetry摘要模型为准(避免高基数与隐私泄露)。目前 metric 与Telemetry存在桥接引用的情况,当telemetry=false 时,部分指标将不会出现(如
openviking_operation_duration_seconds等),解决方案如下4.6 Log 落地设计
4.6.1 设计说明
日志不是简单地“把现在的日志发到 OTLP”,而是要达成三件事:
4.6.2 设计思路
在当前
openviking_cli.utils.logger.get_logger()的使用方式基础上,内部增加:日志上下文应该来自统一 logger context injector,从当前 span 中自动注入,自动携带
trace_id/span_id/request_id/operation等字段,这样就不需要在业务代码中显式写入"trace_id={...}"进行关联。4.6.3 日志作为默认观测入口(与 trace 联动)
在实际开发中,为了同时满足 trace 和 log 的可观测性需求,开发人员经常需要写重复代码:同时写
tracer.xxx()和log.xxx()。为简化开发,推荐把日志作为默认观测入口,并通过上下文注入与可选 bridge 能力和 trace 联动。核心思路:让开发默认只写
log.xxx();trace 关联由底层自动补齐,只有少数需要精细控制 span 层次、事件、属性、状态的场景再显式使用 trace API。具体做法:
trace_id/span_id/request_id/operation/...)效果:
适用场景:
代码示例对比:
日志输出效果(自动携带 trace 上下文):
{ "timestamp": "2026-04-17T10:00:00Z", "level": "INFO", "message": "开始执行 do_something", "trace_id": "abc123...", "span_id": "def456...", "request_id": "req-789...", "operation": "chat_completion", "key": "value" }需要改动的文件:
openviking_cli/utils/logger.py:增加 trace context filteropenviking/observability/log_trace_bridge.py:提供log_and_add_event等便捷函数不需要改动的文件:
tracer.py(保持不变,作为底层基础设施)4.7 配置设计建议
4.7.1 当前配置现状
当前
server.config中已有:server.observability.metrics.enabled而
tracer.py的初始化又依赖更上层配置中的:telemetry.tracer.enabledtelemetry.tracer.endpointtelemetry.tracer.service_nametelemetry.tracer.topictelemetry.tracer.aktelemetry.tracer.sk这意味着当前配置存在“server 侧 metrics 配置”和“全局 telemetry tracer 配置”双入口并存的情况。
4.7.2 推荐配置收敛方向
建议后续收敛为统一观测配置块,例如:
{ "server": { "observability": { "metrics": { "enabled": true, "exporters": { "prometheus": { "enabled": true }, "otel": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server", "export_interval_ms": 10000 } } }, "traces": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" }, "logs": { "enabled": true, "protocol": "grpc", "tls": { "insecure": true }, "endpoint": "otel-collector:4317", "service_name": "openviking-server" } } } }Beta Was this translation helpful? Give feedback.
All reactions