From 45366a281e3572753c92c5b379f4162b83a4362c Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Wed, 28 Oct 2026 17:56:02 +0300 Subject: [PATCH] feat(otel): add deployment.environment.name from service_environment to the resource --- docs/introduction/configuration.md | 2 +- .../instruments/opentelemetry_instrument.py | 1 + .../test_opentelemetry_instrument.py | 20 ++++++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/introduction/configuration.md b/docs/introduction/configuration.md index 43814fe..9e138bf 100644 --- a/docs/introduction/configuration.md +++ b/docs/introduction/configuration.md @@ -205,7 +205,7 @@ OTEL_EXPERIMENTAL_RESOURCE_DETECTORS=process,os `OTEL_RESOURCE_ATTRIBUTES` is honoured as well. The attributes lite-bootstrap sets (`service.name`, `service.namespace`, `service.version`, -`container.name`) are merged last and override anything a detector or environment variable supplies. +`container.name`, and `deployment.environment.name` from `service_environment`) are merged last and override anything a detector or environment variable supplies. `service_name` defaults to `"micro-service"`, so `OTEL_SERVICE_NAME` is overridden unless `service_name` is empty: set `opentelemetry_service_name` or `service_name` instead. diff --git a/lite_bootstrap/instruments/opentelemetry_instrument.py b/lite_bootstrap/instruments/opentelemetry_instrument.py index bc18f23..19593a0 100644 --- a/lite_bootstrap/instruments/opentelemetry_instrument.py +++ b/lite_bootstrap/instruments/opentelemetry_instrument.py @@ -243,6 +243,7 @@ def _build_resource(self) -> "resources.Resource": resources.SERVICE_NAMESPACE: config.opentelemetry_namespace, resources.SERVICE_VERSION: config.service_version, resources.CONTAINER_NAME: config.opentelemetry_container_name, + "deployment.environment.name": config.service_environment, } return resources.Resource.create(attributes={k: v for k, v in attributes.items() if v}) diff --git a/tests/instruments/test_opentelemetry_instrument.py b/tests/instruments/test_opentelemetry_instrument.py index 76d8896..c80a80f 100644 --- a/tests/instruments/test_opentelemetry_instrument.py +++ b/tests/instruments/test_opentelemetry_instrument.py @@ -149,6 +149,21 @@ def test_opentelemetry_configured_service_name_wins_over_env( instrument.teardown() +@pytest.mark.parametrize(("service_environment", "expected"), [("prod", "prod"), (None, None)]) +def test_opentelemetry_resource_carries_deployment_environment( + service_environment: str | None, expected: str | None +) -> None: + instrument = OpenTelemetryInstrument( + bootstrap_config=OpenTelemetryConfig(service_environment=service_environment, opentelemetry_log_traces=True) + ) + try: + instrument.bootstrap() + assert instrument._tracer_provider is not None # noqa: SLF001 + assert instrument._tracer_provider.resource.attributes.get("deployment.environment.name") == expected # noqa: SLF001 + finally: + instrument.teardown() + + def test_opentelemetry_resource_detectors_enrich_resource(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OTEL_EXPERIMENTAL_RESOURCE_DETECTORS", "process") monkeypatch.setenv("OTEL_RESOURCE_ATTRIBUTES", "deployment.environment=from-env") @@ -504,7 +519,9 @@ def test_metrics_endpoint_installs_a_meter_provider_carrying_the_resource() -> N """The metrics pipeline is opt-in through its own endpoint and shares the trace resource.""" instrument = OpenTelemetryInstrument( bootstrap_config=OpenTelemetryConfig( - service_name="metrics-svc", opentelemetry_metrics_endpoint="localhost:4317" + service_name="metrics-svc", + service_environment="prod", + opentelemetry_metrics_endpoint="localhost:4317", ) ) try: @@ -514,6 +531,7 @@ def test_metrics_endpoint_installs_a_meter_provider_carrying_the_resource() -> N assert isinstance(meter_provider, SDKMeterProvider) assert meter_provider is instrument._meter_provider # noqa: SLF001 assert meter_provider._sdk_config.resource.attributes["service.name"] == "metrics-svc" # noqa: SLF001 + assert meter_provider._sdk_config.resource.attributes["deployment.environment.name"] == "prod" # noqa: SLF001 (reader,) = meter_provider._metric_readers # noqa: SLF001 assert isinstance(reader, PeriodicExportingMetricReader) assert isinstance(reader._exporter, OTLPGrpcMetricExporter) # noqa: SLF001