Skip to content

Commit 73a82f6

Browse files
committed
configure unsampled span pipeline for genesis agent observability
1 parent abe6c6b commit 73a82f6

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ def _export_unsampled_span_for_lambda(trace_provider: TracerProvider, resource:
264264
)
265265

266266

267+
def _export_unsampled_span_for_agent_observability(trace_provider: TracerProvider, resource: Resource = None):
268+
if not is_agent_observability_enabled():
269+
return
270+
271+
traces_endpoint = os.environ.get(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
272+
273+
span_exporter = OTLPAwsSpanExporter(endpoint=traces_endpoint, logger_provider=get_logger_provider())
274+
275+
trace_provider.add_span_processor(BatchUnsampledSpanProcessor(span_exporter=span_exporter))
276+
277+
267278
def _is_defer_to_workers_enabled():
268279
return os.environ.get(OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED_CONFIG, "false").strip().lower() == "true"
269280

@@ -407,6 +418,13 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
407418
if _is_lambda_environment():
408419
provider.add_span_processor(AwsLambdaSpanProcessor())
409420

421+
# We always send 100% spans to Genesis platform for agent observability because
422+
# AI applications typically have low throughput traffic patterns and require
423+
# comprehensive monitoring to catch subtle failure modes like hallucinations
424+
# and quality degradation that sampling could miss.
425+
if is_agent_observability_enabled():
426+
_export_unsampled_span_for_agent_observability(provider, resource)
427+
410428
if not _is_application_signals_enabled():
411429
return
412430

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
_customize_sampler,
2626
_customize_span_exporter,
2727
_customize_span_processors,
28+
_export_unsampled_span_for_agent_observability,
2829
_export_unsampled_span_for_lambda,
2930
_init_logging,
3031
_is_application_signals_enabled,
@@ -682,6 +683,78 @@ def test_export_unsampled_span_for_lambda(self):
682683
os.environ.pop("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", None)
683684
os.environ.pop("AWS_LAMBDA_FUNCTION_NAME", None)
684685

686+
def test_export_unsampled_span_for_agent_observability(self):
687+
mock_tracer_provider: TracerProvider = MagicMock()
688+
689+
# Test when agent observability is disabled (default)
690+
_export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty())
691+
self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 0)
692+
693+
# Test when agent observability is enabled with AWS endpoint (the default case)
694+
os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true"
695+
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "https://xray.us-east-1.amazonaws.com/v1/traces"
696+
_export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty())
697+
self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 1)
698+
processor: SpanProcessor = mock_tracer_provider.add_span_processor.call_args_list[0].args[0]
699+
self.assertIsInstance(processor, BatchUnsampledSpanProcessor)
700+
701+
# Clean up
702+
os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None)
703+
os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None)
704+
705+
def test_export_unsampled_span_for_agent_observability_uses_aws_exporter(self):
706+
"""Test that OTLPAwsSpanExporter is used for AWS endpoints"""
707+
mock_tracer_provider: TracerProvider = MagicMock()
708+
709+
with patch(
710+
"amazon.opentelemetry.distro.aws_opentelemetry_configurator.OTLPAwsSpanExporter"
711+
) as mock_aws_exporter:
712+
with patch(
713+
"amazon.opentelemetry.distro.aws_opentelemetry_configurator.BatchUnsampledSpanProcessor"
714+
) as mock_processor:
715+
with patch(
716+
"amazon.opentelemetry.distro.aws_opentelemetry_configurator.get_logger_provider"
717+
) as mock_logger_provider:
718+
os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true"
719+
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "https://xray.us-east-1.amazonaws.com/v1/traces"
720+
721+
_export_unsampled_span_for_agent_observability(mock_tracer_provider, Resource.get_empty())
722+
723+
# Verify OTLPAwsSpanExporter is created with correct parameters
724+
mock_aws_exporter.assert_called_once_with(
725+
endpoint="https://xray.us-east-1.amazonaws.com/v1/traces",
726+
logger_provider=mock_logger_provider.return_value,
727+
)
728+
# Verify BatchUnsampledSpanProcessor wraps the exporter
729+
mock_processor.assert_called_once_with(span_exporter=mock_aws_exporter.return_value)
730+
# Verify processor is added to tracer provider
731+
mock_tracer_provider.add_span_processor.assert_called_once_with(mock_processor.return_value)
732+
733+
# Clean up
734+
os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None)
735+
os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None)
736+
737+
def test_customize_span_processors_with_agent_observability(self):
738+
"""Test that _customize_span_processors calls _export_unsampled_span_for_agent_observability"""
739+
mock_tracer_provider: TracerProvider = MagicMock()
740+
741+
with patch(
742+
"amazon.opentelemetry.distro.aws_opentelemetry_configurator._export_unsampled_span_for_agent_observability"
743+
) as mock_agent_observability:
744+
# Test that agent observability function is NOT called when disabled
745+
os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None)
746+
_customize_span_processors(mock_tracer_provider, Resource.get_empty())
747+
mock_agent_observability.assert_not_called()
748+
749+
# Test that agent observability function is called when enabled
750+
mock_agent_observability.reset_mock()
751+
os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true"
752+
_customize_span_processors(mock_tracer_provider, Resource.get_empty())
753+
mock_agent_observability.assert_called_once_with(mock_tracer_provider, Resource.get_empty())
754+
755+
# Clean up
756+
os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None)
757+
685758
def test_customize_metric_exporter(self):
686759
metric_readers = []
687760
views = []

0 commit comments

Comments
 (0)