Skip to content

Commit c6cef17

Browse files
committed
Implementing OtelJMX provider for JMX metrics insights
1 parent 6eac60e commit c6cef17

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public abstract class ContractTestBase {
8080
.withEnv("OTEL_METRIC_EXPORT_INTERVAL", "100") // 100 ms
8181
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "true")
8282
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", isRuntimeEnabled())
83+
.withEnv("OTEL_JMX_ENABLED", isOtelJVMEnabled())
8384
.withEnv("OTEL_METRICS_EXPORTER", "none")
8485
.withEnv("OTEL_BSP_SCHEDULE_DELAY", "0") // Don't wait to export spans to the collector
8586
.withEnv(
@@ -164,4 +165,8 @@ protected String getApplicationOtelResourceAttributes() {
164165
protected String isRuntimeEnabled() {
165166
return "false";
166167
}
168+
169+
protected String isOtelJVMEnabled() {
170+
return "false";
171+
}
167172
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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+
}

awsagentprovider/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
software.amazon.opentelemetry.javaagent.providers.AwsAgentPropertiesCustomizerProvider
1717
software.amazon.opentelemetry.javaagent.providers.AwsTracerCustomizerProvider
1818
software.amazon.opentelemetry.javaagent.providers.AwsApplicationSignalsCustomizerProvider
19+
software.amazon.opentelemetry.javaagent.providers.OtelJMXMetricsCustomizerProvider

0 commit comments

Comments
 (0)