dify/api/tests/unit_tests/enterprise/telemetry/test_telemetry_log.py

323 lines
12 KiB
Python

"""Unit tests for enterprise/telemetry/telemetry_log.py."""
from __future__ import annotations
import logging
import uuid
import pytest
# ---------------------------------------------------------------------------
# compute_trace_id_hex
# ---------------------------------------------------------------------------
class TestComputeTraceIdHex:
def setup_method(self) -> None:
# Clear lru_cache between tests to avoid cross-test pollution
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
compute_trace_id_hex.cache_clear()
def test_none_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
assert compute_trace_id_hex(None) == ""
def test_empty_string_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
assert compute_trace_id_hex("") == ""
def test_already_32_hex_chars_returned_as_is(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
hex_id = "a" * 32
assert compute_trace_id_hex(hex_id) == hex_id
def test_valid_uuid_string_converted_to_32_hex(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
uid = "123e4567-e89b-12d3-a456-426614174000"
result = compute_trace_id_hex(uid)
assert len(result) == 32
assert all(ch in "0123456789abcdef" for ch in result)
# Round-trip: int of the UUID should equal the int parsed from result
assert int(result, 16) == uuid.UUID(uid).int
def test_invalid_string_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
assert compute_trace_id_hex("not-a-uuid") == ""
def test_whitespace_stripped(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
uid = " 123e4567-e89b-12d3-a456-426614174000 "
result = compute_trace_id_hex(uid)
assert len(result) == 32
def test_uppercase_uuid_accepted(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
uid = "123E4567-E89B-12D3-A456-426614174000"
result = compute_trace_id_hex(uid)
assert len(result) == 32
def test_result_is_cached(self) -> None:
from enterprise.telemetry.telemetry_log import compute_trace_id_hex
uid = "123e4567-e89b-12d3-a456-426614174000"
r1 = compute_trace_id_hex(uid)
r2 = compute_trace_id_hex(uid)
assert r1 == r2
info = compute_trace_id_hex.cache_info()
assert info.hits >= 1
# ---------------------------------------------------------------------------
# compute_span_id_hex
# ---------------------------------------------------------------------------
class TestComputeSpanIdHex:
def setup_method(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
compute_span_id_hex.cache_clear()
def test_none_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
assert compute_span_id_hex(None) == ""
def test_empty_string_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
assert compute_span_id_hex("") == ""
def test_already_16_hex_chars_returned_as_is(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
hex_id = "abcdef0123456789"
assert compute_span_id_hex(hex_id) == hex_id
def test_valid_uuid_produces_16_hex_span_id(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
uid = "123e4567-e89b-12d3-a456-426614174000"
result = compute_span_id_hex(uid)
assert len(result) == 16
assert all(ch in "0123456789abcdef" for ch in result)
def test_invalid_string_returns_empty(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
assert compute_span_id_hex("not-a-uuid-at-all!") == ""
def test_result_is_cached(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex
uid = "123e4567-e89b-12d3-a456-426614174000"
compute_span_id_hex(uid)
compute_span_id_hex(uid)
info = compute_span_id_hex.cache_info()
assert info.hits >= 1
# ---------------------------------------------------------------------------
# emit_telemetry_log
# ---------------------------------------------------------------------------
class TestEmitTelemetryLog:
def setup_method(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex, compute_trace_id_hex
compute_trace_id_hex.cache_clear()
compute_span_id_hex.cache_clear()
def test_logs_info_with_event_name_and_signal(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(
event_name="dify.workflow.run",
attributes={"tenant_id": "t1"},
signal="metric_only",
)
assert len(caplog.records) == 1
record = caplog.records[0]
assert record.levelno == logging.INFO
assert record.getMessage() == "telemetry.metric_only"
assert hasattr(record, "attributes")
assert record.attributes["dify.event.name"] == "dify.workflow.run"
assert record.attributes["dify.event.signal"] == "metric_only"
assert record.attributes["tenant_id"] == "t1"
def test_no_log_when_info_disabled(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.WARNING, logger="dify.telemetry"):
emit_telemetry_log(event_name="dify.workflow.run", attributes={})
assert len(caplog.records) == 0
def test_trace_id_added_to_extra_when_valid_uuid(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
uid = "123e4567-e89b-12d3-a456-426614174000"
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, trace_id_source=uid)
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "trace_id")
assert len(record.trace_id) == 32
def test_trace_id_absent_when_invalid_source(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, trace_id_source="bad-id")
assert len(caplog.records) == 1
record = caplog.records[0]
assert not hasattr(record, "trace_id")
def test_span_id_added_to_extra_when_valid_uuid(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
uid = "123e4567-e89b-12d3-a456-426614174000"
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, span_id_source=uid)
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "span_id")
assert len(record.span_id) == 16
def test_tenant_id_added_when_provided(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, tenant_id="tenant-99")
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "tenant_id")
assert record.tenant_id == "tenant-99"
def test_user_id_added_when_provided(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, user_id="user-42")
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "user_id")
assert record.user_id == "user-42"
def test_tenant_and_user_id_absent_when_not_provided(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={})
assert len(caplog.records) == 1
record = caplog.records[0]
assert not hasattr(record, "tenant_id")
assert not hasattr(record, "user_id")
def test_caller_attributes_merged_into_attrs(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(
event_name="dify.node.run",
attributes={"node_type": "code", "elapsed": 0.5},
)
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "attributes")
assert record.attributes["node_type"] == "code"
assert record.attributes["elapsed"] == 0.5
def test_signal_span_detail_forwarded(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_telemetry_log
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_telemetry_log(event_name="test.event", attributes={}, signal="span_detail")
assert len(caplog.records) == 1
record = caplog.records[0]
assert record.getMessage() == "telemetry.span_detail"
assert hasattr(record, "attributes")
assert record.attributes["dify.event.signal"] == "span_detail"
# ---------------------------------------------------------------------------
# emit_metric_only_event
# ---------------------------------------------------------------------------
class TestEmitMetricOnlyEvent:
def setup_method(self) -> None:
from enterprise.telemetry.telemetry_log import compute_span_id_hex, compute_trace_id_hex
compute_trace_id_hex.cache_clear()
compute_span_id_hex.cache_clear()
def test_delegates_to_emit_telemetry_log_with_metric_only_signal(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_metric_only_event
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_metric_only_event(
event_name="dify.app.created",
attributes={"app_id": "app-1"},
tenant_id="t1",
user_id="u1",
)
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "attributes")
assert record.attributes["dify.event.signal"] == "metric_only"
assert record.attributes["dify.event.name"] == "dify.app.created"
assert record.attributes["app_id"] == "app-1"
assert hasattr(record, "tenant_id")
assert record.tenant_id == "t1"
assert hasattr(record, "user_id")
assert record.user_id == "u1"
def test_trace_and_span_ids_passed_through(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_metric_only_event
uid = "123e4567-e89b-12d3-a456-426614174000"
with caplog.at_level(logging.INFO, logger="dify.telemetry"):
emit_metric_only_event(
event_name="dify.workflow.run",
attributes={},
trace_id_source=uid,
span_id_source=uid,
)
assert len(caplog.records) == 1
record = caplog.records[0]
assert hasattr(record, "trace_id")
assert hasattr(record, "span_id")
def test_no_log_emitted_when_logger_disabled(self, caplog: pytest.LogCaptureFixture) -> None:
from enterprise.telemetry.telemetry_log import emit_metric_only_event
with caplog.at_level(logging.WARNING, logger="dify.telemetry"):
emit_metric_only_event(event_name="dify.workflow.run", attributes={})
assert len(caplog.records) == 0