Skip to content

Commit

Permalink
Auto instrumentation parameters (#3864)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydvoss committed May 30, 2024
1 parent d73593d commit c6edd0f
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- unit annotations (enclosed in curly braces like `{requests}`) are stripped away
- units with slash are converted e.g. `m/s` -> `meters_per_second`.
- The exporter's API is not changed
- Add parameters for Distros and configurators to configure autoinstrumentation in addition to existing environment variables.
([#3864] (https://github.com/open-telemetry/opentelemetry-python/pull/3864))

## Version 1.24.0/0.45b0 (2024-03-28)

Expand Down
66 changes: 45 additions & 21 deletions opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
MetricReader,
PeriodicExportingMetricReader,
)
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.resources import Attributes, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
from opentelemetry.sdk.trace.id_generator import IdGenerator
Expand Down Expand Up @@ -354,37 +354,61 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator:
raise RuntimeError(f"{id_generator_name} is not an IdGenerator")


def _initialize_components(auto_instrumentation_version):
trace_exporters, metric_exporters, log_exporters = _import_exporters(
_get_exporter_names("traces"),
_get_exporter_names("metrics"),
_get_exporter_names("logs"),
def _initialize_components(
auto_instrumentation_version: Optional[str] = None,
trace_exporter_names: Optional[List[str]] = None,
metric_exporter_names: Optional[List[str]] = None,
log_exporter_names: Optional[List[str]] = None,
sampler: Optional[Sampler] = None,
resource_attributes: Optional[Attributes] = None,
id_generator: IdGenerator = None,
logging_enabled: Optional[bool] = None,
):
if trace_exporter_names is None:
trace_exporter_names = []
if metric_exporter_names is None:
metric_exporter_names = []
if log_exporter_names is None:
log_exporter_names = []
span_exporters, metric_exporters, log_exporters = _import_exporters(
trace_exporter_names + _get_exporter_names("traces"),
metric_exporter_names + _get_exporter_names("metrics"),
log_exporter_names + _get_exporter_names("logs"),
)
sampler_name = _get_sampler()
sampler = _import_sampler(sampler_name)
id_generator_name = _get_id_generator()
id_generator = _import_id_generator(id_generator_name)
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
auto_resource = {}
if sampler is None:
sampler_name = _get_sampler()
sampler = _import_sampler(sampler_name)
if id_generator is None:
id_generator_name = _get_id_generator()
id_generator = _import_id_generator(id_generator_name)
if resource_attributes is None:
resource_attributes = {}
# populate version if using auto-instrumentation
if auto_instrumentation_version:
auto_resource[ResourceAttributes.TELEMETRY_AUTO_VERSION] = (
resource_attributes[ResourceAttributes.TELEMETRY_AUTO_VERSION] = (
auto_instrumentation_version
)
resource = Resource.create(auto_resource)
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
resource = Resource.create(resource_attributes)

_init_tracing(
exporters=trace_exporters,
exporters=span_exporters,
id_generator=id_generator,
sampler=sampler,
resource=resource,
)
_init_metrics(metric_exporters, resource)
logging_enabled = os.getenv(
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false"
)
if logging_enabled.strip().lower() == "true":
if logging_enabled is None:
logging_enabled = (
os.getenv(
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false"
)
.strip()
.lower()
== "true"
)
if logging_enabled:
_init_logging(log_exporters, resource)


Expand Down Expand Up @@ -428,4 +452,4 @@ class _OTelSDKConfigurator(_BaseConfigurator):
"""

def _configure(self, **kwargs):
_initialize_components(kwargs.get("auto_instrumentation_version"))
_initialize_components(**kwargs)
121 changes: 118 additions & 3 deletions opentelemetry-sdk/tests/test_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
_init_metrics,
_init_tracing,
_initialize_components,
_OTelSDKConfigurator,
)
from opentelemetry.sdk._logs import LoggingHandler
from opentelemetry.sdk._logs.export import ConsoleLogExporter
Expand Down Expand Up @@ -645,7 +646,7 @@ def test_logging_init_exporter(self):
@patch("opentelemetry.sdk._configuration._init_tracing")
@patch("opentelemetry.sdk._configuration._init_logging")
def test_logging_init_disable_default(self, logging_mock, tracing_mock):
_initialize_components("auto-version")
_initialize_components(auto_instrumentation_version="auto-version")
self.assertEqual(logging_mock.call_count, 0)
self.assertEqual(tracing_mock.call_count, 1)

Expand All @@ -660,7 +661,7 @@ def test_logging_init_disable_default(self, logging_mock, tracing_mock):
@patch("opentelemetry.sdk._configuration._init_logging")
def test_logging_init_enable_env(self, logging_mock, tracing_mock):
with self.assertLogs(level=WARNING):
_initialize_components("auto-version")
_initialize_components(auto_instrumentation_version="auto-version")
self.assertEqual(logging_mock.call_count, 1)
self.assertEqual(tracing_mock.call_count, 1)

Expand All @@ -677,7 +678,7 @@ def test_logging_init_enable_env(self, logging_mock, tracing_mock):
def test_initialize_components_resource(
self, metrics_mock, logging_mock, tracing_mock
):
_initialize_components("auto-version")
_initialize_components(auto_instrumentation_version="auto-version")
self.assertEqual(logging_mock.call_count, 1)
self.assertEqual(tracing_mock.call_count, 1)
self.assertEqual(metrics_mock.call_count, 1)
Expand All @@ -692,6 +693,101 @@ def test_initialize_components_resource(
self.assertEqual(logging_resource, metrics_resource)
self.assertEqual(tracing_resource, metrics_resource)

@patch.dict(
environ,
{
"OTEL_TRACES_EXPORTER": _EXPORTER_OTLP,
"OTEL_METRICS_EXPORTER": _EXPORTER_OTLP_PROTO_GRPC,
"OTEL_LOGS_EXPORTER": _EXPORTER_OTLP_PROTO_HTTP,
},
)
@patch.dict(
environ,
{
"OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service, custom.key.1=env-value",
"OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED": "False",
},
)
@patch("opentelemetry.sdk._configuration.Resource")
@patch("opentelemetry.sdk._configuration._import_exporters")
@patch("opentelemetry.sdk._configuration._get_exporter_names")
@patch("opentelemetry.sdk._configuration._init_tracing")
@patch("opentelemetry.sdk._configuration._init_logging")
@patch("opentelemetry.sdk._configuration._init_metrics")
def test_initialize_components_kwargs(
self,
metrics_mock,
logging_mock,
tracing_mock,
exporter_names_mock,
import_exporters_mock,
resource_mock,
):
exporter_names_mock.return_value = [
"env_var_exporter_1",
"env_var_exporter_2",
]
import_exporters_mock.return_value = (
"TEST_SPAN_EXPORTERS_DICT",
"TEST_METRICS_EXPORTERS_DICT",
"TEST_LOG_EXPORTERS_DICT",
)
resource_mock.create.return_value = "TEST_RESOURCE"
kwargs = {
"auto_instrumentation_version": "auto-version",
"trace_exporter_names": ["custom_span_exporter"],
"metric_exporter_names": ["custom_metric_exporter"],
"log_exporter_names": ["custom_log_exporter"],
"sampler": "TEST_SAMPLER",
"resource_attributes": {
"custom.key.1": "pass-in-value-1",
"custom.key.2": "pass-in-value-2",
},
"id_generator": "TEST_GENERATOR",
"logging_enabled": True,
}
_initialize_components(**kwargs)

import_exporters_mock.assert_called_once_with(
[
"custom_span_exporter",
"env_var_exporter_1",
"env_var_exporter_2",
],
[
"custom_metric_exporter",
"env_var_exporter_1",
"env_var_exporter_2",
],
[
"custom_log_exporter",
"env_var_exporter_1",
"env_var_exporter_2",
],
)
resource_mock.create.assert_called_once_with(
{
"telemetry.auto.version": "auto-version",
"custom.key.1": "pass-in-value-1",
"custom.key.2": "pass-in-value-2",
}
)
# Resource is checked separates
tracing_mock.assert_called_once_with(
exporters="TEST_SPAN_EXPORTERS_DICT",
id_generator="TEST_GENERATOR",
sampler="TEST_SAMPLER",
resource="TEST_RESOURCE",
)
metrics_mock.assert_called_once_with(
"TEST_METRICS_EXPORTERS_DICT",
"TEST_RESOURCE",
)
logging_mock.assert_called_once_with(
"TEST_LOG_EXPORTERS_DICT",
"TEST_RESOURCE",
)


class TestMetricsInit(TestCase):
def setUp(self):
Expand Down Expand Up @@ -910,3 +1006,22 @@ def test__import_config_components_missing_component(
str(error.value),
"Requested component 'a' not found in entry point 'name'",
)


class TestConfigurator(TestCase):
class CustomConfigurator(_OTelSDKConfigurator):
def _configure(self, **kwargs):
kwargs["sampler"] = "TEST_SAMPLER"
super()._configure(**kwargs)

@patch("opentelemetry.sdk._configuration._initialize_components")
def test_custom_configurator(self, mock_init_comp):
custom_configurator = TestConfigurator.CustomConfigurator()
custom_configurator._configure(
auto_instrumentation_version="TEST_VERSION2"
)
kwargs = {
"auto_instrumentation_version": "TEST_VERSION2",
"sampler": "TEST_SAMPLER",
}
mock_init_comp.assert_called_once_with(**kwargs)

0 comments on commit c6edd0f

Please sign in to comment.