Skip to content

Support AppSignals metrics transport protocol configuration. #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 23, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
import os
from logging import Logger, getLogger
from typing import Dict, Type
from typing import ClassVar, Dict, Type

from typing_extensions import override

Expand All @@ -15,7 +15,8 @@
)
from amazon.opentelemetry.distro.aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter as OTLPGrpcOTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter as OTLPHttpOTLPMetricExporter
from opentelemetry.sdk._configuration import (
_get_exporter_names,
_get_id_generator,
Expand All @@ -29,6 +30,8 @@
)
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_TRACES_SAMPLER_ARG,
)
from opentelemetry.sdk.extension.aws.resource.ec2 import AwsEc2ResourceDetector
Expand All @@ -55,6 +58,7 @@
OTEL_AWS_APP_SIGNALS_ENABLED = "OTEL_AWS_APP_SIGNALS_ENABLED"
OTEL_METRIC_EXPORT_INTERVAL = "OTEL_METRIC_EXPORT_INTERVAL"
OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT = "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT"
OTEL_AWS_SMP_EXPORTER_ENDPOINT = "OTEL_AWS_SMP_EXPORTER_ENDPOINT"
DEFAULT_METRIC_EXPORT_INTERVAL = 60000.0

_logger: Logger = getLogger(__name__)
Expand Down Expand Up @@ -172,41 +176,28 @@ def _custom_import_sampler(sampler_name: str, resource: Resource) -> Sampler:


def _customize_sampler(sampler: Sampler) -> Sampler:
if not is_smp_enabled():
if not is_app_signals_enabled():
return sampler
return AlwaysRecordSampler(sampler)


def _customize_exporter(span_exporter: SpanExporter, resource: Resource) -> SpanExporter:
if not is_smp_enabled():
if not is_app_signals_enabled():
return span_exporter
return AwsMetricAttributesSpanExporterBuilder(span_exporter, resource).build()


def _customize_span_processors(provider: TracerProvider, resource: Resource) -> None:
if not is_smp_enabled():
if not is_app_signals_enabled():
return

# Construct and set local and remote attributes span processor
provider.add_span_processor(AttributePropagatingSpanProcessorBuilder().build())

# Construct meterProvider
temporality_dict: Dict[type, AggregationTemporality] = {}
for typ in [
Counter,
UpDownCounter,
ObservableCounter,
ObservableCounter,
ObservableUpDownCounter,
ObservableGauge,
Histogram,
]:
temporality_dict[typ] = AggregationTemporality.DELTA
_logger.info("Span Metrics Processor enabled")
smp_endpoint = os.environ.get(OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT, "http://localhost:4315")
otel_metric_exporter = OTLPMetricExporter(endpoint=smp_endpoint, preferred_temporality=temporality_dict)
_logger.info("AWS AppSignals enabled")
otel_metric_exporter = AppSignalsExporterProvider().create_exporter()
export_interval_millis = float(os.environ.get(OTEL_METRIC_EXPORT_INTERVAL, DEFAULT_METRIC_EXPORT_INTERVAL))
_logger.debug("Span Metrics endpoint: %s", smp_endpoint)
_logger.debug("Span Metrics export interval: %s", export_interval_millis)
# Cap export interval to 60 seconds. This is currently required for metrics-trace correlation to work correctly.
if export_interval_millis > DEFAULT_METRIC_EXPORT_INTERVAL:
Expand All @@ -216,11 +207,53 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
exporter=otel_metric_exporter, export_interval_millis=export_interval_millis
)
meter_provider: MeterProvider = MeterProvider(resource=resource, metric_readers=[periodic_exporting_metric_reader])
# Construct and set span metrics processor
# Construct and set AppSignals metrics processor
provider.add_span_processor(AwsSpanMetricsProcessorBuilder(meter_provider, resource).build())

return


def is_smp_enabled():
def is_app_signals_enabled():
return os.environ.get(OTEL_AWS_APP_SIGNALS_ENABLED, False)


class AppSignalsExporterProvider:
_instance: ClassVar["AppSignalsExporterProvider"] = None

def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

# pylint: disable=no-self-use
def create_exporter(self):
protocol = os.environ.get(
OTEL_EXPORTER_OTLP_PROTOCOL, os.environ.get(OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, "grpc")
)
_logger.debug("AppSignals export protocol: %s", protocol)

app_signals_endpoint = os.environ.get(
OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT,
os.environ.get(OTEL_AWS_SMP_EXPORTER_ENDPOINT, "http://localhost:4315"),
)

_logger.debug("AppSignals export endpoint: %s", app_signals_endpoint)

temporality_dict: Dict[type, AggregationTemporality] = {}
for typ in [
Counter,
UpDownCounter,
ObservableCounter,
ObservableCounter,
ObservableUpDownCounter,
ObservableGauge,
Histogram,
]:
temporality_dict[typ] = AggregationTemporality.DELTA

if protocol == "http/protobuf":
return OTLPHttpOTLPMetricExporter(endpoint=app_signals_endpoint, preferred_temporality=temporality_dict)
if protocol == "grpc":
return OTLPGrpcOTLPMetricExporter(endpoint=app_signals_endpoint, preferred_temporality=temporality_dict)

raise RuntimeError(f"Unsupported AppSignals export protocol: {protocol} ")