Skip to content

Commit 8644e08

Browse files
authored
Configure Span Pipeline for Genesis (#380)
## What does this pull request do? Configures span pipeline for Genesis to send traces to OTLP X-Ray endpoint when `AGENT_OBSERVABILITY_ENABLED` is enabled. We also pass an instance of `logger_provider` to our custom OTLP Span Exporter since we may need to [emit input/output responses to the logs pipeline](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/). By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 61e4963 commit 8644e08

File tree

5 files changed

+80
-2
lines changed

5 files changed

+80
-2
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import os
45
import sys
56
from logging import Logger, getLogger
67

78
import pkg_resources
89

910
_logger: Logger = getLogger(__name__)
1011

12+
AGENT_OBSERVABILITY_ENABLED = "AGENT_OBSERVABILITY_ENABLED"
13+
1114

1215
def is_installed(req: str) -> bool:
1316
"""Is the given required package installed?"""
@@ -21,3 +24,8 @@ def is_installed(req: str) -> bool:
2124
_logger.debug("Skipping instrumentation patch: package %s, exception: %s", req, exc)
2225
return False
2326
return True
27+
28+
29+
def is_agent_observability_enabled() -> bool:
30+
"""Is the Agentic AI monitoring flag set to true?"""
31+
return os.environ.get(AGENT_OBSERVABILITY_ENABLED, "false").lower() == "true"

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from amazon.opentelemetry.distro._aws_attribute_keys import AWS_LOCAL_SERVICE
1313
from amazon.opentelemetry.distro._aws_resource_attribute_configurator import get_service_attribute
14+
from amazon.opentelemetry.distro._utils import is_agent_observability_enabled
1415
from amazon.opentelemetry.distro.always_record_sampler import AlwaysRecordSampler
1516
from amazon.opentelemetry.distro.attribute_propagating_span_processor_builder import (
1617
AttributePropagatingSpanProcessorBuilder,
@@ -27,7 +28,7 @@
2728
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
2829
from amazon.opentelemetry.distro.scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
2930
from amazon.opentelemetry.distro.scope_based_filtering_view import ScopeBasedRetainingView
30-
from opentelemetry._logs import set_logger_provider
31+
from opentelemetry._logs import get_logger_provider, set_logger_provider
3132
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
3233
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter as OTLPHttpOTLPMetricExporter
3334
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
@@ -359,7 +360,15 @@ def _customize_span_exporter(span_exporter: SpanExporter, resource: Resource) ->
359360
_logger.info("Detected using AWS OTLP Traces Endpoint.")
360361

361362
if isinstance(span_exporter, OTLPSpanExporter):
362-
span_exporter = OTLPAwsSpanExporter(endpoint=traces_endpoint)
363+
if is_agent_observability_enabled():
364+
# Span exporter needs an instance of logger provider in ai agent
365+
# observability case because we need to split input/output prompts
366+
# from span attributes and send them to the logs pipeline per
367+
# the new Gen AI semantic convention from OTel
368+
# ref: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/
369+
span_exporter = OTLPAwsSpanExporter(endpoint=traces_endpoint, logger_provider=get_logger_provider())
370+
else:
371+
span_exporter = OTLPAwsSpanExporter(endpoint=traces_endpoint)
363372

364373
else:
365374
_logger.warning(

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/exporter/otlp/aws/traces/otlp_aws_span_exporter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from amazon.opentelemetry.distro.exporter.otlp.aws.common.aws_auth_session import AwsAuthSession
77
from opentelemetry.exporter.otlp.proto.http import Compression
88
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
9+
from opentelemetry.sdk._logs import LoggerProvider
910

1011

1112
class OTLPAwsSpanExporter(OTLPSpanExporter):
@@ -18,8 +19,10 @@ def __init__(
1819
headers: Optional[Dict[str, str]] = None,
1920
timeout: Optional[int] = None,
2021
compression: Optional[Compression] = None,
22+
logger_provider: Optional[LoggerProvider] = None,
2123
):
2224
self._aws_region = None
25+
self._logger_provider = logger_provider
2326

2427
if endpoint:
2528
self._aws_region = endpoint.split(".")[1]

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,35 @@ def test_customize_span_exporter(self):
311311
self.assertIsInstance(customized_exporter._delegate, OTLPUdpSpanExporter)
312312
os.environ.pop("AWS_LAMBDA_FUNCTION_NAME", None)
313313

314+
def test_customize_span_exporter_with_agent_observability(self):
315+
# Test that logger_provider is passed when agent observability is enabled
316+
os.environ["AGENT_OBSERVABILITY_ENABLED"] = "true"
317+
os.environ[OTEL_EXPORTER_OTLP_TRACES_ENDPOINT] = "https://xray.us-east-1.amazonaws.com/v1/traces"
318+
319+
mock_logger_provider = MagicMock()
320+
with patch(
321+
"amazon.opentelemetry.distro.aws_opentelemetry_configurator.get_logger_provider",
322+
return_value=mock_logger_provider,
323+
):
324+
mock_exporter = MagicMock(spec=OTLPSpanExporter)
325+
result = _customize_span_exporter(mock_exporter, Resource.get_empty())
326+
327+
self.assertIsInstance(result, OTLPAwsSpanExporter)
328+
self.assertEqual(result._logger_provider, mock_logger_provider)
329+
330+
# Test that logger_provider is not passed when agent observability is disabled
331+
os.environ["AGENT_OBSERVABILITY_ENABLED"] = "false"
332+
333+
mock_exporter = MagicMock(spec=OTLPSpanExporter)
334+
result = _customize_span_exporter(mock_exporter, Resource.get_empty())
335+
336+
self.assertIsInstance(result, OTLPAwsSpanExporter)
337+
self.assertIsNone(result._logger_provider)
338+
339+
# Clean up
340+
os.environ.pop("AGENT_OBSERVABILITY_ENABLED", None)
341+
os.environ.pop(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, None)
342+
314343
def test_customize_span_exporter_sigv4(self):
315344

316345
traces_good_endpoints = [
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from unittest import TestCase
5+
from unittest.mock import MagicMock
6+
7+
from amazon.opentelemetry.distro.exporter.otlp.aws.traces.otlp_aws_span_exporter import OTLPAwsSpanExporter
8+
from opentelemetry.sdk._logs import LoggerProvider
9+
10+
11+
class TestOTLPAwsSpanExporter(TestCase):
12+
def test_init_with_logger_provider(self):
13+
# Test initialization with logger_provider
14+
mock_logger_provider = MagicMock(spec=LoggerProvider)
15+
endpoint = "https://xray.us-east-1.amazonaws.com/v1/traces"
16+
17+
exporter = OTLPAwsSpanExporter(endpoint=endpoint, logger_provider=mock_logger_provider)
18+
19+
self.assertEqual(exporter._logger_provider, mock_logger_provider)
20+
self.assertEqual(exporter._aws_region, "us-east-1")
21+
22+
def test_init_without_logger_provider(self):
23+
# Test initialization without logger_provider (default behavior)
24+
endpoint = "https://xray.us-west-2.amazonaws.com/v1/traces"
25+
26+
exporter = OTLPAwsSpanExporter(endpoint=endpoint)
27+
28+
self.assertIsNone(exporter._logger_provider)
29+
self.assertEqual(exporter._aws_region, "us-west-2")

0 commit comments

Comments
 (0)