Skip to content

Commit 9dcaf24

Browse files
authored
Merge branch 'main' into pr1-emf-exporter-foundation
2 parents 1b34c6f + 8644e08 commit 9dcaf24

File tree

19 files changed

+95
-52
lines changed

19 files changed

+95
-52
lines changed

.github/workflows/application-signals-e2e-test.yml

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,6 @@ jobs:
4646
# DEFAULT SETTING: {Python Version}, EC2, AMD64, AL2
4747
#
4848

49-
default-py38-amd64:
50-
needs: [ upload-main-build ]
51-
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/python-ec2-default-test.yml@main
52-
secrets: inherit
53-
with:
54-
aws-region: us-east-1
55-
caller-workflow-name: 'main-build'
56-
python-version: '3.8'
57-
cpu-architecture: 'x86_64'
58-
staging-wheel-name: ${{ inputs.staging-wheel-name }}
59-
6049
default-py39-amd64:
6150
needs: [ upload-main-build ]
6251
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/python-ec2-default-test.yml@main
@@ -106,19 +95,8 @@ jobs:
10695
# DEFAULT SETTING: {Python Version}, EKS, AMD64, AL2
10796
#
10897

109-
eks-py38-amd64:
110-
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/python-eks-test.yml@main
111-
secrets: inherit
112-
with:
113-
aws-region: us-east-1
114-
test-cluster-name: 'e2e-python-adot-test'
115-
adot-image-name: ${{ inputs.adot-image-name }}
116-
caller-workflow-name: 'main-build'
117-
python-version: '3.8'
118-
11998
eks-py39-amd64:
12099
if: ${{ always() }}
121-
needs: eks-py38-amd64
122100
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/python-eks-test.yml@main
123101
secrets: inherit
124102
with:

.github/workflows/pr_build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
fail-fast: false # ensures the entire test matrix is run, even if one permutation fails
1717
matrix:
18-
python-version: ["3.8", "3.9", "3.10", "3.11"]
18+
python-version: ["3.9", "3.10", "3.11"]
1919
steps:
2020
- name: Checkout Repo @ SHA - ${{ github.sha }}
2121
uses: actions/checkout@v4

Dockerfile

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,10 @@ ADD aws-opentelemetry-distro/ ./aws-opentelemetry-distro/
1818
# * https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/azure-functions/recover-python-functions.md#troubleshoot-cannot-import-cygrpc
1919
RUN sed -i "/opentelemetry-exporter-otlp-proto-grpc/d" ./aws-opentelemetry-distro/pyproject.toml
2020

21-
# urllib3 recently made a release that drops support for Python 3.8
22-
# Our sdk depends on botocore which pulls in the version of urllib3 based Python runtime it detects.
23-
# The rule is that if current pip command version is > 3.9 botocore will pick up the latest urllib3,
24-
# otherwise it picks up the older urllib3 that is compatible with Python 3.8.
25-
# https://github.com/boto/botocore/blob/develop/requirements-docs.txt
26-
# Since this Dockerfile currently uses the fixed Python 3.11 base image to pull the required dependencies,
27-
# EKS and ECS applications will encounter a runtime error for Python 3.8 compatibility.
28-
# Our fix is to temporarily restrict the urllib3 version to one that works for all supported Python versions
29-
# that we currently commit to support (notably 3.8).
30-
# We also pin the setuptools version for similar issues with the library dropping 3.8 support.
31-
# https://github.com/pypa/setuptools/blame/main/pkg_resources/__init__.py#L24
32-
# TODO: Remove these temporary workarounds once we deprecate Python 3.8 support since it has reached end-of-life.
33-
RUN mkdir workspace && pip install setuptools==75.2.0 urllib3==2.2.3 --target workspace ./aws-opentelemetry-distro
21+
RUN mkdir workspace && pip install --target workspace ./aws-opentelemetry-distro
3422

3523
# Stage 2: Build the cp-utility binary
36-
FROM public.ecr.aws/docker/library/rust:1.82 as builder
24+
FROM public.ecr.aws/docker/library/rust:1.87 as builder
3725

3826
WORKDIR /usr/src/cp-utility
3927
COPY ./tools/cp-utility .

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ This project is licensed under the Apache-2.0 License.
3535
## Notices
3636

3737
### Python Version Support
38-
This project ensures compatibility with the following supported Python versions: 3.8, 3.9, 3.10, 3.11, 3.12
38+
This project ensures compatibility with the following supported Python versions: 3.9, 3.10, 3.11, 3.12
3939

4040
### Note on Amazon CloudWatch Application Signals
4141
[Amazon CloudWatch Application Signals](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Monitoring-Sections.html) components are designed to seamlessly work with all library instrumentations offered by [OpenTelemetry Python auto-instrumentation](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/README.md). While upstream OpenTelemetry Python instrumentations are in beta, Application Signals components are stable, production ready and have also been tested for popular libraries/frameworks such as [Django, Boto3, and others](https://github.com/aws-observability/aws-otel-python-instrumentation/tree/main/contract-tests/images/applications). We will prioritize backward compatibility for Application Signals components, striving to ensure that they remain functional even in the face of potential breaking changes introduced by OpenTelemetry upstream libraries. Please [raise an issue](https://github.com/aws-observability/aws-otel-python-instrumentation/blob/main/CONTRIBUTING.md#reporting-bugsfeature-requests) if you notice Application Signals doesn't work for a particular OpenTelemetry supported library.

aws-opentelemetry-distro/pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ dynamic = ["version"]
88
description = "AWS OpenTelemetry Python Distro"
99
readme = "README.rst"
1010
license = "Apache-2.0"
11-
requires-python = ">=3.8"
11+
requires-python = ">=3.9"
1212
authors = [
1313
{ name = "Amazon Web Services" },
1414
]
@@ -18,7 +18,6 @@ classifiers = [
1818
"License :: OSI Approved :: Apache Software License",
1919
"Programming Language :: Python",
2020
"Programming Language :: Python :: 3",
21-
"Programming Language :: Python :: 3.8",
2221
"Programming Language :: Python :: 3.9",
2322
"Programming Language :: Python :: 3.10",
2423
"Programming Language :: Python :: 3.11",

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)