@@ -122,7 +122,14 @@ class OtlpLogHeaderSetting(NamedTuple):
122122 log_group : Optional [str ]
123123 log_stream : Optional [str ]
124124 namespace : Optional [str ]
125- is_valid : bool
125+
126+ def is_valid (self ) -> bool :
127+ """Check if the log header setting is valid by ensuring both log_group and log_stream are present."""
128+ return self .log_group is not None and self .log_stream is not None
129+
130+
131+ # Singleton cache for OtlpLogHeaderSetting
132+ _otlp_log_header_setting_cache : Optional [OtlpLogHeaderSetting ] = None
126133
127134
128135class AwsOpenTelemetryConfigurator (_OTelSDKConfigurator ):
@@ -440,7 +447,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
440447
441448 if isinstance (log_exporter , OTLPLogExporter ):
442449
443- if _validate_and_fetch_logs_header ().is_valid :
450+ if _fetch_logs_header ().is_valid () :
444451 endpoint , region = _extract_endpoint_and_region_from_otlp_endpoint (logs_endpoint )
445452 # Setting default compression mode to Gzip as this is the behavior in upstream's
446453 # collector otlp http exporter:
@@ -627,9 +634,12 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
627634 return endpoint , region
628635
629636
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."""
637+ def _fetch_logs_header () -> OtlpLogHeaderSetting :
638+ """Returns the OTLP log header setting as a singleton instance."""
639+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
640+
641+ if _otlp_log_header_setting_cache is not None :
642+ return _otlp_log_header_setting_cache
633643
634644 logs_headers = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_HEADERS )
635645
@@ -639,7 +649,8 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
639649 "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
640650 "to include x-aws-log-group and x-aws-log-stream"
641651 )
642- return OtlpLogHeaderSetting (None , None , None , False )
652+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (None , None , None )
653+ return _otlp_log_header_setting_cache
643654
644655 log_group = None
645656 log_stream = None
@@ -657,9 +668,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
657668 elif key == AWS_EMF_METRICS_NAMESPACE and value :
658669 namespace = value
659670
660- is_valid = log_group is not None and log_stream is not None
671+ _otlp_log_header_setting_cache = OtlpLogHeaderSetting (log_group , log_stream , namespace )
672+ return _otlp_log_header_setting_cache
661673
662- return OtlpLogHeaderSetting (log_group , log_stream , namespace , is_valid )
674+
675+ def _clear_logs_header_cache ():
676+ """Clear the singleton cache for OtlpLogHeaderSetting. Used primarily for testing."""
677+ global _otlp_log_header_setting_cache # pylint: disable=global-statement
678+ _otlp_log_header_setting_cache = None
663679
664680
665681def _get_metric_export_interval ():
@@ -772,26 +788,26 @@ def _check_emf_exporter_enabled() -> bool:
772788
773789 return True
774790
791+
775792def _create_emf_exporter ():
776793 """
777794 Create the appropriate EMF exporter based on the environment and configuration.
778795
796+ Returns:
797+ ConsoleEmfExporter for Lambda without log headers log group and stream
798+ AwsCloudWatchEmfExporter for other cases (when conditions are met)
799+ None if CloudWatch exporter cannot be created
779800 """
780- log_header_setting = _validate_and_fetch_logs_header ()
781-
782- if _is_lambda_environment () and not log_header_setting . is_valid :
801+ try :
802+ log_header_setting = _fetch_logs_header ()
803+
783804 # Lambda without valid logs http headers - use Console EMF exporter
784- from amazon .opentelemetry .distro .exporter .aws .metrics .console_emf_exporter import (
785- ConsoleEmfExporter ,
786- )
787- return ConsoleEmfExporter (namespace = log_header_setting .namespace )
788- else :
789- # Non-Lambda environment - use CloudWatch EMF exporter
790- return _create_cloudwatch_emf_exporter ()
805+ if _is_lambda_environment () and not log_header_setting .is_valid ():
806+ # pylint: disable=import-outside-toplevel
807+ from amazon .opentelemetry .distro .exporter .aws .metrics .console_emf_exporter import ConsoleEmfExporter
808+ return ConsoleEmfExporter (namespace = log_header_setting .namespace )
791809
792- def _create_cloudwatch_emf_exporter ():
793- """Create and configure the CloudWatch EMF exporter."""
794- try :
810+ # For non-Lambda environment or Lambda with valid headers - use CloudWatch EMF exporter
795811 session = get_aws_session ()
796812 # Check if botocore is available before importing the EMF exporter
797813 if not session :
@@ -803,9 +819,7 @@ def _create_cloudwatch_emf_exporter():
803819 AwsCloudWatchEmfExporter ,
804820 )
805821
806- log_header_setting = _validate_and_fetch_logs_header ()
807-
808- if not log_header_setting .is_valid :
822+ if not log_header_setting .is_valid ():
809823 return None
810824
811825 return AwsCloudWatchEmfExporter (
0 commit comments