diff --git a/api/core/ops/entities/trace_entity.py b/api/core/ops/entities/trace_entity.py index 2eb745aeb9..aee08affbd 100644 --- a/api/core/ops/entities/trace_entity.py +++ b/api/core/ops/entities/trace_entity.py @@ -27,6 +27,26 @@ class BaseTraceInfo(BaseModel): model_config = ConfigDict(protected_namespaces=()) + @property + def resolved_trace_id(self) -> str | None: + """Get trace_id with intelligent fallback. + + Priority: + 1. External trace_id (from X-Trace-Id header) + 2. workflow_run_id (if this trace type has it) + 3. message_id (as final fallback) + """ + if self.trace_id: + return self.trace_id + + # Try workflow_run_id (only exists on workflow-related traces) + workflow_run_id = getattr(self, "workflow_run_id", None) + if workflow_run_id: + return workflow_run_id + + # Final fallback to message_id + return str(self.message_id) if self.message_id else None + @field_serializer("start_time", "end_time") def serialize_datetime(self, dt: datetime | None) -> str | None: if dt is None: diff --git a/api/enterprise/telemetry/enterprise_trace.py b/api/enterprise/telemetry/enterprise_trace.py index 214ab9cd29..4370ede15e 100644 --- a/api/enterprise/telemetry/enterprise_trace.py +++ b/api/enterprise/telemetry/enterprise_trace.py @@ -102,7 +102,7 @@ class EnterpriseOtelTrace: metadata = self._metadata(trace_info) tenant_id, app_id, user_id = self._context_ids(trace_info, metadata) return { - "dify.trace_id": trace_info.trace_id, + "dify.trace_id": trace_info.resolved_trace_id, "dify.tenant_id": tenant_id, "dify.app_id": app_id, "dify.app.name": metadata.get("app_name"), @@ -163,7 +163,7 @@ class EnterpriseOtelTrace: tenant_id, app_id, user_id = self._context_ids(info, metadata) # -- Slim span attrs: identity + structure + status + timing only -- span_attrs: dict[str, Any] = { - "dify.trace_id": info.trace_id, + "dify.trace_id": info.resolved_trace_id, "dify.tenant_id": tenant_id, "dify.app_id": app_id, "dify.workflow.id": info.workflow_id, @@ -307,7 +307,7 @@ class EnterpriseOtelTrace: tenant_id, app_id, user_id = self._context_ids(info, metadata) # -- Slim span attrs: identity + structure + status + timing -- span_attrs: dict[str, Any] = { - "dify.trace_id": info.trace_id, + "dify.trace_id": info.resolved_trace_id, "dify.tenant_id": tenant_id, "dify.app_id": app_id, "dify.workflow.id": info.workflow_id, @@ -563,6 +563,7 @@ class EnterpriseOtelTrace: emit_metric_only_event( event_name=EnterpriseTelemetryEvent.TOOL_EXECUTION, attributes=attrs, + trace_id_source=info.resolved_trace_id, span_id_source=node_execution_id, tenant_id=tenant_id, user_id=user_id, @@ -617,6 +618,7 @@ class EnterpriseOtelTrace: emit_metric_only_event( event_name=EnterpriseTelemetryEvent.MODERATION_CHECK, attributes=attrs, + trace_id_source=info.resolved_trace_id, span_id_source=node_execution_id, tenant_id=tenant_id, user_id=user_id, @@ -662,6 +664,7 @@ class EnterpriseOtelTrace: emit_metric_only_event( event_name=EnterpriseTelemetryEvent.SUGGESTED_QUESTION_GENERATION, attributes=attrs, + trace_id_source=info.resolved_trace_id, span_id_source=node_execution_id, tenant_id=tenant_id, user_id=user_id, @@ -818,6 +821,7 @@ class EnterpriseOtelTrace: emit_metric_only_event( event_name=EnterpriseTelemetryEvent.GENERATE_NAME_EXECUTION, attributes=attrs, + trace_id_source=info.resolved_trace_id, span_id_source=node_execution_id, tenant_id=tenant_id, user_id=user_id, @@ -840,7 +844,7 @@ class EnterpriseOtelTrace: metadata = self._metadata(info) tenant_id, app_id, user_id = self._context_ids(info, metadata) attrs = { - "dify.trace_id": info.trace_id, + "dify.trace_id": info.resolved_trace_id, "dify.tenant_id": tenant_id, "dify.user.id": user_id, "dify.app.id": app_id or "", @@ -871,6 +875,7 @@ class EnterpriseOtelTrace: emit_metric_only_event( event_name=EnterpriseTelemetryEvent.PROMPT_GENERATION_EXECUTION, attributes=attrs, + trace_id_source=info.resolved_trace_id, span_id_source=node_execution_id, tenant_id=tenant_id, user_id=user_id,