2323 AwsMetricAttributesSpanExporterBuilder ,
2424)
2525from amazon .opentelemetry .distro .aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
26+ from amazon .opentelemetry .distro .exporter .console .logs .compact_console_log_exporter import CompactConsoleLogExporter
2627from amazon .opentelemetry .distro .otlp_udp_exporter import OTLPUdpSpanExporter
2728from amazon .opentelemetry .distro .sampler .aws_xray_remote_sampler import AwsXRayRemoteSampler
2829from amazon .opentelemetry .distro .scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
4647)
4748from opentelemetry .sdk ._events import EventLoggerProvider
4849from opentelemetry .sdk ._logs import LoggerProvider , LoggingHandler
49- from opentelemetry .sdk ._logs .export import BatchLogRecordProcessor , LogExporter
50+ from opentelemetry .sdk ._logs .export import BatchLogRecordProcessor , ConsoleLogExporter , LogExporter
5051from opentelemetry .sdk .environment_variables import (
5152 _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED ,
5253 OTEL_EXPORTER_OTLP_METRICS_PROTOCOL ,
@@ -122,7 +123,14 @@ class OtlpLogHeaderSetting(NamedTuple):
122123 log_group : Optional [str ]
123124 log_stream : Optional [str ]
124125 namespace : Optional [str ]
125- is_valid : bool
126+
127+ def is_valid (self ) -> bool :
128+ """Check if the log header setting is valid by ensuring both log_group and log_stream are present."""
129+ return self .log_group is not None and self .log_stream is not None
130+
131+
132+ # Singleton cache for OtlpLogHeaderSetting
133+ _otlp_log_header_setting_cache : Optional [OtlpLogHeaderSetting ] = None
126134
127135
128136class AwsOpenTelemetryConfigurator (_OTelSDKConfigurator ):
@@ -209,6 +217,9 @@ def _init_logging(
209217 set_logger_provider (provider )
210218
211219 for _ , exporter_class in exporters .items ():
220+ if exporter_class is ConsoleLogExporter and _is_lambda_environment ():
221+ exporter_class = CompactConsoleLogExporter
222+ _logger .debug ("Lambda environment detected, using CompactConsoleLogExporter instead of ConsoleLogExporter" )
212223 exporter_args = {}
213224 _customize_log_record_processor (
214225 logger_provider = provider , log_exporter = _customize_logs_exporter (exporter_class (** exporter_args ))
@@ -440,7 +451,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
440451
441452 if isinstance (log_exporter , OTLPLogExporter ):
442453
443- if _validate_and_fetch_logs_header ().is_valid :
454+ if _fetch_logs_header ().is_valid () :
444455 endpoint , region = _extract_endpoint_and_region_from_otlp_endpoint (logs_endpoint )
445456 # Setting default compression mode to Gzip as this is the behavior in upstream's
446457 # collector otlp http exporter:
@@ -627,18 +638,23 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
627638 return endpoint , region
628639
629640
630- def _validate_and_fetch_logs_header () -> OtlpLogHeaderSetting :
631- """Checks if x-aws-log-group and x-aws-log-stream are present in the headers in order to send logs to
632- AWS OTLP Logs endpoint."""
641+ def _fetch_logs_header () -> OtlpLogHeaderSetting :
642+ """Returns the OTLP log header setting as a singleton instance."""
643+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
644+
645+ if _otlp_log_header_setting_cache is not None :
646+ return _otlp_log_header_setting_cache
633647
634648 logs_headers = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_HEADERS )
635649
636650 if not logs_headers :
637- _logger .warning (
638- "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
639- "to include x-aws-log-group and x-aws-log-stream"
640- )
641- return OtlpLogHeaderSetting (None , None , None , False )
651+ if not _is_lambda_environment ():
652+ _logger .warning (
653+ "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
654+ "to include x-aws-log-group and x-aws-log-stream"
655+ )
656+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (None , None , None )
657+ return _otlp_log_header_setting_cache
642658
643659 log_group = None
644660 log_stream = None
@@ -656,9 +672,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
656672 elif key == AWS_EMF_METRICS_NAMESPACE and value :
657673 namespace = value
658674
659- is_valid = log_group is not None and log_stream is not None
675+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (log_group , log_stream , namespace )
676+ return _otlp_log_header_setting_cache
677+
660678
661- return OtlpLogHeaderSetting (log_group , log_stream , namespace , is_valid )
679+ def _clear_logs_header_cache ():
680+ """Clear the singleton cache for OtlpLogHeaderSetting. Used primarily for testing."""
681+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
682+ _otlp_log_header_setting_cache = None
662683
663684
664685def _get_metric_export_interval ():
@@ -773,8 +794,25 @@ def _check_emf_exporter_enabled() -> bool:
773794
774795
775796def _create_emf_exporter ():
776- """Create and configure the CloudWatch EMF exporter."""
797+ """
798+ Create the appropriate EMF exporter based on the environment and configuration.
799+
800+ Returns:
801+ ConsoleEmfExporter for Lambda without log headers log group and stream
802+ AwsCloudWatchEmfExporter for other cases (when conditions are met)
803+ None if CloudWatch exporter cannot be created
804+ """
777805 try :
806+ log_header_setting = _fetch_logs_header ()
807+
808+ # Lambda without valid logs http headers - use Console EMF exporter
809+ if _is_lambda_environment () and not log_header_setting .is_valid ():
810+ # pylint: disable=import-outside-toplevel
811+ from amazon .opentelemetry .distro .exporter .aws .metrics .console_emf_exporter import ConsoleEmfExporter
812+
813+ return ConsoleEmfExporter (namespace = log_header_setting .namespace )
814+
815+ # For non-Lambda environment or Lambda with valid headers - use CloudWatch EMF exporter
778816 session = get_aws_session ()
779817 # Check if botocore is available before importing the EMF exporter
780818 if not session :
@@ -786,9 +824,7 @@ def _create_emf_exporter():
786824 AwsCloudWatchEmfExporter ,
787825 )
788826
789- log_header_setting = _validate_and_fetch_logs_header ()
790-
791- if not log_header_setting .is_valid :
827+ if not log_header_setting .is_valid ():
792828 return None
793829
794830 return AwsCloudWatchEmfExporter (
0 commit comments