haystack/test/tracing/test_tracer.py

174 lines
6.2 KiB
Python
Raw Normal View History

# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
#
# SPDX-License-Identifier: Apache-2.0
import sys
from unittest.mock import Mock, patch
import ddtrace
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.datadog import DatadogTracer
from haystack.tracing.opentelemetry import OpenTelemetryTracer
from haystack.tracing.tracer import (
NullTracer,
NullSpan,
_auto_configured_opentelemetry_tracer,
_auto_configured_datadog_tracer,
enable_tracing,
Tracer,
disable_tracing,
is_tracing_enabled,
ProxyTracer,
auto_enable_tracing,
tracer,
HAYSTACK_CONTENT_TRACING_ENABLED_ENV_VAR,
)
from test.tracing.utils import SpyingTracer, SpyingSpan
class TestNullTracer:
def test_tracing(self) -> None:
assert isinstance(tracer.actual_tracer, NullTracer)
# None of this raises
with tracer.trace("operation", {"key": "value"}, parent_span=None) 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)
parent_span = Mock(spec=SpyingSpan)
with my_tracer.trace("operation", {"key": "value"}, parent_span=parent_span) 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].parent_span == parent_span
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
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 unset `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_add_datadog_tracer(self) -> None:
auto_enable_tracing()
activated_tracer = tracer.actual_tracer
assert isinstance(activated_tracer, DatadogTracer)
assert is_tracing_enabled()
def test__auto_configured_opentelemetry_tracer(self, configured_opentelemetry_tracing):
tracer = _auto_configured_opentelemetry_tracer()
assert isinstance(tracer, OpenTelemetryTracer)
def test__auto_configured_opentelemetry_tracer_with_failing_import(self, monkeypatch):
monkeypatch.delitem(sys.modules, "opentelemetry.trace", raising=False)
tracer = _auto_configured_opentelemetry_tracer()
assert tracer is None
def test__auto_configured_datadog_tracer(self):
tracer = _auto_configured_datadog_tracer()
assert isinstance(tracer, DatadogTracer)
def test__auto_configured_datadog_tracer_with_failing_import(self, monkeypatch):
monkeypatch.setattr(ddtrace.tracer, "enabled", False)
tracer = _auto_configured_datadog_tracer()
assert tracer is None
class TestTracingContent:
fix: pipeline run bugs in cyclic and acyclic pipelines (#8707) * add component checks * pipeline should run deterministically * add FIFOQueue * add agent tests * add order dependent tests * run new tests * remove code that is not needed * test: intermediate from cycle outputs are available outside cycle * add tests for component checks (Claude) * adapt tests for component checks (o1 review) * chore: format * remove tests that aren't needed anymore * add _calculate_priority tests * revert accidental change in pyproject.toml * test format conversion * adapt to naming convention * chore: proper docstrings and type hints for PQ * format * add more unit tests * rm unneeded comments * test input consumption * lint * fix: docstrings * lint * format * format * fix license header * fix license header * add component run tests * fix: pass correct input format to tracing * fix types * format * format * types * add defaults from Socket instead of signature - otherwise components with dynamic inputs would fail * fix test names * still wait for optional inputs on greedy variadic sockets - mirrors previous behavior * fix format * wip: warn for ambiguous running order * wip: alternative warning * fix license header * make code more readable Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * Introduce content tracing to a behavioral test * Fixing linting * Remove debug print statements * Fix tracer tests * remove print * test: test for component inputs * test: remove testing for run order * chore: update component checks from experimental * chore: update pipeline and base from experimental * refactor: remove unused method * refactor: remove unused method * refactor: outdated comment * refactor: inputs state is updated as side effect - to prepare for AsyncPipeline implementation * format * test: add file conversion test * format * fix: original implementation deepcopies outputs * lint * fix: from_dict was updated * fix: format * fix: test * test: add test for thread safety * remove unused imports * format * test: FIFOPriorityQueue * chore: add release note * fix: resolve merge conflict with mermaid changes * fix: format * fix: remove unused import * refactor: rename to avoid accidental conflicts * chore: remove unused inputs, add missing license header * chore: extend release notes * Update releasenotes/notes/fix-pipeline-run-2fefeafc705a6d91.yaml Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * fix: format * fix: format * Update release note --------- Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> Co-authored-by: David S. Batista <dsbatista@gmail.com>
2025-02-06 15:19:47 +01:00
def test_set_content_tag_with_enabled_content_tracing(self, spying_tracer: SpyingTracer) -> None:
# SpyingTracer supports content tracing by default
enable_tracing(spying_tracer)
with tracer.trace("test") as span:
span.set_content_tag("my_content", "my_content")
assert len(spying_tracer.spans) == 1
span = spying_tracer.spans[0]
assert span.tags == {"my_content": "my_content"}
fix: pipeline run bugs in cyclic and acyclic pipelines (#8707) * add component checks * pipeline should run deterministically * add FIFOQueue * add agent tests * add order dependent tests * run new tests * remove code that is not needed * test: intermediate from cycle outputs are available outside cycle * add tests for component checks (Claude) * adapt tests for component checks (o1 review) * chore: format * remove tests that aren't needed anymore * add _calculate_priority tests * revert accidental change in pyproject.toml * test format conversion * adapt to naming convention * chore: proper docstrings and type hints for PQ * format * add more unit tests * rm unneeded comments * test input consumption * lint * fix: docstrings * lint * format * format * fix license header * fix license header * add component run tests * fix: pass correct input format to tracing * fix types * format * format * types * add defaults from Socket instead of signature - otherwise components with dynamic inputs would fail * fix test names * still wait for optional inputs on greedy variadic sockets - mirrors previous behavior * fix format * wip: warn for ambiguous running order * wip: alternative warning * fix license header * make code more readable Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * Introduce content tracing to a behavioral test * Fixing linting * Remove debug print statements * Fix tracer tests * remove print * test: test for component inputs * test: remove testing for run order * chore: update component checks from experimental * chore: update pipeline and base from experimental * refactor: remove unused method * refactor: remove unused method * refactor: outdated comment * refactor: inputs state is updated as side effect - to prepare for AsyncPipeline implementation * format * test: add file conversion test * format * fix: original implementation deepcopies outputs * lint * fix: from_dict was updated * fix: format * fix: test * test: add test for thread safety * remove unused imports * format * test: FIFOPriorityQueue * chore: add release note * fix: resolve merge conflict with mermaid changes * fix: format * fix: remove unused import * refactor: rename to avoid accidental conflicts * chore: remove unused inputs, add missing license header * chore: extend release notes * Update releasenotes/notes/fix-pipeline-run-2fefeafc705a6d91.yaml Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * fix: format * fix: format * Update release note --------- Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> Co-authored-by: David S. Batista <dsbatista@gmail.com>
2025-02-06 15:19:47 +01:00
def test_set_content_tag_when_disabled_via_env_variable(self, monkeypatch: MonkeyPatch) -> None:
# we test if content tracing is disabled when the env variable is set to false
monkeypatch.setenv(HAYSTACK_CONTENT_TRACING_ENABLED_ENV_VAR, "false")
proxy_tracer = ProxyTracer(provided_tracer=SpyingTracer())
fix: pipeline run bugs in cyclic and acyclic pipelines (#8707) * add component checks * pipeline should run deterministically * add FIFOQueue * add agent tests * add order dependent tests * run new tests * remove code that is not needed * test: intermediate from cycle outputs are available outside cycle * add tests for component checks (Claude) * adapt tests for component checks (o1 review) * chore: format * remove tests that aren't needed anymore * add _calculate_priority tests * revert accidental change in pyproject.toml * test format conversion * adapt to naming convention * chore: proper docstrings and type hints for PQ * format * add more unit tests * rm unneeded comments * test input consumption * lint * fix: docstrings * lint * format * format * fix license header * fix license header * add component run tests * fix: pass correct input format to tracing * fix types * format * format * types * add defaults from Socket instead of signature - otherwise components with dynamic inputs would fail * fix test names * still wait for optional inputs on greedy variadic sockets - mirrors previous behavior * fix format * wip: warn for ambiguous running order * wip: alternative warning * fix license header * make code more readable Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * Introduce content tracing to a behavioral test * Fixing linting * Remove debug print statements * Fix tracer tests * remove print * test: test for component inputs * test: remove testing for run order * chore: update component checks from experimental * chore: update pipeline and base from experimental * refactor: remove unused method * refactor: remove unused method * refactor: outdated comment * refactor: inputs state is updated as side effect - to prepare for AsyncPipeline implementation * format * test: add file conversion test * format * fix: original implementation deepcopies outputs * lint * fix: from_dict was updated * fix: format * fix: test * test: add test for thread safety * remove unused imports * format * test: FIFOPriorityQueue * chore: add release note * fix: resolve merge conflict with mermaid changes * fix: format * fix: remove unused import * refactor: rename to avoid accidental conflicts * chore: remove unused inputs, add missing license header * chore: extend release notes * Update releasenotes/notes/fix-pipeline-run-2fefeafc705a6d91.yaml Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> * fix: format * fix: format * Update release note --------- Co-authored-by: Amna Mubashar <amnahkhan.ak@gmail.com> Co-authored-by: David S. Batista <dsbatista@gmail.com>
2025-02-06 15:19:47 +01:00
assert proxy_tracer.is_content_tracing_enabled is False