Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/daily_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ jobs:
id: high_scan
uses: ./.github/actions/image_scan
with:
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-python:v0.10.0"
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-python:v0.10.1"
severity: 'CRITICAL,HIGH'

- name: Perform low image scan
if: always()
id: low_scan
uses: ./.github/actions/image_scan
with:
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-python:v0.10.0"
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-python:v0.10.1"
severity: 'MEDIUM,LOW,UNKNOWN'

- name: Configure AWS Credentials for emitting metrics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
AwsMetricAttributesSpanExporterBuilder,
)
from amazon.opentelemetry.distro.aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
from amazon.opentelemetry.distro.exporter.console.logs.compact_console_log_exporter import CompactConsoleLogExporter
from amazon.opentelemetry.distro.otlp_udp_exporter import OTLPUdpSpanExporter
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
from amazon.opentelemetry.distro.scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
Expand All @@ -46,7 +47,7 @@
)
from opentelemetry.sdk._events import EventLoggerProvider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, LogExporter
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter, LogExporter
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
Expand Down Expand Up @@ -122,7 +123,14 @@ class OtlpLogHeaderSetting(NamedTuple):
log_group: Optional[str]
log_stream: Optional[str]
namespace: Optional[str]
is_valid: bool

def is_valid(self) -> bool:
"""Check if the log header setting is valid by ensuring both log_group and log_stream are present."""
return self.log_group is not None and self.log_stream is not None


# Singleton cache for OtlpLogHeaderSetting
_otlp_log_header_setting_cache: Optional[OtlpLogHeaderSetting] = None


class AwsOpenTelemetryConfigurator(_OTelSDKConfigurator):
Expand Down Expand Up @@ -209,6 +217,9 @@ def _init_logging(
set_logger_provider(provider)

for _, exporter_class in exporters.items():
if exporter_class is ConsoleLogExporter and _is_lambda_environment():
exporter_class = CompactConsoleLogExporter
_logger.debug("Lambda environment detected, using CompactConsoleLogExporter instead of ConsoleLogExporter")
exporter_args = {}
_customize_log_record_processor(
logger_provider=provider, log_exporter=_customize_logs_exporter(exporter_class(**exporter_args))
Expand Down Expand Up @@ -440,7 +451,7 @@ def _customize_logs_exporter(log_exporter: LogExporter) -> LogExporter:

if isinstance(log_exporter, OTLPLogExporter):

if _validate_and_fetch_logs_header().is_valid:
if _fetch_logs_header().is_valid():
endpoint, region = _extract_endpoint_and_region_from_otlp_endpoint(logs_endpoint)
# Setting default compression mode to Gzip as this is the behavior in upstream's
# collector otlp http exporter:
Expand Down Expand Up @@ -627,18 +638,23 @@ def _extract_endpoint_and_region_from_otlp_endpoint(endpoint: str):
return endpoint, region


def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
"""Checks if x-aws-log-group and x-aws-log-stream are present in the headers in order to send logs to
AWS OTLP Logs endpoint."""
def _fetch_logs_header() -> OtlpLogHeaderSetting:
"""Returns the OTLP log header setting as a singleton instance."""
global _otlp_log_header_setting_cache # pylint: disable=global-statement

if _otlp_log_header_setting_cache is not None:
return _otlp_log_header_setting_cache

logs_headers = os.environ.get(OTEL_EXPORTER_OTLP_LOGS_HEADERS)

if not logs_headers:
_logger.warning(
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
"to include x-aws-log-group and x-aws-log-stream"
)
return OtlpLogHeaderSetting(None, None, None, False)
if not _is_lambda_environment():
_logger.warning(
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS "
"to include x-aws-log-group and x-aws-log-stream"
)
_otlp_log_header_setting_cache = OtlpLogHeaderSetting(None, None, None)
return _otlp_log_header_setting_cache

log_group = None
log_stream = None
Expand All @@ -656,9 +672,14 @@ def _validate_and_fetch_logs_header() -> OtlpLogHeaderSetting:
elif key == AWS_EMF_METRICS_NAMESPACE and value:
namespace = value

is_valid = log_group is not None and log_stream is not None
_otlp_log_header_setting_cache = OtlpLogHeaderSetting(log_group, log_stream, namespace)
return _otlp_log_header_setting_cache


return OtlpLogHeaderSetting(log_group, log_stream, namespace, is_valid)
def _clear_logs_header_cache():
"""Clear the singleton cache for OtlpLogHeaderSetting. Used primarily for testing."""
global _otlp_log_header_setting_cache # pylint: disable=global-statement
_otlp_log_header_setting_cache = None


def _get_metric_export_interval():
Expand Down Expand Up @@ -773,8 +794,25 @@ def _check_emf_exporter_enabled() -> bool:


def _create_emf_exporter():
"""Create and configure the CloudWatch EMF exporter."""
"""
Create the appropriate EMF exporter based on the environment and configuration.

Returns:
ConsoleEmfExporter for Lambda without log headers log group and stream
AwsCloudWatchEmfExporter for other cases (when conditions are met)
None if CloudWatch exporter cannot be created
"""
try:
log_header_setting = _fetch_logs_header()

# Lambda without valid logs http headers - use Console EMF exporter
if _is_lambda_environment() and not log_header_setting.is_valid():
# pylint: disable=import-outside-toplevel
from amazon.opentelemetry.distro.exporter.aws.metrics.console_emf_exporter import ConsoleEmfExporter

return ConsoleEmfExporter(namespace=log_header_setting.namespace)

# For non-Lambda environment or Lambda with valid headers - use CloudWatch EMF exporter
session = get_aws_session()
# Check if botocore is available before importing the EMF exporter
if not session:
Expand All @@ -786,9 +824,7 @@ def _create_emf_exporter():
AwsCloudWatchEmfExporter,
)

log_header_setting = _validate_and_fetch_logs_header()

if not log_header_setting.is_valid:
if not log_header_setting.is_valid():
return None

return AwsCloudWatchEmfExporter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import importlib
import os
import sys
from logging import Logger, getLogger
from logging import ERROR, Logger, getLogger

from amazon.opentelemetry.distro._utils import get_aws_region, is_agent_observability_enabled
from amazon.opentelemetry.distro.aws_opentelemetry_configurator import (
Expand All @@ -22,12 +22,17 @@
from opentelemetry import propagate
from opentelemetry.distro import OpenTelemetryDistro
from opentelemetry.environment_variables import OTEL_PROPAGATORS, OTEL_PYTHON_ID_GENERATOR
from opentelemetry.instrumentation.auto_instrumentation import _load
from opentelemetry.instrumentation.logging import LEVELS
from opentelemetry.instrumentation.logging.environment_variables import OTEL_PYTHON_LOG_LEVEL
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
OTEL_EXPORTER_OTLP_PROTOCOL,
)

_logger: Logger = getLogger(__name__)
# Suppress configurator warnings from auto-instrumentation
_load._logger.setLevel(LEVELS.get(os.environ.get(OTEL_PYTHON_LOG_LEVEL, "error").lower(), ERROR))


class AwsOpenTelemetryDistro(OpenTelemetryDistro):
Expand Down
Loading
Loading