mirror of https://github.com/langgenius/dify.git
Merge branch '1.12.1-otel-ee' into deploy/enterprise
This commit is contained in:
commit
4e624af5e0
|
|
@ -32,6 +32,7 @@ from core.ops.entities.trace_entity import (
|
|||
)
|
||||
from enterprise.telemetry.entities import (
|
||||
EnterpriseTelemetryCounter,
|
||||
EnterpriseTelemetryEvent,
|
||||
EnterpriseTelemetryHistogram,
|
||||
EnterpriseTelemetrySpan,
|
||||
)
|
||||
|
|
@ -203,7 +204,7 @@ class EnterpriseOtelTrace:
|
|||
log_attrs["dify.workflow.query"] = self._content_or_ref(info.query, ref)
|
||||
|
||||
emit_telemetry_log(
|
||||
event_name="dify.workflow.run",
|
||||
event_name=EnterpriseTelemetryEvent.WORKFLOW_RUN,
|
||||
attributes=log_attrs,
|
||||
signal="span_detail",
|
||||
trace_id_source=info.workflow_run_id,
|
||||
|
|
@ -448,7 +449,7 @@ class EnterpriseOtelTrace:
|
|||
attrs["dify.message.outputs"] = self._content_or_ref(outputs, ref)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.message.run",
|
||||
event_name=EnterpriseTelemetryEvent.MESSAGE_RUN,
|
||||
attributes=attrs,
|
||||
trace_id_source=metadata.get("workflow_run_id") or str(info.message_id) if info.message_id else None,
|
||||
span_id_source=node_execution_id,
|
||||
|
|
@ -525,7 +526,7 @@ class EnterpriseOtelTrace:
|
|||
attrs["dify.tool.config"] = self._content_or_ref(info.tool_config, ref)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.tool.execution",
|
||||
event_name=EnterpriseTelemetryEvent.TOOL_EXECUTION,
|
||||
attributes=attrs,
|
||||
span_id_source=node_execution_id,
|
||||
tenant_id=tenant_id,
|
||||
|
|
@ -579,7 +580,7 @@ class EnterpriseOtelTrace:
|
|||
)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.moderation.check",
|
||||
event_name=EnterpriseTelemetryEvent.MODERATION_CHECK,
|
||||
attributes=attrs,
|
||||
span_id_source=node_execution_id,
|
||||
tenant_id=tenant_id,
|
||||
|
|
@ -624,7 +625,7 @@ class EnterpriseOtelTrace:
|
|||
)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.suggested_question.generation",
|
||||
event_name=EnterpriseTelemetryEvent.SUGGESTED_QUESTION_GENERATION,
|
||||
attributes=attrs,
|
||||
span_id_source=node_execution_id,
|
||||
tenant_id=tenant_id,
|
||||
|
|
@ -711,7 +712,7 @@ class EnterpriseOtelTrace:
|
|||
attrs["dify.dataset.documents"] = self._content_or_ref(structured_docs, ref)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.dataset.retrieval",
|
||||
event_name=EnterpriseTelemetryEvent.DATASET_RETRIEVAL,
|
||||
attributes=attrs,
|
||||
trace_id_source=metadata.get("workflow_run_id") or str(info.message_id) if info.message_id else None,
|
||||
span_id_source=node_execution_id or (str(info.message_id) if info.message_id else None),
|
||||
|
|
@ -758,7 +759,7 @@ class EnterpriseOtelTrace:
|
|||
attrs["dify.generate_name.outputs"] = self._content_or_ref(outputs, ref)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.generate_name.execution",
|
||||
event_name=EnterpriseTelemetryEvent.GENERATE_NAME_EXECUTION,
|
||||
attributes=attrs,
|
||||
span_id_source=node_execution_id,
|
||||
tenant_id=tenant_id,
|
||||
|
|
@ -811,7 +812,7 @@ class EnterpriseOtelTrace:
|
|||
attrs["dify.prompt_generation.output"] = self._content_or_ref(outputs, ref)
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.prompt_generation.execution",
|
||||
event_name=EnterpriseTelemetryEvent.PROMPT_GENERATION_EXECUTION,
|
||||
attributes=attrs,
|
||||
span_id_source=node_execution_id,
|
||||
tenant_id=tenant_id,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,24 @@ class EnterpriseTelemetrySpan(StrEnum):
|
|||
DRAFT_NODE_EXECUTION = "dify.node.execution.draft"
|
||||
|
||||
|
||||
class EnterpriseTelemetryEvent(StrEnum):
|
||||
"""Event names for enterprise telemetry logs."""
|
||||
|
||||
APP_CREATED = "dify.app.created"
|
||||
APP_UPDATED = "dify.app.updated"
|
||||
APP_DELETED = "dify.app.deleted"
|
||||
FEEDBACK_CREATED = "dify.feedback.created"
|
||||
WORKFLOW_RUN = "dify.workflow.run"
|
||||
MESSAGE_RUN = "dify.message.run"
|
||||
TOOL_EXECUTION = "dify.tool.execution"
|
||||
MODERATION_CHECK = "dify.moderation.check"
|
||||
SUGGESTED_QUESTION_GENERATION = "dify.suggested_question.generation"
|
||||
DATASET_RETRIEVAL = "dify.dataset.retrieval"
|
||||
GENERATE_NAME_EXECUTION = "dify.generate_name.execution"
|
||||
PROMPT_GENERATION_EXECUTION = "dify.prompt_generation.execution"
|
||||
REHYDRATION_FAILED = "dify.telemetry.rehydration_failed"
|
||||
|
||||
|
||||
class EnterpriseTelemetryCounter(StrEnum):
|
||||
TOKENS = "tokens"
|
||||
INPUT_TOKENS = "input_tokens"
|
||||
|
|
@ -15,6 +33,9 @@ class EnterpriseTelemetryCounter(StrEnum):
|
|||
ERRORS = "errors"
|
||||
FEEDBACK = "feedback"
|
||||
DATASET_RETRIEVALS = "dataset_retrievals"
|
||||
APP_CREATED = "app_created"
|
||||
APP_UPDATED = "app_updated"
|
||||
APP_DELETED = "app_deleted"
|
||||
|
||||
|
||||
class EnterpriseTelemetryHistogram(StrEnum):
|
||||
|
|
@ -28,6 +49,7 @@ class EnterpriseTelemetryHistogram(StrEnum):
|
|||
|
||||
__all__ = [
|
||||
"EnterpriseTelemetryCounter",
|
||||
"EnterpriseTelemetryEvent",
|
||||
"EnterpriseTelemetryHistogram",
|
||||
"EnterpriseTelemetrySpan",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ class EnterpriseExporter:
|
|||
EnterpriseTelemetryCounter.DATASET_RETRIEVALS: meter.create_counter(
|
||||
"dify.dataset.retrievals.total", unit="{retrieval}"
|
||||
),
|
||||
EnterpriseTelemetryCounter.APP_CREATED: meter.create_counter("dify.app.created.total", unit="{app}"),
|
||||
EnterpriseTelemetryCounter.APP_UPDATED: meter.create_counter("dify.app.updated.total", unit="{app}"),
|
||||
EnterpriseTelemetryCounter.APP_DELETED: meter.create_counter("dify.app.deleted.total", unit="{app}"),
|
||||
}
|
||||
self._histograms = {
|
||||
EnterpriseTelemetryHistogram.WORKFLOW_DURATION: meter.create_histogram("dify.workflow.duration", unit="s"),
|
||||
|
|
|
|||
|
|
@ -174,10 +174,11 @@ class EnterpriseMetricHandler:
|
|||
envelope.case,
|
||||
)
|
||||
# Emit degraded event marker
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
from enterprise.telemetry.telemetry_log import emit_metric_only_event
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.telemetry.rehydration_failed",
|
||||
event_name=EnterpriseTelemetryEvent.REHYDRATION_FAILED,
|
||||
attributes={
|
||||
"dify.tenant_id": envelope.tenant_id,
|
||||
"dify.event_id": envelope.event_id,
|
||||
|
|
@ -196,7 +197,7 @@ class EnterpriseMetricHandler:
|
|||
|
||||
def _on_app_created(self, envelope: TelemetryEnvelope) -> None:
|
||||
"""Handle app created event."""
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter, EnterpriseTelemetryEvent
|
||||
from enterprise.telemetry.telemetry_log import emit_metric_only_event
|
||||
from extensions.ext_enterprise_telemetry import get_enterprise_exporter
|
||||
|
||||
|
|
@ -216,22 +217,23 @@ class EnterpriseMetricHandler:
|
|||
}
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.app.created",
|
||||
event_name=EnterpriseTelemetryEvent.APP_CREATED,
|
||||
attributes=attrs,
|
||||
tenant_id=envelope.tenant_id,
|
||||
)
|
||||
exporter.increment_counter(
|
||||
EnterpriseTelemetryCounter.REQUESTS,
|
||||
EnterpriseTelemetryCounter.APP_CREATED,
|
||||
1,
|
||||
{
|
||||
"type": "app.created",
|
||||
"tenant_id": envelope.tenant_id,
|
||||
"app_id": str(payload.get("app_id", "")),
|
||||
"mode": str(payload.get("mode", "")),
|
||||
},
|
||||
)
|
||||
|
||||
def _on_app_updated(self, envelope: TelemetryEnvelope) -> None:
|
||||
"""Handle app updated event."""
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter, EnterpriseTelemetryEvent
|
||||
from enterprise.telemetry.telemetry_log import emit_metric_only_event
|
||||
from extensions.ext_enterprise_telemetry import get_enterprise_exporter
|
||||
|
||||
|
|
@ -250,22 +252,22 @@ class EnterpriseMetricHandler:
|
|||
}
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.app.updated",
|
||||
event_name=EnterpriseTelemetryEvent.APP_UPDATED,
|
||||
attributes=attrs,
|
||||
tenant_id=envelope.tenant_id,
|
||||
)
|
||||
exporter.increment_counter(
|
||||
EnterpriseTelemetryCounter.REQUESTS,
|
||||
EnterpriseTelemetryCounter.APP_UPDATED,
|
||||
1,
|
||||
{
|
||||
"type": "app.updated",
|
||||
"tenant_id": envelope.tenant_id,
|
||||
"app_id": str(payload.get("app_id", "")),
|
||||
},
|
||||
)
|
||||
|
||||
def _on_app_deleted(self, envelope: TelemetryEnvelope) -> None:
|
||||
"""Handle app deleted event."""
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter, EnterpriseTelemetryEvent
|
||||
from enterprise.telemetry.telemetry_log import emit_metric_only_event
|
||||
from extensions.ext_enterprise_telemetry import get_enterprise_exporter
|
||||
|
||||
|
|
@ -284,22 +286,22 @@ class EnterpriseMetricHandler:
|
|||
}
|
||||
|
||||
emit_metric_only_event(
|
||||
event_name="dify.app.deleted",
|
||||
event_name=EnterpriseTelemetryEvent.APP_DELETED,
|
||||
attributes=attrs,
|
||||
tenant_id=envelope.tenant_id,
|
||||
)
|
||||
exporter.increment_counter(
|
||||
EnterpriseTelemetryCounter.REQUESTS,
|
||||
EnterpriseTelemetryCounter.APP_DELETED,
|
||||
1,
|
||||
{
|
||||
"type": "app.deleted",
|
||||
"tenant_id": envelope.tenant_id,
|
||||
"app_id": str(payload.get("app_id", "")),
|
||||
},
|
||||
)
|
||||
|
||||
def _on_feedback_created(self, envelope: TelemetryEnvelope) -> None:
|
||||
"""Handle feedback created event."""
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter, EnterpriseTelemetryEvent
|
||||
from enterprise.telemetry.telemetry_log import emit_metric_only_event
|
||||
from extensions.ext_enterprise_telemetry import get_enterprise_exporter
|
||||
|
||||
|
|
@ -327,7 +329,7 @@ class EnterpriseMetricHandler:
|
|||
|
||||
user_id = payload.get("from_end_user_id") or payload.get("from_account_id")
|
||||
emit_metric_only_event(
|
||||
event_name="dify.feedback.created",
|
||||
event_name=EnterpriseTelemetryEvent.FEEDBACK_CREATED,
|
||||
attributes=attrs,
|
||||
tenant_id=envelope.tenant_id,
|
||||
user_id=str(user_id or ""),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ from __future__ import annotations
|
|||
import logging
|
||||
import uuid
|
||||
from functools import lru_cache
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
|
||||
logger = logging.getLogger("dify.telemetry")
|
||||
|
||||
|
|
@ -48,7 +51,7 @@ def compute_span_id_hex(uuid_str: str | None) -> str:
|
|||
|
||||
def emit_telemetry_log(
|
||||
*,
|
||||
event_name: str,
|
||||
event_name: str | EnterpriseTelemetryEvent,
|
||||
attributes: dict[str, Any],
|
||||
signal: str = "metric_only",
|
||||
trace_id_source: str | None = None,
|
||||
|
|
@ -101,7 +104,7 @@ def emit_telemetry_log(
|
|||
|
||||
def emit_metric_only_event(
|
||||
*,
|
||||
event_name: str,
|
||||
event_name: str | EnterpriseTelemetryEvent,
|
||||
attributes: dict[str, Any],
|
||||
trace_id_source: str | None = None,
|
||||
span_id_source: str | None = None,
|
||||
|
|
|
|||
|
|
@ -269,10 +269,12 @@ def test_rehydration_emits_degraded_event_on_failure():
|
|||
with patch("enterprise.telemetry.telemetry_log.emit_metric_only_event") as mock_emit:
|
||||
payload = handler._rehydrate(envelope)
|
||||
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
|
||||
assert payload == {}
|
||||
mock_emit.assert_called_once()
|
||||
call_args = mock_emit.call_args
|
||||
assert call_args[1]["event_name"] == "dify.telemetry.rehydration_failed"
|
||||
assert call_args[1]["event_name"] == EnterpriseTelemetryEvent.REHYDRATION_FAILED
|
||||
assert call_args[1]["attributes"]["rehydration_failed"] is True
|
||||
|
||||
|
||||
|
|
@ -295,8 +297,10 @@ def test_on_app_created_emits_correct_event(mock_redis):
|
|||
|
||||
handler._on_app_created(envelope)
|
||||
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
|
||||
mock_emit.assert_called_once_with(
|
||||
event_name="dify.app.created",
|
||||
event_name=EnterpriseTelemetryEvent.APP_CREATED,
|
||||
attributes={
|
||||
"dify.app.id": "app-789",
|
||||
"dify.tenant_id": "tenant-123",
|
||||
|
|
@ -304,11 +308,15 @@ def test_on_app_created_emits_correct_event(mock_redis):
|
|||
},
|
||||
tenant_id="tenant-123",
|
||||
)
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
|
||||
mock_exporter.increment_counter.assert_called_once()
|
||||
call_args = mock_exporter.increment_counter.call_args
|
||||
assert call_args[0][0] == EnterpriseTelemetryCounter.APP_CREATED
|
||||
assert call_args[0][1] == 1
|
||||
assert call_args[0][2]["type"] == "app.created"
|
||||
assert call_args[0][2]["tenant_id"] == "tenant-123"
|
||||
assert call_args[0][2]["app_id"] == "app-789"
|
||||
assert call_args[0][2]["mode"] == "chat"
|
||||
|
||||
|
||||
def test_on_app_updated_emits_correct_event(mock_redis):
|
||||
|
|
@ -330,17 +338,24 @@ def test_on_app_updated_emits_correct_event(mock_redis):
|
|||
|
||||
handler._on_app_updated(envelope)
|
||||
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
|
||||
mock_emit.assert_called_once_with(
|
||||
event_name="dify.app.updated",
|
||||
event_name=EnterpriseTelemetryEvent.APP_UPDATED,
|
||||
attributes={
|
||||
"dify.app.id": "app-789",
|
||||
"dify.tenant_id": "tenant-123",
|
||||
},
|
||||
tenant_id="tenant-123",
|
||||
)
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
|
||||
mock_exporter.increment_counter.assert_called_once()
|
||||
call_args = mock_exporter.increment_counter.call_args
|
||||
assert call_args[0][2]["type"] == "app.updated"
|
||||
assert call_args[0][0] == EnterpriseTelemetryCounter.APP_UPDATED
|
||||
assert call_args[0][1] == 1
|
||||
assert call_args[0][2]["tenant_id"] == "tenant-123"
|
||||
assert call_args[0][2]["app_id"] == "app-789"
|
||||
|
||||
|
||||
def test_on_app_deleted_emits_correct_event(mock_redis):
|
||||
|
|
@ -362,17 +377,24 @@ def test_on_app_deleted_emits_correct_event(mock_redis):
|
|||
|
||||
handler._on_app_deleted(envelope)
|
||||
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryEvent
|
||||
|
||||
mock_emit.assert_called_once_with(
|
||||
event_name="dify.app.deleted",
|
||||
event_name=EnterpriseTelemetryEvent.APP_DELETED,
|
||||
attributes={
|
||||
"dify.app.id": "app-789",
|
||||
"dify.tenant_id": "tenant-123",
|
||||
},
|
||||
tenant_id="tenant-123",
|
||||
)
|
||||
from enterprise.telemetry.entities import EnterpriseTelemetryCounter
|
||||
|
||||
mock_exporter.increment_counter.assert_called_once()
|
||||
call_args = mock_exporter.increment_counter.call_args
|
||||
assert call_args[0][2]["type"] == "app.deleted"
|
||||
assert call_args[0][0] == EnterpriseTelemetryCounter.APP_DELETED
|
||||
assert call_args[0][1] == 1
|
||||
assert call_args[0][2]["tenant_id"] == "tenant-123"
|
||||
assert call_args[0][2]["app_id"] == "app-789"
|
||||
|
||||
|
||||
def test_on_feedback_created_emits_correct_event(mock_redis):
|
||||
|
|
|
|||
Loading…
Reference in New Issue