-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementing OtelJMX metrics exporter for JMX metrics insights (#901)
*Issue #, if available:* *Description of changes:* 1. Implemented custom provider for OtelJMX metrics insights 2. Is enabled when ```AWS_JMX_ENABLED``` is set to true 3. Sets up a metrics exporter over 4314 port only over HTTP protocol *Testing* Tested as part of ```amazon-cloudwatch-observability``` helm-chart 1. Annotate a pod deploying a sprint boot project with ```instrumentation.opentelemetry.io/inject-java: "true" cloudwatch.aws.amazon.com/inject-jmx-jvm: "true"``` and deploy on the cluster 2. Deploy the helm-chart with a custom amazon-cloudwatch-operator build to inject the following environment vars ``` OTEL_EXPORTER_OTLP_PROTOCOL OTEL_METRICS_EXPORTER OTEL_LOGS_EXPORTER AWS_JMX_EXPORTER_METRICS_ENDPOINT OTEL_JMX_TARGET_SYSTEM ```` 3. Verified AwsJMX exporter is enabled in pod logs ``` INFO software.amazon.opentelemetry.javaagent.providers.AwsJMXMetricsCustomizerProvider - AWS JMX metric collection enabled ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
- Loading branch information
1 parent
e144053
commit 81e799b
Showing
4 changed files
with
185 additions
and
44 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
112 changes: 112 additions & 0 deletions
112
...va/software/amazon/opentelemetry/javaagent/providers/AwsJMXMetricsCustomizerProvider.java
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,112 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package software.amazon.opentelemetry.javaagent.providers; | ||
|
||
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; | ||
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil; | ||
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; | ||
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; | ||
import io.opentelemetry.sdk.metrics.Aggregation; | ||
import io.opentelemetry.sdk.metrics.InstrumentType; | ||
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; | ||
import io.opentelemetry.sdk.metrics.export.MetricExporter; | ||
import io.opentelemetry.sdk.metrics.export.MetricReader; | ||
import java.time.Duration; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
/** | ||
* You can control when these customizations are applied using both the properties - | ||
* otel.jmx.enabled and otel.aws.jmx.exporter.metrics.endpoint or the environment variable | ||
* OTEL_JMX_ENABLED_CONFIG and AWS_JMX_EXPORTER_ENDPOINT_CONFIG. These flags are disabled by | ||
* default. | ||
*/ | ||
public class AwsJMXMetricsCustomizerProvider implements AutoConfigurationCustomizerProvider { | ||
private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1); | ||
private static final Logger logger = | ||
Logger.getLogger(AwsJMXMetricsCustomizerProvider.class.getName()); | ||
|
||
private static final String OTEL_JMX_ENABLED_CONFIG = "otel.jmx.enabled"; | ||
private static final String AWS_JMX_EXPORTER_ENDPOINT_CONFIG = | ||
"otel.aws.jmx.exporter.metrics.endpoint"; | ||
|
||
public void customize(AutoConfigurationCustomizer autoConfiguration) { | ||
autoConfiguration.addMeterProviderCustomizer(this::customizeMeterProvider); | ||
} | ||
|
||
private boolean isOtelJMXEnabled(ConfigProperties configProps) { | ||
return configProps.getBoolean(OTEL_JMX_ENABLED_CONFIG, true) | ||
&& configProps.getString(AWS_JMX_EXPORTER_ENDPOINT_CONFIG, "") != ""; | ||
} | ||
|
||
private SdkMeterProviderBuilder customizeMeterProvider( | ||
SdkMeterProviderBuilder sdkMeterProviderBuilder, ConfigProperties configProps) { | ||
|
||
if (isOtelJMXEnabled(configProps)) { | ||
Set<String> registeredScopeNames = new HashSet<>(1); | ||
String jmxRuntimeScopeName = "io.opentelemetry.jmx"; | ||
registeredScopeNames.add(jmxRuntimeScopeName); | ||
|
||
SDKMeterProviderBuilder.configureMetricFilter( | ||
configProps, sdkMeterProviderBuilder, registeredScopeNames, logger); | ||
|
||
MetricExporter metricsExporter = JMXExporterProvider.INSTANCE.createExporter(configProps); | ||
MetricReader metricReader = | ||
ScopeBasedPeriodicMetricReader.create(metricsExporter, registeredScopeNames) | ||
.setInterval( | ||
SDKMeterProviderBuilder.getMetricExportInterval( | ||
configProps, DEFAULT_METRIC_EXPORT_INTERVAL, logger)) | ||
.build(); | ||
sdkMeterProviderBuilder.registerMetricReader(metricReader); | ||
|
||
logger.info("AWS JMX metric collection enabled"); | ||
} | ||
return sdkMeterProviderBuilder; | ||
} | ||
|
||
private enum JMXExporterProvider { | ||
INSTANCE; | ||
|
||
public MetricExporter createExporter(ConfigProperties configProps) { | ||
String protocol = | ||
OtlpConfigUtil.getOtlpProtocol(OtlpConfigUtil.DATA_TYPE_METRICS, configProps); | ||
logger.log(Level.FINE, String.format("AWS JMX metrics export protocol: %s", protocol)); | ||
|
||
String otelJMXEndpoint; | ||
if (protocol.equals(OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF)) { | ||
otelJMXEndpoint = configProps.getString(AWS_JMX_EXPORTER_ENDPOINT_CONFIG); | ||
logger.log( | ||
Level.FINE, String.format("AWS JMX metrics export endpoint: %s", otelJMXEndpoint)); | ||
return OtlpHttpMetricExporter.builder() | ||
.setEndpoint(otelJMXEndpoint) | ||
.setDefaultAggregationSelector(this::getAggregation) | ||
.build(); | ||
} | ||
throw new ConfigurationException("Unsupported AWS JMX metrics export protocol: " + protocol); | ||
} | ||
|
||
private Aggregation getAggregation(InstrumentType instrumentType) { | ||
if (instrumentType == InstrumentType.HISTOGRAM) { | ||
return Aggregation.base2ExponentialBucketHistogram(); | ||
} | ||
return Aggregation.defaultAggregation(); | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
.../main/java/software/amazon/opentelemetry/javaagent/providers/SDKMeterProviderBuilder.java
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,64 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package software.amazon.opentelemetry.javaagent.providers; | ||
|
||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; | ||
import io.opentelemetry.sdk.metrics.Aggregation; | ||
import io.opentelemetry.sdk.metrics.InstrumentSelector; | ||
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; | ||
import io.opentelemetry.sdk.metrics.View; | ||
import java.time.Duration; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
public class SDKMeterProviderBuilder { | ||
static void configureMetricFilter( | ||
ConfigProperties configProps, | ||
SdkMeterProviderBuilder sdkMeterProviderBuilder, | ||
Set<String> registeredScopeNames, | ||
Logger logger) { | ||
Set<String> exporterNames = | ||
DefaultConfigProperties.getSet(configProps, "otel.metrics.exporter"); | ||
if (exporterNames.contains("none")) { | ||
for (String scope : registeredScopeNames) { | ||
sdkMeterProviderBuilder.registerView( | ||
InstrumentSelector.builder().setMeterName(scope).build(), | ||
View.builder().setAggregation(Aggregation.defaultAggregation()).build()); | ||
|
||
logger.log(Level.FINE, "Registered scope {0}", scope); | ||
} | ||
sdkMeterProviderBuilder.registerView( | ||
InstrumentSelector.builder().setName("*").build(), | ||
View.builder().setAggregation(Aggregation.drop()).build()); | ||
} | ||
} | ||
|
||
static Duration getMetricExportInterval( | ||
ConfigProperties configProps, Duration exportIntervalEnvVar, Logger logger) { | ||
Duration exportInterval = | ||
configProps.getDuration("otel.metric.export.interval", exportIntervalEnvVar); | ||
logger.log(Level.FINE, String.format("Metrics export interval: %s", exportInterval)); | ||
// Cap export interval to 60 seconds. This is currently required for metrics-trace correlation | ||
// to work correctly. | ||
if (exportInterval.compareTo(exportIntervalEnvVar) > 0) { | ||
exportInterval = exportIntervalEnvVar; | ||
logger.log(Level.INFO, String.format("Metrics export interval capped to %s", exportInterval)); | ||
} | ||
return exportInterval; | ||
} | ||
} |
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