From 6448690fcf6c0f7434d6a0f94df861f715d17ef5 Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 20 Mar 2024 11:05:38 +0000 Subject: [PATCH] Test the setup_default_tracing() This resulted in some fix to the configuration too, as consuming OTEL_SERVICE_NAME was not being done correctly It does involve some messy reaching into otel internals though --- jobrunner/tracing.py | 43 +++++++++++++++++++-------------- tests/conftest.py | 6 +++-- tests/test_tracing.py | 55 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/jobrunner/tracing.py b/jobrunner/tracing.py index 52c9bc6f..edceb8f2 100644 --- a/jobrunner/tracing.py +++ b/jobrunner/tracing.py @@ -17,19 +17,20 @@ logger = logging.getLogger(__name__) -# https://github.com/open-telemetry/semantic-conventions/tree/main/docs/resource#service -resource = Resource.create( - attributes={ - "service.name": "jobrunner", - "service.namespace": os.environ.get("BACKEND", "unknown"), - "service.version": config.VERSION, - } -) -provider = TracerProvider(resource=resource) -trace.set_tracer_provider(provider) - - -def add_exporter(exporter, processor=BatchSpanProcessor): + +def get_provider(): + # https://github.com/open-telemetry/semantic-conventions/tree/main/docs/resource#service + resource = Resource.create( + attributes={ + "service.name": os.environ.get("OTEL_SERVICE_NAME", "jobrunner"), + "service.namespace": os.environ.get("BACKEND", "unknown"), + "service.version": config.VERSION, + } + ) + return TracerProvider(resource=resource) + + +def add_exporter(provider, exporter, processor=BatchSpanProcessor): """Utility method to add an exporter. We use the BatchSpanProcessor by default, which is the default for @@ -43,18 +44,24 @@ def add_exporter(exporter, processor=BatchSpanProcessor): provider.add_span_processor(processor(exporter)) -def setup_default_tracing(): +def setup_default_tracing(set_global=True): """Inspect environment variables and set up exporters accordingly.""" + + provider = get_provider() + if "OTEL_EXPORTER_OTLP_HEADERS" in os.environ: - if "OTEL_SERVICE_NAME" not in os.environ: - os.environ["OTEL_SERVICE_NAME"] == "jobrunner" if "OTEL_EXPORTER_OTLP_ENDPOINT" not in os.environ: os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://api.honeycomb.io" - add_exporter(OTLPSpanExporter()) + add_exporter(provider, OTLPSpanExporter()) if "OTEL_EXPORTER_CONSOLE" in os.environ: - add_exporter(ConsoleSpanExporter()) + add_exporter(provider, ConsoleSpanExporter()) + + if set_global: + trace.set_tracer_provider(provider) + + return provider @warn_assertions diff --git a/tests/conftest.py b/tests/conftest.py index 4d86e31d..3da488a4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,9 +19,11 @@ from jobrunner.lib.subprocess_utils import subprocess_run -# set up tracing +# set up test tracing +provider = tracing.get_provider() +tracing.trace.set_tracer_provider(provider) test_exporter = InMemorySpanExporter() -tracing.add_exporter(test_exporter, processor=SimpleSpanProcessor) +tracing.add_exporter(provider, test_exporter, processor=SimpleSpanProcessor) def pytest_configure(config): diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 6e07a663..6ef152a5 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -1,13 +1,68 @@ import os import time +import opentelemetry.exporter.otlp.proto.http.trace_exporter from opentelemetry import trace +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.trace.export import ConsoleSpanExporter from jobrunner import config, models, tracing from tests.conftest import get_trace from tests.factories import job_factory, job_request_factory, job_results_factory +def test_setup_default_tracing_empty_env(monkeypatch): + env = {} + monkeypatch.setattr(os, "environ", env) + provider = tracing.setup_default_tracing(set_global=False) + assert provider._active_span_processor._span_processors == () + + +def test_setup_default_tracing_console(monkeypatch): + env = {"OTEL_EXPORTER_CONSOLE": "1"} + monkeypatch.setattr(os, "environ", env) + provider = tracing.setup_default_tracing(set_global=False) + + processor = provider._active_span_processor._span_processors[0] + assert isinstance(processor.span_exporter, ConsoleSpanExporter) + + +def test_setup_default_tracing_otlp_defaults(monkeypatch): + env = {"OTEL_EXPORTER_OTLP_HEADERS": "foo=bar"} + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr( + opentelemetry.exporter.otlp.proto.http.trace_exporter, "environ", env + ) + provider = tracing.setup_default_tracing(set_global=False) + assert provider.resource.attributes["service.name"] == "jobrunner" + + exporter = provider._active_span_processor._span_processors[0].span_exporter + assert isinstance(exporter, OTLPSpanExporter) + assert exporter._endpoint == "https://api.honeycomb.io/v1/traces" + assert exporter._headers == {"foo": "bar"} + assert env["OTEL_EXPORTER_OTLP_ENDPOINT"] == "https://api.honeycomb.io" + + +def test_setup_default_tracing_otlp_with_env(monkeypatch): + env = { + "OTEL_EXPORTER_OTLP_HEADERS": "foo=bar", + "OTEL_SERVICE_NAME": "service", + "OTEL_EXPORTER_OTLP_ENDPOINT": "https://endpoint", + } + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr( + opentelemetry.exporter.otlp.proto.http.trace_exporter, "environ", env + ) + provider = tracing.setup_default_tracing(set_global=False) + assert provider.resource.attributes["service.name"] == "service" + + exporter = provider._active_span_processor._span_processors[0].span_exporter + + assert isinstance(exporter, OTLPSpanExporter) + assert exporter._endpoint == "https://endpoint/v1/traces" + assert exporter._headers == {"foo": "bar"} + + def test_trace_attributes(db): jr = job_request_factory( original=dict(