@@ -125,7 +125,14 @@ class OtlpLogHeaderSetting(NamedTuple):
125
125
log_group : Optional [str ]
126
126
log_stream : Optional [str ]
127
127
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
129
136
130
137
131
138
class AwsOpenTelemetryConfigurator (_OTelSDKConfigurator ):
@@ -446,7 +453,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
446
453
447
454
if isinstance (log_exporter , OTLPLogExporter ):
448
455
449
- if _validate_and_fetch_logs_header ().is_valid :
456
+ if _fetch_logs_header ().is_valid () :
450
457
endpoint , region = _extract_endpoint_and_region_from_otlp_endpoint (logs_endpoint )
451
458
# Setting default compression mode to Gzip as this is the behavior in upstream's
452
459
# collector otlp http exporter:
@@ -633,18 +640,23 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
633
640
return endpoint , region
634
641
635
642
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
639
649
640
650
logs_headers = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_HEADERS )
641
651
642
652
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
648
660
649
661
log_group = None
650
662
log_stream = None
@@ -662,9 +674,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
662
674
elif key == AWS_EMF_METRICS_NAMESPACE and value :
663
675
namespace = value
664
676
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
+
666
680
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
668
685
669
686
670
687
def _get_metric_export_interval ():
@@ -779,8 +796,25 @@ def _check_emf_exporter_enabled() -> bool:
779
796
780
797
781
798
def _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
+ """
783
807
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
784
818
session = get_aws_session ()
785
819
# Check if botocore is available before importing the EMF exporter
786
820
if not session :
@@ -792,9 +826,7 @@ def _create_emf_exporter():
792
826
AwsCloudWatchEmfExporter ,
793
827
)
794
828
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 ():
798
830
return None
799
831
800
832
return AwsCloudWatchEmfExporter (
0 commit comments