23
23
AwsMetricAttributesSpanExporterBuilder ,
24
24
)
25
25
from amazon .opentelemetry .distro .aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
26
+ from amazon .opentelemetry .distro .exporter .console .logs .compact_console_log_exporter import CompactConsoleLogExporter
26
27
from amazon .opentelemetry .distro .otlp_udp_exporter import OTLPUdpSpanExporter
27
28
from amazon .opentelemetry .distro .sampler .aws_xray_remote_sampler import AwsXRayRemoteSampler
28
29
from amazon .opentelemetry .distro .scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
46
47
)
47
48
from opentelemetry .sdk ._events import EventLoggerProvider
48
49
from 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
50
51
from opentelemetry .sdk .environment_variables import (
51
52
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED ,
52
53
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL ,
@@ -122,7 +123,14 @@ class OtlpLogHeaderSetting(NamedTuple):
122
123
log_group : Optional [str ]
123
124
log_stream : Optional [str ]
124
125
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
126
134
127
135
128
136
class AwsOpenTelemetryConfigurator (_OTelSDKConfigurator ):
@@ -209,6 +217,9 @@ def _init_logging(
209
217
set_logger_provider (provider )
210
218
211
219
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" )
212
223
exporter_args = {}
213
224
_customize_log_record_processor (
214
225
logger_provider = provider , log_exporter = _customize_logs_exporter (exporter_class (** exporter_args ))
@@ -440,7 +451,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:
440
451
441
452
if isinstance (log_exporter , OTLPLogExporter ):
442
453
443
- if _validate_and_fetch_logs_header ().is_valid :
454
+ if _fetch_logs_header ().is_valid () :
444
455
endpoint , region = _extract_endpoint_and_region_from_otlp_endpoint (logs_endpoint )
445
456
# Setting default compression mode to Gzip as this is the behavior in upstream's
446
457
# collector otlp http exporter:
@@ -627,18 +638,23 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
627
638
return endpoint , region
628
639
629
640
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
633
647
634
648
logs_headers = os .environ .get (OTEL_EXPORTER_OTLP_LOGS_HEADERS )
635
649
636
650
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
642
658
643
659
log_group = None
644
660
log_stream = None
@@ -656,9 +672,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
656
672
elif key == AWS_EMF_METRICS_NAMESPACE and value :
657
673
namespace = value
658
674
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
+
660
678
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
662
683
663
684
664
685
def _get_metric_export_interval ():
@@ -773,8 +794,25 @@ def _check_emf_exporter_enabled() -> bool:
773
794
774
795
775
796
def _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
+ """
777
805
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
778
816
session = get_aws_session ()
779
817
# Check if botocore is available before importing the EMF exporter
780
818
if not session :
@@ -786,9 +824,7 @@ def _create_emf_exporter():
786
824
AwsCloudWatchEmfExporter ,
787
825
)
788
826
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 ():
792
828
return None
793
829
794
830
return AwsCloudWatchEmfExporter (
0 commit comments