From bfd45e1261d9947f7bb6e972e0cdee7d0e2f79af Mon Sep 17 00:00:00 2001 From: Puskar Deb <36558965+puskardeb@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:37:06 +0000 Subject: [PATCH] Adds support OTEL_SDK_DISABLED environment variable --- .../src/opentelemetry/sdk/_logs/_internal/__init__.py | 8 ++++++++ .../src/opentelemetry/sdk/environment_variables.py | 8 ++++++++ .../opentelemetry/sdk/metrics/_internal/__init__.py | 10 ++++++++++ .../src/opentelemetry/sdk/trace/__init__.py | 10 +++++++++- opentelemetry-sdk/tests/logs/test_logs.py | 9 ++++++++- opentelemetry-sdk/tests/metrics/test_metrics.py | 6 ++++++ opentelemetry-sdk/tests/trace/test_trace.py | 6 ++++++ 7 files changed, 55 insertions(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py index d84c9919608..31c49b57a3a 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py @@ -37,6 +37,7 @@ from opentelemetry.sdk.environment_variables import ( OTEL_ATTRIBUTE_COUNT_LIMIT, OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_SDK_DISABLED, ) from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.util import ns_to_iso_str @@ -607,6 +608,10 @@ def __init__( self._multi_log_record_processor = ( multi_log_record_processor or SynchronousMultiLogRecordProcessor() ) + disabled = environ.get(OTEL_SDK_DISABLED) + if disabled is None: + disabled = "false" + self._disabled = disabled.lower().strip() == "true" self._at_exit_handler = None if shutdown_on_exit: self._at_exit_handler = atexit.register(self.shutdown) @@ -621,6 +626,9 @@ def get_logger( version: Optional[str] = None, schema_url: Optional[str] = None, ) -> Logger: + if self._disabled: + _logger.warning("SDK is disabled.") + return NoOpLogger(name) return Logger( self._resource, self._multi_log_record_processor, diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py index a69e451cbb8..6557d2a1391 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py @@ -12,6 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +OTEL_SDK_DISABLED = "OTEL_SDK_DISABLED" +""" +.. envvar:: OTEL_SDK_DISABLED + +The :envvar:`OTEL_SDK_DISABLED` environment variable disables the SDK for all signals +Default: "false" +""" + OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES" """ .. envvar:: OTEL_RESOURCE_ATTRIBUTES diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py index 9b5aac16143..d7af35d961c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py @@ -14,6 +14,7 @@ from atexit import register, unregister from logging import getLogger +from os import environ from threading import Lock from time import time_ns from typing import Optional, Sequence @@ -32,6 +33,7 @@ ) from opentelemetry.metrics import UpDownCounter as APIUpDownCounter from opentelemetry.metrics import _Gauge as APIGauge +from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED from opentelemetry.sdk.metrics._internal.exceptions import MetricsTimeoutError from opentelemetry.sdk.metrics._internal.instrument import ( _Counter, @@ -394,6 +396,10 @@ def __init__( self._measurement_consumer = SynchronousMeasurementConsumer( sdk_config=self._sdk_config ) + disabled = environ.get(OTEL_SDK_DISABLED) + if disabled is None: + disabled = "false" + self._disabled = disabled.lower().strip() == "true" if shutdown_on_exit: self._atexit_handler = register(self.shutdown) @@ -512,6 +518,10 @@ def get_meter( schema_url: Optional[str] = None, ) -> Meter: + if self._disabled: + _logger.warning("SDK is disabled.") + return NoOpMeter(name) + if self._shutdown: _logger.warning( "A shutdown `MeterProvider` can not provide a `Meter`" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index f937ece8958..3140915c2d9 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -50,6 +50,7 @@ OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + OTEL_SDK_DISABLED, OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_SPAN_EVENT_COUNT_LIMIT, @@ -63,7 +64,7 @@ InstrumentationInfo, InstrumentationScope, ) -from opentelemetry.trace import SpanContext +from opentelemetry.trace import NoOpTracer, SpanContext from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util import types @@ -1176,6 +1177,10 @@ def __init__( sampler = sampling._get_from_env_or_default() self.sampler = sampler self._span_limits = span_limits or SpanLimits() + disabled = environ.get(OTEL_SDK_DISABLED) + if disabled is None: + disabled = "false" + self._disabled = disabled.lower().strip() == "true" self._atexit_handler = None if shutdown_on_exit: @@ -1191,6 +1196,9 @@ def get_tracer( instrumenting_library_version: typing.Optional[str] = None, schema_url: typing.Optional[str] = None, ) -> "trace_api.Tracer": + if self._disabled: + logger.warning("SDK is disabled.") + return NoOpTracer() if not instrumenting_module_name: # Reject empty strings too. instrumenting_module_name = "" logger.error("get_tracer called with missing module name.") diff --git a/opentelemetry-sdk/tests/logs/test_logs.py b/opentelemetry-sdk/tests/logs/test_logs.py index 935b5ee2493..71098531d4f 100644 --- a/opentelemetry-sdk/tests/logs/test_logs.py +++ b/opentelemetry-sdk/tests/logs/test_logs.py @@ -15,12 +15,14 @@ # pylint: disable=protected-access import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs._internal import ( + NoOpLogger, SynchronousMultiLogRecordProcessor, ) +from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED from opentelemetry.sdk.resources import Resource @@ -61,6 +63,11 @@ def test_get_logger(self): logger._instrumentation_scope.schema_url, "schema_url" ) + @patch.dict("os.environ", {OTEL_SDK_DISABLED: "true"}) + def test_get_logger_with_sdk_disabled(self): + logger = LoggerProvider().get_logger(Mock()) + self.assertIsInstance(logger, NoOpLogger) + @patch.object(Resource, "create") def test_logger_provider_init(self, resource_patch): logger_provider = LoggerProvider() diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 6232e072e23..61cfb79f291 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -19,6 +19,7 @@ from unittest.mock import MagicMock, Mock, patch from opentelemetry.metrics import NoOpMeter +from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED from opentelemetry.sdk.metrics import ( Counter, Histogram, @@ -464,6 +465,11 @@ def test_create_observable_up_down_counter(self): observable_up_down_counter, ObservableUpDownCounter ) self.assertEqual(observable_up_down_counter.name, "name") + + @patch.dict("os.environ", {OTEL_SDK_DISABLED: "true"}) + def test_get_meter_with_sdk_disabled(self): + meter_provider = MeterProvider() + self.assertIsInstance(meter_provider.get_meter(Mock()), NoOpMeter) class InMemoryMetricExporter(MetricExporter): diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 40f4f4b3162..bf05103db8a 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -37,6 +37,7 @@ OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_SPAN_EVENT_COUNT_LIMIT, OTEL_SPAN_LINK_COUNT_LIMIT, + OTEL_SDK_DISABLED, OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, ) @@ -162,6 +163,11 @@ def test_tracer_provider_accepts_concurrent_multi_span_processor(self): span_processor, tracer_provider._active_span_processor ) + @mock.patch.dict("os.environ", {OTEL_SDK_DISABLED: "true"}) + def test_get_tracer_with_sdk_disabled(self): + tracer_provider = trace.TracerProvider() + self.assertIsInstance(tracer_provider.get_tracer(Mock()), trace_api.NoOpTracer) + class TestTracerSampling(unittest.TestCase): def tearDown(self):