diff --git a/CHANGELOG.md b/CHANGELOG.md index ea55e13..da6e9ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.42 + +* **Support "none" value for OTEL_TRACES_EXPORTER and OTEL_METRICS_EXPORTER** - Filter "none" from exporter lists and return None when no exporters configured to properly disable OpenTelemetry instrumentation + ## 0.0.39 * **Remove wrap_error logic as exceptions are categorized in unstructured-ingest** diff --git a/test/test_otel.py b/test/test_otel.py new file mode 100644 index 0000000..a377f09 --- /dev/null +++ b/test/test_otel.py @@ -0,0 +1,68 @@ +from opentelemetry.environment_variables import OTEL_METRICS_EXPORTER, OTEL_TRACES_EXPORTER +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.trace import TracerProvider + +from unstructured_platform_plugins.etl_uvicorn.otel import ( + get_metric_provider, + get_settings, + get_trace_provider, +) + + +def test_get_settings_filters_none_from_traces(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + settings = get_settings() + assert settings["trace_exporters"] == [] + + +def test_get_settings_filters_none_from_metrics(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + settings = get_settings() + assert settings["metric_exporters"] == [] + + +def test_get_settings_filters_none_from_combined_trace_exporters(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none,console") + settings = get_settings() + assert settings["trace_exporters"] == ["console"] + + +def test_get_settings_filters_none_from_combined_metric_exporters(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none,console") + settings = get_settings() + assert settings["metric_exporters"] == ["console"] + + +def test_get_trace_provider_returns_none_when_exporter_is_none(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + assert get_trace_provider() is None + + +def test_get_metric_provider_returns_none_when_exporter_is_none(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + assert get_metric_provider() is None + + +def test_get_trace_provider_returns_provider_when_exporter_set(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "console") + provider = get_trace_provider() + assert isinstance(provider, TracerProvider) + + +def test_get_metric_provider_returns_provider_when_exporter_set(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "console") + provider = get_metric_provider() + assert isinstance(provider, MeterProvider) + + +def test_wrap_in_fastapi_does_not_crash_with_none_otel_exporters(monkeypatch): + """Regression: previously crashed with NotImplementedError when none was set.""" + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + + from test.assets.async_typed_dict_response import async_sample_function + from unstructured_platform_plugins.etl_uvicorn.api_generator import wrap_in_fastapi + + # Should not raise NotImplementedError + app = wrap_in_fastapi(func=async_sample_function, plugin_id="test_plugin") + assert app is not None diff --git a/unstructured_platform_plugins/__version__.py b/unstructured_platform_plugins/__version__.py index 592359c..89da183 100644 --- a/unstructured_platform_plugins/__version__.py +++ b/unstructured_platform_plugins/__version__.py @@ -1 +1 @@ -__version__ = "0.0.41" # pragma: no cover +__version__ = "0.0.42" # pragma: no cover diff --git a/unstructured_platform_plugins/etl_uvicorn/otel.py b/unstructured_platform_plugins/etl_uvicorn/otel.py index 5eae4e4..4654d87 100644 --- a/unstructured_platform_plugins/etl_uvicorn/otel.py +++ b/unstructured_platform_plugins/etl_uvicorn/otel.py @@ -30,9 +30,11 @@ def get_settings() -> OtelSettings: service_name = os.environ.get(OTEL_SERVICE_NAME, "unknown_service") trace_exporters = os.environ.get(OTEL_TRACES_EXPORTER) trace_exporters = trace_exporters.split(",") if trace_exporters else [] + trace_exporters = [e for e in trace_exporters if e != "none"] metric_exporters = os.environ.get(OTEL_METRICS_EXPORTER) metric_exporters = metric_exporters.split(",") if metric_exporters else [] + metric_exporters = [e for e in metric_exporters if e != "none"] return OtelSettings( service_name=service_name, trace_exporters=trace_exporters, @@ -40,8 +42,11 @@ def get_settings() -> OtelSettings: ) -def get_trace_provider() -> TracerProvider: +def get_trace_provider() -> TracerProvider | None: settings = get_settings() + if not settings["trace_exporters"]: + return None + provider = TracerProvider(resource=Resource({SERVICE_NAME: settings["service_name"]})) for trace_exporter_type in settings["trace_exporters"]: @@ -50,8 +55,11 @@ def get_trace_provider() -> TracerProvider: return provider -def get_metric_provider() -> MeterProvider: +def get_metric_provider() -> MeterProvider | None: settings = get_settings() + if not settings["metric_exporters"]: + return None + readers = [] for metric_exporter_type in settings["metric_exporters"]: readers.append(_get_metrics_reader(exporter_type=metric_exporter_type))