@@ -125,7 +125,14 @@ class OtlpLogHeaderSetting(NamedTuple):
125125 log_group : Optional [str ]
126126 log_stream : Optional [str ]
127127 namespace : Optional [str ]
128- is_valid : bool
128+
129+ def is_valid (self ) -> bool :
130+ """Check if the log header setting is valid by ensuring both log_group and log_stream are present."""
131+ return self .log_group is not None and self .log_stream is not None
132+
133+
134+ # Singleton cache for OtlpLogHeaderSetting
135+ _otlp_log_header_setting_cache : Optional [OtlpLogHeaderSetting ] = None
129136
130137
131138class AwsOpenTelemetryConfigurator (_OTelSDKConfigurator ):
@@ -446,7 +453,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
446453
447454 if isinstance (log_exporter , OTLPLogExporter ):
448455
449- if _validate_and_fetch_logs_header ().is_valid :
456+ if _fetch_logs_header ().is_valid () :
450457 endpoint , region = _extract_endpoint_and_region_from_otlp_endpoint (logs_endpoint )
451458 # Setting default compression mode to Gzip as this is the behavior in upstream's
452459 # collector otlp http exporter:
@@ -633,18 +640,23 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
633640 return endpoint , region
634641
635642
636- def _validate_and_fetch_logs_header () -> OtlpLogHeaderSetting :
637- """Checks if x-aws-log-group and x-aws-log-stream are present in the headers in order to send logs to
638- AWS OTLP Logs endpoint."""
643+ def _fetch_logs_header () -> OtlpLogHeaderSetting :
644+ """Returns the OTLP log header setting as a singleton instance."""
645+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
646+
647+ if _otlp_log_header_setting_cache is not None :
648+ return _otlp_log_header_setting_cache
639649
640650 logs_headers = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_HEADERS )
641651
642652 if not logs_headers :
643- _logger .warning (
644- "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
645- "to include x-aws-log-group and x-aws-log-stream"
646- )
647- return OtlpLogHeaderSetting (None , None , None , False )
653+ if not _is_lambda_environment ():
654+ _logger .warning (
655+ "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
656+ "to include x-aws-log-group and x-aws-log-stream"
657+ )
658+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (None , None , None )
659+ return _otlp_log_header_setting_cache
648660
649661 log_group = None
650662 log_stream = None
@@ -662,9 +674,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
662674 elif key == AWS_EMF_METRICS_NAMESPACE and value :
663675 namespace = value
664676
665- is_valid = log_group is not None and log_stream is not None
677+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (log_group , log_stream , namespace )
678+ return _otlp_log_header_setting_cache
679+
666680
667- return OtlpLogHeaderSetting (log_group , log_stream , namespace , is_valid )
681+ def _clear_logs_header_cache ():
682+ """Clear the singleton cache for OtlpLogHeaderSetting. Used primarily for testing."""
683+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
684+ _otlp_log_header_setting_cache = None
668685
669686
670687def _get_metric_export_interval ():
@@ -779,8 +796,25 @@ def _check_emf_exporter_enabled() -> bool:
779796
780797
781798def _create_emf_exporter ():
782- """Create and configure the CloudWatch EMF exporter."""
799+ """
800+ Create the appropriate EMF exporter based on the environment and configuration.
801+
802+ Returns:
803+ ConsoleEmfExporter for Lambda without log headers log group and stream
804+ AwsCloudWatchEmfExporter for other cases (when conditions are met)
805+ None if CloudWatch exporter cannot be created
806+ """
783807 try :
808+ log_header_setting = _fetch_logs_header ()
809+
810+ # Lambda without valid logs http headers - use Console EMF exporter
811+ if _is_lambda_environment () and not log_header_setting .is_valid ():
812+ # pylint: disable=import-outside-toplevel
813+ from amazon .opentelemetry .distro .exporter .aws .metrics .console_emf_exporter import ConsoleEmfExporter
814+
815+ return ConsoleEmfExporter (namespace = log_header_setting .namespace )
816+
817+ # For non-Lambda environment or Lambda with valid headers - use CloudWatch EMF exporter
784818 session = get_aws_session ()
785819 # Check if botocore is available before importing the EMF exporter
786820 if not session :
@@ -792,9 +826,7 @@ def _create_emf_exporter():
792826 AwsCloudWatchEmfExporter ,
793827 )
794828
795- log_header_setting = _validate_and_fetch_logs_header ()
796-
797- if not log_header_setting .is_valid :
829+ if not log_header_setting .is_valid ():
798830 return None
799831
800832 return AwsCloudWatchEmfExporter (
0 commit comments