Добавлен сбор телеметрии на ВМ2
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.sdk.trace import ReadableSpan
|
||||
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
||||
from opentelemetry.trace import NonRecordingSpan, SpanContext, TraceFlags, TraceState
|
||||
|
||||
from app import telemetry
|
||||
|
||||
|
||||
def test_telemetry_is_fail_open_without_endpoint(monkeypatch) -> None:
|
||||
monkeypatch.delenv("OTEL_EXPORTER_OTLP_ENDPOINT", raising=False)
|
||||
monkeypatch.setattr(telemetry, "_runtime", None)
|
||||
|
||||
assert telemetry.init_telemetry("api") is None
|
||||
|
||||
|
||||
def test_resource_uses_canonical_namespace(monkeypatch) -> None:
|
||||
monkeypatch.setenv("APP_ENV", "test")
|
||||
resource = telemetry._resource("worker")
|
||||
|
||||
assert resource.attributes["service.name"] == "message-safety"
|
||||
assert resource.attributes["service.namespace"] == "han-chat"
|
||||
assert resource.attributes["deployment.environment"] == "test"
|
||||
assert resource.attributes["process.role"] == "worker"
|
||||
|
||||
|
||||
def test_json_stdout_redacts_sensitive_data_and_adds_trace_context() -> None:
|
||||
record = logging.LogRecord(
|
||||
"message_safety",
|
||||
logging.INFO,
|
||||
__file__,
|
||||
1,
|
||||
"Bearer top-secret user@example.org +79991234567 https://host/path?token=x",
|
||||
(),
|
||||
None,
|
||||
)
|
||||
record.event = "redaction_canary"
|
||||
record.authorization = "secret"
|
||||
telemetry.RedactionFilter().filter(record)
|
||||
context = SpanContext(
|
||||
trace_id=1,
|
||||
span_id=2,
|
||||
is_remote=False,
|
||||
trace_flags=TraceFlags(1),
|
||||
trace_state=TraceState(),
|
||||
)
|
||||
with trace.use_span(NonRecordingSpan(context)):
|
||||
payload = json.loads(telemetry.JsonFormatter().format(record))
|
||||
|
||||
serialized = json.dumps(payload)
|
||||
assert "top-secret" not in serialized
|
||||
assert "user@example.org" not in serialized
|
||||
assert "79991234567" not in serialized
|
||||
assert "?token=x" not in serialized
|
||||
assert payload["trace_id"] == f"{1:032x}"
|
||||
assert payload["span_id"] == f"{2:016x}"
|
||||
assert payload["service.name"] == "message-safety"
|
||||
|
||||
|
||||
def test_span_attributes_are_redacted_before_export() -> None:
|
||||
attributes = {
|
||||
"url.full": "https://storage.example/private?token=secret",
|
||||
"db.statement": "SELECT private_value FROM safety_tasks",
|
||||
"safe.outcome": "allow",
|
||||
}
|
||||
|
||||
telemetry._redact_span_attributes(attributes)
|
||||
|
||||
assert attributes["url.full"] == "[REDACTED]"
|
||||
assert attributes["db.statement"] == "[REDACTED]"
|
||||
assert attributes["safe.outcome"] == "allow"
|
||||
|
||||
|
||||
def test_span_processor_replaces_immutable_sdk_attributes() -> None:
|
||||
exporter = InMemorySpanExporter()
|
||||
processor = telemetry.RedactingBatchSpanProcessor(exporter)
|
||||
span = ReadableSpan(
|
||||
name="database.connect",
|
||||
attributes={"db.statement": "SELECT secret", "safe.outcome": "allow"},
|
||||
)
|
||||
|
||||
processor.on_end(span)
|
||||
processor.shutdown()
|
||||
|
||||
assert span.attributes["db.statement"] == "[REDACTED]"
|
||||
assert span.attributes["safe.outcome"] == "allow"
|
||||
|
||||
|
||||
def test_task_link_rebuilds_valid_remote_context() -> None:
|
||||
task = SimpleNamespace(
|
||||
origin_trace_id=(123).to_bytes(16, "big"),
|
||||
origin_span_id=(456).to_bytes(8, "big"),
|
||||
origin_trace_flags=1,
|
||||
origin_tracestate=None,
|
||||
)
|
||||
|
||||
link = telemetry.task_link(task)
|
||||
|
||||
assert link is not None
|
||||
assert link.context.is_remote
|
||||
assert link.context.trace_id == 123
|
||||
assert link.context.span_id == 456
|
||||
|
||||
|
||||
def test_current_trace_fields_round_trip_into_task_link() -> None:
|
||||
context = SpanContext(
|
||||
trace_id=123,
|
||||
span_id=456,
|
||||
is_remote=False,
|
||||
trace_flags=TraceFlags(1),
|
||||
trace_state=TraceState(),
|
||||
)
|
||||
with trace.use_span(NonRecordingSpan(context)):
|
||||
trace_id, span_id, flags, tracestate = telemetry.current_trace_fields()
|
||||
|
||||
link = telemetry.task_link(
|
||||
SimpleNamespace(
|
||||
origin_trace_id=trace_id,
|
||||
origin_span_id=span_id,
|
||||
origin_trace_flags=flags,
|
||||
origin_tracestate=tracestate,
|
||||
)
|
||||
)
|
||||
assert link is not None
|
||||
assert link.context.trace_id == context.trace_id
|
||||
assert link.context.span_id == context.span_id
|
||||
Reference in New Issue
Block a user