-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Application Signals runtime metrics (#244)
## Feature request Add runtime metrics collection into Application Signals. Runtime metrics will be enabled by default if `OTEL_AWS_APPLICATION_SIGNALS_ENABLED` is `true`, and can be disabled separately by setting `OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED` to `false`. ## Description of changes: 1. Add `ScopeBasedPeriodicExportingMetricReader` to copy metrics from `opentelemetry.instrumentation.system_metrics` instrumentation scope to Application Signals exporter. 2. Set `aws.local.service` into resource attributes. 3. Add views to workaround open-telemetry/opentelemetry-python-contrib#2861. 4. Add contract testing for runtime metrics. ## Result example ``` { "resource_metrics": [ { "resource": { "attributes": { "telemetry.sdk.language": "python", "telemetry.sdk.name": "opentelemetry", "telemetry.sdk.version": "1.25.0", "service.name": "unknown_service", "cloud.provider": "aws", "cloud.platform": "aws_ec2", "cloud.account.id": "633750930120", "cloud.region": "us-east-1", "cloud.availability_zone": "us-east-1a", "host.id": "i-03ff80a878a803e0e", "host.type": "t2.medium", "host.name": "ip-172-31-25-215.ec2.internal", "telemetry.auto.version": "0.3.0.dev0-aws", "aws.local.service": "UnknownService" }, "schema_url": "" }, "scope_metrics": [ { "scope": { "name": "opentelemetry.instrumentation.system_metrics", "version": "0.46b0", "schema_url": "https://opentelemetry.io/schemas/1.11.0" }, "metrics": [ { "name": "process.runtime.cpython.memory", "description": "Runtime cpython memory", "unit": "bytes", "data": { "data_points": [ { "attributes": { "type": "rss" }, "start_time_unix_nano": 1724953385390606423, "time_unix_nano": 1724953385391126083, "value": 75747328 }, { "attributes": { "type": "vms" }, "start_time_unix_nano": 1724953385390606423, "time_unix_nano": 1724953385391126083, "value": 546709504 } ], "aggregation_temporality": 2, "is_monotonic": false } } ] } ] } ] } ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
- Loading branch information
Showing
12 changed files
with
519 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
...ntelemetry-distro/src/amazon/opentelemetry/distro/_aws_resource_attribute_configurator.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
from amazon.opentelemetry.distro._aws_span_processing_util import UNKNOWN_SERVICE | ||
from opentelemetry.sdk.resources import SERVICE_NAME, Resource | ||
|
||
# As per https://opentelemetry.io/docs/specs/semconv/resource/#service, if service name is not specified, SDK defaults | ||
# the service name to unknown_service:<process name> or just unknown_service. | ||
_OTEL_UNKNOWN_SERVICE_PREFIX: str = "unknown_service" | ||
|
||
|
||
def get_service_attribute(resource: Resource) -> (str, bool): | ||
"""Service is always derived from SERVICE_NAME""" | ||
service: str = resource.attributes.get(SERVICE_NAME) | ||
|
||
# In practice the service name is never None, but we can be defensive here. | ||
if service is None or service.startswith(_OTEL_UNKNOWN_SERVICE_PREFIX): | ||
return UNKNOWN_SERVICE, True | ||
|
||
return service, False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
aws-opentelemetry-distro/src/amazon/opentelemetry/distro/scope_based_exporter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
from logging import Logger, getLogger | ||
from typing import Optional, Set | ||
|
||
from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY, attach, detach, set_value | ||
from opentelemetry.sdk.metrics.export import MetricExporter, MetricsData, PeriodicExportingMetricReader, ResourceMetrics | ||
|
||
_logger: Logger = getLogger(__name__) | ||
|
||
|
||
class ScopeBasedPeriodicExportingMetricReader(PeriodicExportingMetricReader): | ||
|
||
def __init__( | ||
self, | ||
exporter: MetricExporter, | ||
export_interval_millis: Optional[float] = None, | ||
export_timeout_millis: Optional[float] = None, | ||
registered_scope_names: Set[str] = None, | ||
): | ||
super().__init__(exporter, export_interval_millis, export_timeout_millis) | ||
self._registered_scope_names = registered_scope_names | ||
|
||
def _receive_metrics( | ||
self, | ||
metrics_data: MetricsData, | ||
timeout_millis: float = 10_000, | ||
**kwargs, | ||
) -> None: | ||
|
||
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) | ||
# pylint: disable=broad-exception-caught,invalid-name | ||
try: | ||
with self._export_lock: | ||
exporting_resource_metrics = [] | ||
for metric in metrics_data.resource_metrics: | ||
exporting_scope_metrics = [] | ||
for scope_metric in metric.scope_metrics: | ||
if scope_metric.scope.name in self._registered_scope_names: | ||
exporting_scope_metrics.append(scope_metric) | ||
if len(exporting_scope_metrics) > 0: | ||
exporting_resource_metrics.append( | ||
ResourceMetrics( | ||
resource=metric.resource, | ||
scope_metrics=exporting_scope_metrics, | ||
schema_url=metric.schema_url, | ||
) | ||
) | ||
if len(exporting_resource_metrics) > 0: | ||
new_metrics_data = MetricsData(resource_metrics=exporting_resource_metrics) | ||
self._exporter.export(new_metrics_data, timeout_millis=timeout_millis) | ||
except Exception as e: | ||
_logger.exception("Exception while exporting metrics %s", str(e)) | ||
detach(token) |
Oops, something went wrong.