1212
1313from amazon .opentelemetry .distro ._aws_attribute_keys import AWS_LOCAL_SERVICE
1414from amazon .opentelemetry .distro ._aws_resource_attribute_configurator import get_service_attribute
15- from amazon .opentelemetry .distro ._utils import is_agent_observability_enabled , is_installed
15+ from amazon .opentelemetry .distro ._utils import IS_BOTOCORE_INSTALLED , get_aws_session , is_agent_observability_enabled
1616from amazon .opentelemetry .distro .always_record_sampler import AlwaysRecordSampler
1717from amazon .opentelemetry .distro .attribute_propagating_span_processor_builder import (
1818 AttributePropagatingSpanProcessorBuilder ,
2323 AwsMetricAttributesSpanExporterBuilder ,
2424)
2525from amazon .opentelemetry .distro .aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
26- from amazon .opentelemetry .distro .exporter .otlp .aws .logs .aws_batch_log_record_processor import (
27- AwsCloudWatchOtlpBatchLogRecordProcessor ,
28- )
29- from amazon .opentelemetry .distro .exporter .otlp .aws .logs .otlp_aws_logs_exporter import OTLPAwsLogExporter
30- from amazon .opentelemetry .distro .exporter .otlp .aws .traces .otlp_aws_span_exporter import OTLPAwsSpanExporter
3126from amazon .opentelemetry .distro .otlp_udp_exporter import OTLPUdpSpanExporter
3227from amazon .opentelemetry .distro .sampler .aws_xray_remote_sampler import AwsXRayRemoteSampler
3328from amazon .opentelemetry .distro .scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
@@ -214,8 +209,7 @@ def _init_logging(
214209 for _ , exporter_class in exporters .items ():
215210 exporter_args = {}
216211 log_exporter : LogExporter = _customize_logs_exporter (exporter_class (** exporter_args ))
217- log_processor = _customize_log_record_processor (log_exporter )
218- provider .add_log_record_processor (log_processor )
212+ _customize_log_record_processor (provider , log_exporter )
219213
220214 event_logger_provider = EventLoggerProvider (logger_provider = provider )
221215 set_event_logger_provider (event_logger_provider )
@@ -303,7 +297,7 @@ def _export_unsampled_span_for_agent_observability(trace_provider: TracerProvide
303297
304298 traces_endpoint = os .environ .get (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT )
305299
306- span_exporter = OTLPAwsSpanExporter (endpoint = traces_endpoint , logger_provider = get_logger_provider () )
300+ span_exporter = _create_aws_exporter (endpoint = traces_endpoint )
307301
308302 trace_provider .add_span_processor (BatchUnsampledSpanProcessor (span_exporter = span_exporter ))
309303
@@ -404,15 +398,7 @@ def _customize_span_exporter(span_exporter: SpanExporter, resource: Resource) ->
404398 _logger .info ("Detected using AWS OTLP Traces Endpoint." )
405399
406400 if isinstance (span_exporter , OTLPSpanExporter ):
407- if is_agent_observability_enabled ():
408- # Span exporter needs an instance of logger provider in ai agent
409- # observability case because we need to split input/output prompts
410- # from span attributes and send them to the logs pipeline per
411- # the new Gen AI semantic convention from OTel
412- # ref: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/
413- span_exporter = OTLPAwsSpanExporter (endpoint = traces_endpoint , logger_provider = get_logger_provider ())
414- else :
415- span_exporter = OTLPAwsSpanExporter (endpoint = traces_endpoint )
401+ return _create_aws_exporter (endpoint = traces_endpoint )
416402
417403 else :
418404 _logger .warning (
@@ -426,14 +412,20 @@ def _customize_span_exporter(span_exporter: SpanExporter, resource: Resource) ->
426412 return AwsMetricAttributesSpanExporterBuilder (span_exporter , resource ).build ()
427413
428414
429- def _customize_log_record_processor (log_exporter : LogExporter ):
430- if isinstance (log_exporter , OTLPAwsLogExporter ) and is_agent_observability_enabled ():
431- return AwsCloudWatchOtlpBatchLogRecordProcessor (exporter = log_exporter )
415+ def _customize_log_record_processor (provider : LoggerProvider , log_exporter : Optional [LogExporter ]) -> None :
416+ if log_exporter is None :
417+ return
418+ if is_agent_observability_enabled () and IS_BOTOCORE_INSTALLED :
419+ from amazon .opentelemetry .distro .exporter .otlp .aws .logs .aws_batch_log_record_processor import (
420+ AwsCloudWatchOtlpBatchLogRecordProcessor ,
421+ )
432422
433- return BatchLogRecordProcessor (exporter = log_exporter )
423+ provider .add_log_record_processor (AwsCloudWatchOtlpBatchLogRecordProcessor (exporter = log_exporter ))
424+ else :
425+ provider .add_log_record_processor (BatchLogRecordProcessor (exporter = log_exporter ))
434426
435427
436- def _customize_logs_exporter (log_exporter : LogExporter ) -> LogExporter :
428+ def _customize_logs_exporter (log_exporter : LogExporter ) -> Optional [ LogExporter ] :
437429 logs_endpoint = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_ENDPOINT )
438430
439431 if _is_aws_otlp_endpoint (logs_endpoint , "logs" ):
@@ -443,7 +435,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
443435 # Setting default compression mode to Gzip as this is the behavior in upstream's
444436 # collector otlp http exporter:
445437 # https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlphttpexporter
446- return OTLPAwsLogExporter (endpoint = logs_endpoint )
438+ return _create_aws_exporter (endpoint = logs_endpoint )
447439
448440 _logger .warning (
449441 "Improper configuration see: please export/set "
@@ -762,8 +754,9 @@ def _check_emf_exporter_enabled() -> bool:
762754def create_emf_exporter ():
763755 """Create and configure the CloudWatch EMF exporter."""
764756 try :
757+ session = get_aws_session ()
765758 # Check if botocore is available before importing the EMF exporter
766- if not is_installed ( "botocore" ) :
759+ if not session :
767760 _logger .warning ("botocore is not installed. EMF exporter requires botocore" )
768761 return None
769762
@@ -778,6 +771,7 @@ def create_emf_exporter():
778771 return None
779772
780773 return AwsCloudWatchEmfExporter (
774+ session = session ,
781775 namespace = log_header_setting .namespace ,
782776 log_group_name = log_header_setting .log_group ,
783777 log_stream_name = log_header_setting .log_stream ,
@@ -786,3 +780,43 @@ def create_emf_exporter():
786780 except Exception as errors :
787781 _logger .error ("Failed to create EMF exporter: %s" , errors )
788782 return None
783+
784+
785+ def _create_aws_exporter (endpoint : str ):
786+ """Create and configure the AWS OTLP exporters."""
787+ try :
788+ session = get_aws_session ()
789+ # Check if botocore is available before importing the AWS exporter
790+ if not session :
791+ _logger .warning ("SigV4 Auth requires botocore to be enabled" )
792+ return None
793+
794+ # pylint: disable=import-outside-toplevel
795+ from amazon .opentelemetry .distro .exporter .otlp .aws .logs .otlp_aws_logs_exporter import OTLPAwsLogExporter
796+ from amazon .opentelemetry .distro .exporter .otlp .aws .traces .otlp_aws_span_exporter import OTLPAwsSpanExporter
797+
798+ endpoint = endpoint .lower ()
799+ split = endpoint .split ("." )
800+ service = split [0 ]
801+ region = split [1 ]
802+
803+ if "xray" in service :
804+ if is_agent_observability_enabled ():
805+ # Span exporter needs an instance of logger provider in ai agent
806+ # observability case because we need to split input/output prompts
807+ # from span attributes and send them to the logs pipeline per
808+ # the new Gen AI semantic convention from OTel
809+ # ref: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/
810+ return OTLPAwsSpanExporter (
811+ session = session , endpoint = endpoint , aws_region = region , logger_provider = get_logger_provider ()
812+ )
813+
814+ return OTLPAwsSpanExporter (session = session , endpoint = endpoint , aws_region = region )
815+
816+ if "logs" in service :
817+ return OTLPAwsLogExporter (session = session , aws_region = region )
818+
819+ # pylint: disable=broad-exception-caught
820+ except Exception as errors :
821+ _logger .error ("Failed to create AWS OTLP exporter: %s" , errors )
822+ return None
0 commit comments