2024-02-22 14:30:58 +01:00
|
|
|
import builtins
|
|
|
|
import sys
|
2024-02-22 12:52:04 +01:00
|
|
|
from unittest.mock import Mock
|
|
|
|
|
2024-02-22 14:30:58 +01:00
|
|
|
import opentelemetry.trace
|
|
|
|
import pytest
|
|
|
|
from _pytest.monkeypatch import MonkeyPatch
|
|
|
|
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
|
|
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
|
|
from opentelemetry.sdk.trace import TracerProvider
|
|
|
|
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
|
|
|
|
|
|
|
from haystack.tracing.opentelemetry import OpenTelemetryTracer
|
2024-02-22 12:52:04 +01:00
|
|
|
from haystack.tracing.tracer import (
|
|
|
|
NullTracer,
|
|
|
|
NullSpan,
|
|
|
|
enable_tracing,
|
|
|
|
Tracer,
|
|
|
|
disable_tracing,
|
|
|
|
is_tracing_enabled,
|
|
|
|
ProxyTracer,
|
2024-02-22 14:30:58 +01:00
|
|
|
auto_enable_tracing,
|
2024-02-22 12:52:04 +01:00
|
|
|
tracer,
|
|
|
|
)
|
|
|
|
from test.tracing.utils import SpyingTracer
|
|
|
|
|
|
|
|
|
|
|
|
class TestNullTracer:
|
|
|
|
def test_tracing(self) -> None:
|
|
|
|
assert isinstance(tracer.actual_tracer, NullTracer)
|
|
|
|
|
|
|
|
# None of this raises
|
|
|
|
with tracer.trace("operation", {"key": "value"}) as span:
|
|
|
|
span.set_tag("key", "value")
|
|
|
|
span.set_tags({"key": "value"})
|
|
|
|
|
|
|
|
assert isinstance(tracer.current_span(), NullSpan)
|
|
|
|
assert isinstance(tracer.current_span().raw_span(), NullSpan)
|
|
|
|
|
|
|
|
|
|
|
|
class TestProxyTracer:
|
|
|
|
def test_tracing(self) -> None:
|
|
|
|
spying_tracer = SpyingTracer()
|
|
|
|
my_tracer = ProxyTracer(provided_tracer=spying_tracer)
|
|
|
|
|
|
|
|
enable_tracing(spying_tracer)
|
|
|
|
|
|
|
|
with my_tracer.trace("operation", {"key": "value"}) as span:
|
|
|
|
span.set_tag("key", "value")
|
|
|
|
span.set_tags({"key2": "value2"})
|
|
|
|
|
|
|
|
assert len(spying_tracer.spans) == 1
|
|
|
|
assert spying_tracer.spans[0].operation_name == "operation"
|
|
|
|
assert spying_tracer.spans[0].tags == {"key": "value", "key2": "value2"}
|
|
|
|
|
|
|
|
|
|
|
|
class TestConfigureTracer:
|
|
|
|
def test_enable_tracer(self) -> None:
|
|
|
|
my_tracer = Mock(spec=Tracer) # anything else than `NullTracer` works for this test
|
|
|
|
|
|
|
|
enable_tracing(my_tracer)
|
|
|
|
|
|
|
|
assert isinstance(tracer, ProxyTracer)
|
|
|
|
assert tracer.actual_tracer is my_tracer
|
|
|
|
assert is_tracing_enabled()
|
|
|
|
|
|
|
|
def test_disable_tracing(self) -> None:
|
|
|
|
my_tracker = Mock(spec=Tracer) # anything else than `NullTracer` works for this test
|
|
|
|
|
|
|
|
enable_tracing(my_tracker)
|
|
|
|
assert tracer.actual_tracer is my_tracker
|
|
|
|
|
|
|
|
disable_tracing()
|
|
|
|
assert isinstance(tracer.actual_tracer, NullTracer)
|
|
|
|
assert is_tracing_enabled() is False
|
2024-02-22 14:30:58 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestAutoEnableTracer:
|
|
|
|
@pytest.fixture()
|
|
|
|
def configured_opentelemetry_tracing(self) -> None:
|
|
|
|
resource = Resource(attributes={SERVICE_NAME: "haystack-testing"})
|
|
|
|
|
|
|
|
traceProvider = TracerProvider(resource=resource)
|
|
|
|
processor = SimpleSpanProcessor(InMemorySpanExporter())
|
|
|
|
traceProvider.add_span_processor(processor)
|
|
|
|
|
|
|
|
# We can't uset `set_tracer_provider` here, because opentelemetry has a lock to only set it once
|
|
|
|
opentelemetry.trace._TRACER_PROVIDER = traceProvider
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
# unfortunately, there's no cleaner way to reset the global tracer provider
|
|
|
|
opentelemetry.trace._TRACER_PROVIDER = None
|
|
|
|
disable_tracing()
|
|
|
|
|
|
|
|
def test_skip_auto_enable_tracer_if_already_configured(self) -> None:
|
|
|
|
my_tracker = Mock(spec=Tracer) # anything else than `NullTracer` works for this test
|
|
|
|
enable_tracing(my_tracker)
|
|
|
|
|
|
|
|
auto_enable_tracing()
|
|
|
|
|
|
|
|
assert tracer.actual_tracer is my_tracker
|
|
|
|
|
|
|
|
def test_skip_auto_enable_if_tracing_disabled_via_env(
|
|
|
|
self, monkeypatch: MonkeyPatch, configured_opentelemetry_tracing: None
|
|
|
|
) -> None:
|
|
|
|
monkeypatch.setenv("HAYSTACK_AUTO_TRACE_ENABLED", "false")
|
|
|
|
|
|
|
|
old_tracer = tracer.actual_tracer
|
|
|
|
|
|
|
|
auto_enable_tracing()
|
|
|
|
|
|
|
|
assert tracer.actual_tracer is old_tracer
|
|
|
|
|
|
|
|
def test_enable_opentelemetry_tracer(self, configured_opentelemetry_tracing: None) -> None:
|
|
|
|
auto_enable_tracing()
|
|
|
|
|
|
|
|
activated_tracer = tracer.actual_tracer
|
|
|
|
assert isinstance(activated_tracer, OpenTelemetryTracer)
|
|
|
|
assert is_tracing_enabled()
|
|
|
|
|
|
|
|
def test_skip_enable_opentelemetry_tracer_if_import_error(self, monkeypatch: MonkeyPatch) -> None:
|
|
|
|
monkeypatch.delitem(sys.modules, "opentelemetry", raising=False)
|
|
|
|
monkeypatch.setattr(builtins, "__import__", Mock(side_effect=ImportError))
|
|
|
|
auto_enable_tracing()
|
|
|
|
|
|
|
|
activated_tracer = tracer.actual_tracer
|
|
|
|
assert isinstance(activated_tracer, NullTracer)
|
|
|
|
assert not is_tracing_enabled()
|