33# Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
44import os
55from logging import Logger , getLogger
6- from typing import ClassVar , Dict , Type
6+ from typing import ClassVar , Dict , List , Type , Union
77
88from importlib_metadata import version
99from typing_extensions import override
1010
11+ from amazon .opentelemetry .distro ._aws_attribute_keys import AWS_LOCAL_SERVICE
12+ from amazon .opentelemetry .distro ._aws_resource_attribute_configurator import get_service_attribute
1113from amazon .opentelemetry .distro .always_record_sampler import AlwaysRecordSampler
1214from amazon .opentelemetry .distro .attribute_propagating_span_processor_builder import (
1315 AttributePropagatingSpanProcessorBuilder ,
1921from amazon .opentelemetry .distro .aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
2022from amazon .opentelemetry .distro .otlp_udp_exporter import OTLPUdpSpanExporter
2123from amazon .opentelemetry .distro .sampler .aws_xray_remote_sampler import AwsXRayRemoteSampler
24+ from amazon .opentelemetry .distro .scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
25+ from amazon .opentelemetry .distro .scope_based_filtering_view import ScopeBasedRetainingView
2226from opentelemetry .exporter .otlp .proto .http .metric_exporter import OTLPMetricExporter as OTLPHttpOTLPMetricExporter
2327from opentelemetry .exporter .otlp .proto .http .trace_exporter import OTLPSpanExporter
28+ from opentelemetry .metrics import set_meter_provider
2429from opentelemetry .sdk ._configuration import (
2530 _get_exporter_names ,
2631 _get_id_generator ,
2934 _import_id_generator ,
3035 _import_sampler ,
3136 _init_logging ,
32- _init_metrics ,
3337 _OTelSDKConfigurator ,
3438)
3539from opentelemetry .sdk .environment_variables import (
5054 ObservableUpDownCounter ,
5155 UpDownCounter ,
5256)
53- from opentelemetry .sdk .metrics .export import AggregationTemporality , PeriodicExportingMetricReader
57+ from opentelemetry .sdk .metrics .export import (
58+ AggregationTemporality ,
59+ MetricExporter ,
60+ MetricReader ,
61+ PeriodicExportingMetricReader ,
62+ )
63+ from opentelemetry .sdk .metrics .view import LastValueAggregation , View
5464from opentelemetry .sdk .resources import Resource , get_aggregated_resources
5565from opentelemetry .sdk .trace import TracerProvider
5666from opentelemetry .sdk .trace .export import BatchSpanProcessor , SpanExporter
5969from opentelemetry .semconv .resource import ResourceAttributes
6070from opentelemetry .trace import set_tracer_provider
6171
62- APP_SIGNALS_ENABLED_CONFIG = "OTEL_AWS_APP_SIGNALS_ENABLED"
72+ DEPRECATED_APP_SIGNALS_ENABLED_CONFIG = "OTEL_AWS_APP_SIGNALS_ENABLED"
6373APPLICATION_SIGNALS_ENABLED_CONFIG = "OTEL_AWS_APPLICATION_SIGNALS_ENABLED"
64- APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG = "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT"
74+ APPLICATION_SIGNALS_RUNTIME_ENABLED_CONFIG = "OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED"
75+ DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG = "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT"
6576APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG = "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT"
6677METRIC_EXPORT_INTERVAL_CONFIG = "OTEL_METRIC_EXPORT_INTERVAL"
6778DEFAULT_METRIC_EXPORT_INTERVAL = 60000.0
6879AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME"
6980AWS_XRAY_DAEMON_ADDRESS_CONFIG = "AWS_XRAY_DAEMON_ADDRESS"
7081OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED_CONFIG = "OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED"
82+ SYSTEM_METRICS_INSTRUMENTATION_SCOPE_NAME = "opentelemetry.instrumentation.system_metrics"
7183OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
7284# UDP package size is not larger than 64KB
7385LAMBDA_SPAN_EXPORT_BATCH_SIZE = 10
@@ -127,7 +139,7 @@ def _initialize_components():
127139 else []
128140 )
129141
130- resource = get_aggregated_resources (resource_detectors ).merge (Resource .create (auto_resource ))
142+ resource = _customize_resource ( get_aggregated_resources (resource_detectors ).merge (Resource .create (auto_resource ) ))
131143
132144 sampler_name = _get_sampler ()
133145 sampler = _custom_import_sampler (sampler_name , resource )
@@ -171,6 +183,27 @@ def _init_tracing(
171183 set_tracer_provider (trace_provider )
172184
173185
186+ def _init_metrics (
187+ exporters_or_readers : Dict [str , Union [Type [MetricExporter ], Type [MetricReader ]]],
188+ resource : Resource = None ,
189+ ):
190+ metric_readers = []
191+ views = []
192+
193+ for _ , exporter_or_reader_class in exporters_or_readers .items ():
194+ exporter_args = {}
195+
196+ if issubclass (exporter_or_reader_class , MetricReader ):
197+ metric_readers .append (exporter_or_reader_class (** exporter_args ))
198+ else :
199+ metric_readers .append (PeriodicExportingMetricReader (exporter_or_reader_class (** exporter_args )))
200+
201+ _customize_metric_exporters (metric_readers , views )
202+
203+ provider = MeterProvider (resource = resource , metric_readers = metric_readers , views = views )
204+ set_meter_provider (provider )
205+
206+
174207# END The OpenTelemetry Authors code
175208
176209
@@ -303,14 +336,9 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
303336 # Construct meterProvider
304337 _logger .info ("AWS Application Signals enabled" )
305338 otel_metric_exporter = ApplicationSignalsExporterProvider ().create_exporter ()
306- export_interval_millis = float (os .environ .get (METRIC_EXPORT_INTERVAL_CONFIG , DEFAULT_METRIC_EXPORT_INTERVAL ))
307- _logger .debug ("Span Metrics export interval: %s" , export_interval_millis )
308- # Cap export interval to 60 seconds. This is currently required for metrics-trace correlation to work correctly.
309- if export_interval_millis > DEFAULT_METRIC_EXPORT_INTERVAL :
310- export_interval_millis = DEFAULT_METRIC_EXPORT_INTERVAL
311- _logger .info ("AWS Application Signals metrics export interval capped to %s" , export_interval_millis )
339+
312340 periodic_exporting_metric_reader = PeriodicExportingMetricReader (
313- exporter = otel_metric_exporter , export_interval_millis = export_interval_millis
341+ exporter = otel_metric_exporter , export_interval_millis = _get_metric_export_interval ()
314342 )
315343 meter_provider : MeterProvider = MeterProvider (resource = resource , metric_readers = [periodic_exporting_metric_reader ])
316344 # Construct and set application signals metrics processor
@@ -319,25 +347,106 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
319347 return
320348
321349
350+ def _customize_metric_exporters (metric_readers : List [MetricReader ], views : List [View ]) -> None :
351+ if _is_application_signals_runtime_enabled ():
352+ _get_runtime_metric_views (views , 0 == len (metric_readers ))
353+
354+ application_signals_metric_exporter = ApplicationSignalsExporterProvider ().create_exporter ()
355+ scope_based_periodic_exporting_metric_reader = ScopeBasedPeriodicExportingMetricReader (
356+ exporter = application_signals_metric_exporter ,
357+ export_interval_millis = _get_metric_export_interval (),
358+ registered_scope_names = {SYSTEM_METRICS_INSTRUMENTATION_SCOPE_NAME },
359+ )
360+ metric_readers .append (scope_based_periodic_exporting_metric_reader )
361+
362+
363+ def _get_runtime_metric_views (views : List [View ], retain_runtime_only : bool ) -> None :
364+ runtime_metrics_scope_name = SYSTEM_METRICS_INSTRUMENTATION_SCOPE_NAME
365+ _logger .info ("Registered scope %s" , runtime_metrics_scope_name )
366+ views .append (
367+ View (
368+ instrument_name = "system.network.connections" ,
369+ meter_name = runtime_metrics_scope_name ,
370+ aggregation = LastValueAggregation (),
371+ )
372+ )
373+ views .append (
374+ View (
375+ instrument_name = "process.open_file_descriptor.count" ,
376+ meter_name = runtime_metrics_scope_name ,
377+ aggregation = LastValueAggregation (),
378+ )
379+ )
380+ views .append (
381+ View (
382+ instrument_name = "process.runtime.*.memory" ,
383+ meter_name = runtime_metrics_scope_name ,
384+ aggregation = LastValueAggregation (),
385+ )
386+ )
387+ views .append (
388+ View (
389+ instrument_name = "process.runtime.*.gc_count" ,
390+ meter_name = runtime_metrics_scope_name ,
391+ aggregation = LastValueAggregation (),
392+ )
393+ )
394+ views .append (
395+ View (
396+ instrument_name = "process.runtime.*.thread_count" ,
397+ meter_name = runtime_metrics_scope_name ,
398+ aggregation = LastValueAggregation (),
399+ )
400+ )
401+ if retain_runtime_only :
402+ views .append (ScopeBasedRetainingView (meter_name = runtime_metrics_scope_name ))
403+
404+
322405def _customize_versions (auto_resource : Dict [str , any ]) -> Dict [str , any ]:
323406 distro_version = version ("aws-opentelemetry-distro" )
324407 auto_resource [ResourceAttributes .TELEMETRY_AUTO_VERSION ] = distro_version + "-aws"
325408 _logger .debug ("aws-opentelementry-distro - version: %s" , auto_resource [ResourceAttributes .TELEMETRY_AUTO_VERSION ])
326409 return auto_resource
327410
328411
412+ def _customize_resource (resource : Resource ) -> Resource :
413+ service_name , is_unknown = get_service_attribute (resource )
414+ if is_unknown :
415+ _logger .debug ("No valid service name found" )
416+
417+ return resource .merge (Resource .create ({AWS_LOCAL_SERVICE : service_name }))
418+
419+
329420def _is_application_signals_enabled ():
330421 return (
331- os .environ .get (APPLICATION_SIGNALS_ENABLED_CONFIG , os .environ .get (APP_SIGNALS_ENABLED_CONFIG , "false" )).lower ()
422+ os .environ .get (
423+ APPLICATION_SIGNALS_ENABLED_CONFIG , os .environ .get (DEPRECATED_APP_SIGNALS_ENABLED_CONFIG , "false" )
424+ ).lower ()
332425 == "true"
333426 )
334427
335428
429+ def _is_application_signals_runtime_enabled ():
430+ return _is_application_signals_enabled () and (
431+ os .environ .get (APPLICATION_SIGNALS_RUNTIME_ENABLED_CONFIG , "true" ).lower () == "true"
432+ )
433+
434+
336435def _is_lambda_environment ():
337436 # detect if running in AWS Lambda environment
338437 return AWS_LAMBDA_FUNCTION_NAME_CONFIG in os .environ
339438
340439
440+ def _get_metric_export_interval ():
441+ export_interval_millis = float (os .environ .get (METRIC_EXPORT_INTERVAL_CONFIG , DEFAULT_METRIC_EXPORT_INTERVAL ))
442+ _logger .debug ("Span Metrics export interval: %s" , export_interval_millis )
443+ # Cap export interval to 60 seconds. This is currently required for metrics-trace correlation to work correctly.
444+ if export_interval_millis > DEFAULT_METRIC_EXPORT_INTERVAL :
445+ export_interval_millis = DEFAULT_METRIC_EXPORT_INTERVAL
446+ _logger .info ("AWS Application Signals metrics export interval capped to %s" , export_interval_millis )
447+ return export_interval_millis
448+
449+
341450def _span_export_batch_size ():
342451 return LAMBDA_SPAN_EXPORT_BATCH_SIZE if _is_lambda_environment () else None
343452
@@ -372,7 +481,7 @@ def create_exporter(self):
372481 if protocol == "http/protobuf" :
373482 application_signals_endpoint = os .environ .get (
374483 APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG ,
375- os .environ .get (APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG , "http://localhost:4316/v1/metrics" ),
484+ os .environ .get (DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG , "http://localhost:4316/v1/metrics" ),
376485 )
377486 _logger .debug ("AWS Application Signals export endpoint: %s" , application_signals_endpoint )
378487 return OTLPHttpOTLPMetricExporter (
@@ -388,7 +497,7 @@ def create_exporter(self):
388497
389498 application_signals_endpoint = os .environ .get (
390499 APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG ,
391- os .environ .get (APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG , "localhost:4315" ),
500+ os .environ .get (DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG , "localhost:4315" ),
392501 )
393502 _logger .debug ("AWS Application Signals export endpoint: %s" , application_signals_endpoint )
394503 return OTLPGrpcOTLPMetricExporter (
0 commit comments