diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac28f986..a9781a80a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ If your change does not need a CHANGELOG entry, add the "skip changelog" label t ## Unreleased +- fix(agent-observability): fall back to OTEL_EXPORTER_OTLP_ENDPOINT for unsampled spans; also export unsampled spans to non-AWS endpoints + ([#738](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/738)) - feat: auto-detect and mutually exclude AWS native vs third-party agentic instrumentors; add `AWS_AGENTIC_INSTRUMENTATION_OPT_IN` env var to override auto-detection ([#729](https://github.com/aws-observability/aws-otel-python-instrumentation/pull/729)) - fix(lambda-layer): align context propagation with JS — delegate to global propagator so W3C traceparent is no longer ignored when X-Ray active tracing is enabled diff --git a/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py b/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py index 81f87ba3b..f3906c36f 100644 --- a/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py +++ b/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py @@ -64,6 +64,7 @@ from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter, LogRecordExporter from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, + OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_TRACES_SAMPLER_ARG, @@ -331,11 +332,20 @@ def _export_unsampled_span_for_agent_observability(trace_provider: TracerProvide return traces_endpoint = os.environ.get(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) - if traces_endpoint and _is_aws_otlp_endpoint(traces_endpoint, XRAY_SERVICE): + if not traces_endpoint: + base_endpoint = os.environ.get(OTEL_EXPORTER_OTLP_ENDPOINT) + if base_endpoint: + traces_endpoint = base_endpoint.rstrip("/") + "/v1/traces" + if not traces_endpoint: + return + + if _is_aws_otlp_endpoint(traces_endpoint, XRAY_SERVICE): endpoint, region = _extract_endpoint_and_region_from_otlp_endpoint(traces_endpoint) span_exporter = _create_aws_otlp_exporter(endpoint=endpoint, service=XRAY_SERVICE, region=region) + else: + span_exporter = OTLPSpanExporter(endpoint=traces_endpoint) - trace_provider.add_span_processor(BatchUnsampledSpanProcessor(span_exporter=span_exporter)) + trace_provider.add_span_processor(BatchUnsampledSpanProcessor(span_exporter=span_exporter)) def _is_defer_to_workers_enabled(): diff --git a/aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py b/aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py index 265a341a2..b1c218adb 100644 --- a/aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py +++ b/aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py @@ -1102,6 +1102,43 @@ def test_export_unsampled_span_for_agent_observability(self): os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None) os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None) + mock_tracer_provider.reset_mock() + + os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true" + os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4318" + _export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty()) + self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 1) + processor = mock_tracer_provider.add_span_processor.call_args_list[0].args[0] + self.assertIsInstance(processor, BatchUnsampledSpanProcessor) + self.assertEqual(processor.span_exporter._endpoint, "http://localhost:4318/v1/traces") + + os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None) + os.environ.pop("OTEL_EXPORTER_OTLP_ENDPOINT", None) + + mock_tracer_provider.reset_mock() + + os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true" + os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://xray.us-west-2.amazonaws.com" + _export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty()) + self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 1) + + os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None) + os.environ.pop("OTEL_EXPORTER_OTLP_ENDPOINT", None) + + mock_tracer_provider.reset_mock() + + os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true" + os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "https://xray.us-east-1.amazonaws.com/v1/traces" + os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4318" + _export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty()) + self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 1) + processor = mock_tracer_provider.add_span_processor.call_args_list[0].args[0] + self.assertIsInstance(processor, BatchUnsampledSpanProcessor) + + os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None) + os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None) + os.environ.pop("OTEL_EXPORTER_OTLP_ENDPOINT", None) + # pylint: disable=no-self-use def test_export_unsampled_span_for_agent_observability_uses_aws_exporter(self): """Test that OTLPAwsSpanExporter is used for AWS endpoints"""