1+ /*
2+ * Copyright Amazon.com, Inc. or its affiliates.
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License").
5+ * You may not use this file except in compliance with the License.
6+ * A copy of the License is located at
7+ *
8+ * http://aws.amazon.com/apache2.0
9+ *
10+ * or in the "license" file accompanying this file. This file is distributed
11+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+ * express or implied. See the License for the specific language governing
13+ * permissions and limitations under the License.
14+ */
15+
16+ package software .amazon .opentelemetry .javaagent .providers ;
17+
18+ import io .opentelemetry .exporter .otlp .http .metrics .OtlpHttpMetricExporter ;
19+ import io .opentelemetry .exporter .otlp .internal .OtlpConfigUtil ;
20+ import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizer ;
21+ import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizerProvider ;
22+ import io .opentelemetry .sdk .autoconfigure .spi .ConfigProperties ;
23+ import io .opentelemetry .sdk .autoconfigure .spi .ConfigurationException ;
24+ import io .opentelemetry .sdk .autoconfigure .spi .internal .DefaultConfigProperties ;
25+ import io .opentelemetry .sdk .metrics .Aggregation ;
26+ import io .opentelemetry .sdk .metrics .InstrumentSelector ;
27+ import io .opentelemetry .sdk .metrics .InstrumentType ;
28+ import io .opentelemetry .sdk .metrics .SdkMeterProviderBuilder ;
29+ import io .opentelemetry .sdk .metrics .View ;
30+ import io .opentelemetry .sdk .metrics .export .MetricExporter ;
31+ import io .opentelemetry .sdk .metrics .export .MetricReader ;
32+ import java .time .Duration ;
33+ import java .util .HashSet ;
34+ import java .util .Set ;
35+ import java .util .logging .Level ;
36+ import java .util .logging .Logger ;
37+
38+ /**
39+ * You can control when these customizations are applied using the property otel.jmx.enabled or the
40+ * environment variable OTEL_JMX_ENABLED_CONFIG. This flag is disabled by default.
41+ */
42+ public class OtelJMXMetricsCustomizerProvider implements AutoConfigurationCustomizerProvider {
43+ private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration .ofMinutes (1 );
44+ private static final Logger logger =
45+ Logger .getLogger (OtelJMXMetricsCustomizerProvider .class .getName ());
46+
47+ private static final String OTEL_JMX_ENABLED_CONFIG = "otel.jmx.enabled" ;
48+ private static final String OTEL_JMX_ENDPOINT_CONFIG = "otel.jmx.exporter.metrics.endpoint" ;
49+
50+ public void customize (AutoConfigurationCustomizer autoConfiguration ) {
51+ autoConfiguration .addMeterProviderCustomizer (this ::customizeMeterProvider );
52+ }
53+
54+ private boolean isOtelJMXEnabled (ConfigProperties configProps ) {
55+ return configProps .getBoolean (OTEL_JMX_ENABLED_CONFIG );
56+ }
57+
58+ private SdkMeterProviderBuilder customizeMeterProvider (
59+ SdkMeterProviderBuilder sdkMeterProviderBuilder , ConfigProperties configProps ) {
60+
61+ if (isOtelJMXEnabled (configProps )) {
62+ Set <String > registeredScopeNames = new HashSet <>(1 );
63+ String jmxRuntimeScopeName = "io.opentelemetry.jmx" ;
64+ registeredScopeNames .add (jmxRuntimeScopeName );
65+
66+ configureMetricFilter (configProps , sdkMeterProviderBuilder , registeredScopeNames );
67+
68+ MetricExporter metricsExporter = JMXExporterProvider .INSTANCE .createExporter (configProps );
69+ MetricReader metricReader =
70+ ScopeBasedPeriodicMetricReader .create (metricsExporter , registeredScopeNames )
71+ .setInterval (getMetricExportInterval (configProps ))
72+ .build ();
73+ sdkMeterProviderBuilder .registerMetricReader (metricReader );
74+
75+ logger .info ("Otel JMX metric collection enabled" );
76+ }
77+ return sdkMeterProviderBuilder ;
78+ }
79+
80+ private static void configureMetricFilter (
81+ ConfigProperties configProps ,
82+ SdkMeterProviderBuilder sdkMeterProviderBuilder ,
83+ Set <String > registeredScopeNames ) {
84+ Set <String > exporterNames =
85+ DefaultConfigProperties .getSet (configProps , "otel.metrics.exporter" );
86+ if (exporterNames .contains ("none" )) {
87+ for (String scope : registeredScopeNames ) {
88+ sdkMeterProviderBuilder .registerView (
89+ InstrumentSelector .builder ().setMeterName (scope ).build (),
90+ View .builder ().setAggregation (Aggregation .defaultAggregation ()).build ());
91+
92+ logger .log (Level .FINE , "Registered scope {0}" , scope );
93+ }
94+ sdkMeterProviderBuilder .registerView (
95+ InstrumentSelector .builder ().setName ("*" ).build (),
96+ View .builder ().setAggregation (Aggregation .drop ()).build ());
97+ }
98+ }
99+
100+ private static Duration getMetricExportInterval (ConfigProperties configProps ) {
101+ Duration exportInterval =
102+ configProps .getDuration ("otel.metric.export.interval" , DEFAULT_METRIC_EXPORT_INTERVAL );
103+ logger .log (Level .FINE , String .format ("Otel JMX Metrics export interval: %s" , exportInterval ));
104+ // Cap export interval to 60 seconds. This is currently required for metrics-trace correlation
105+ // to work correctly.
106+ if (exportInterval .compareTo (DEFAULT_METRIC_EXPORT_INTERVAL ) > 0 ) {
107+ exportInterval = DEFAULT_METRIC_EXPORT_INTERVAL ;
108+ logger .log (
109+ Level .INFO ,
110+ String .format ("Otel JMX Metrics export interval capped to %s" , exportInterval ));
111+ }
112+ return exportInterval ;
113+ }
114+
115+ private enum JMXExporterProvider {
116+ INSTANCE ;
117+
118+ public MetricExporter createExporter (ConfigProperties configProps ) {
119+ String protocol =
120+ OtlpConfigUtil .getOtlpProtocol (OtlpConfigUtil .DATA_TYPE_METRICS , configProps );
121+ logger .log (Level .FINE , String .format ("Otel JMX metrics export protocol: %s" , protocol ));
122+
123+ String otelJMXEndpoint ;
124+ if (protocol .equals (OtlpConfigUtil .PROTOCOL_HTTP_PROTOBUF )) {
125+ otelJMXEndpoint = configProps .getString (OTEL_JMX_ENDPOINT_CONFIG , "http://localhost:4314" );
126+ logger .log (
127+ Level .FINE , String .format ("Otel JMX metrics export endpoint: %s" , otelJMXEndpoint ));
128+ return OtlpHttpMetricExporter .builder ()
129+ .setEndpoint (otelJMXEndpoint )
130+ .setDefaultAggregationSelector (this ::getAggregation )
131+ .setAggregationTemporalitySelector (CloudWatchTemporalitySelector .alwaysDelta ())
132+ .build ();
133+ }
134+ throw new ConfigurationException ("Unsupported Otel JMX metrics export protocol: " + protocol );
135+ }
136+
137+ private Aggregation getAggregation (InstrumentType instrumentType ) {
138+ if (instrumentType == InstrumentType .HISTOGRAM ) {
139+ return Aggregation .base2ExponentialBucketHistogram ();
140+ }
141+ return Aggregation .defaultAggregation ();
142+ }
143+ }
144+ }
0 commit comments