Skip to content

Commit 048535a

Browse files
authored
Add Application Signals runtime metrics with feature disabled (#900)
## Feature request Add runtime metrics collection into Application Signals. ## Description of changes: This PR is (mostly) equivalent to [Add Application Signals runtime metrics #881](#881). The difference is that it disables runtime metrics for now. We split the core function from feature enablement because there are other ongoing feature developments depending on the changes made by runtime metrics. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. *Issue #, if available:* *Description of changes:* By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent c8b5e0e commit 048535a

File tree

12 files changed

+747
-41
lines changed

12 files changed

+747
-41
lines changed

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java

Lines changed: 134 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
package software.amazon.opentelemetry.javaagent.providers;
1717

18+
import io.opentelemetry.api.common.Attributes;
19+
import io.opentelemetry.api.common.AttributesBuilder;
1820
import io.opentelemetry.api.metrics.MeterProvider;
1921
import io.opentelemetry.contrib.awsxray.AlwaysRecordSampler;
2022
import io.opentelemetry.contrib.awsxray.ResourceHolder;
@@ -25,18 +27,29 @@
2527
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
2628
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2729
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
30+
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
2831
import io.opentelemetry.sdk.metrics.Aggregation;
32+
import io.opentelemetry.sdk.metrics.InstrumentSelector;
2933
import io.opentelemetry.sdk.metrics.InstrumentType;
3034
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
31-
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
35+
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
36+
import io.opentelemetry.sdk.metrics.View;
3237
import io.opentelemetry.sdk.metrics.export.MetricExporter;
3338
import io.opentelemetry.sdk.metrics.export.MetricReader;
3439
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
40+
import io.opentelemetry.sdk.resources.Resource;
3541
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
3642
import io.opentelemetry.sdk.trace.SpanProcessor;
3743
import io.opentelemetry.sdk.trace.export.SpanExporter;
3844
import io.opentelemetry.sdk.trace.samplers.Sampler;
3945
import java.time.Duration;
46+
import java.util.ArrayList;
47+
import java.util.Collections;
48+
import java.util.HashMap;
49+
import java.util.HashSet;
50+
import java.util.List;
51+
import java.util.Map;
52+
import java.util.Set;
4053
import java.util.logging.Level;
4154
import java.util.logging.Logger;
4255

@@ -61,27 +74,72 @@ public class AwsApplicationSignalsCustomizerProvider
6174
private static final Logger logger =
6275
Logger.getLogger(AwsApplicationSignalsCustomizerProvider.class.getName());
6376

64-
private static final String SMP_ENABLED_CONFIG = "otel.smp.enabled";
65-
private static final String APP_SIGNALS_ENABLED_CONFIG = "otel.aws.app.signals.enabled";
77+
private static final String DEPRECATED_SMP_ENABLED_CONFIG = "otel.smp.enabled";
78+
private static final String DEPRECATED_APP_SIGNALS_ENABLED_CONFIG =
79+
"otel.aws.app.signals.enabled";
6680
private static final String APPLICATION_SIGNALS_ENABLED_CONFIG =
6781
"otel.aws.application.signals.enabled";
68-
private static final String SMP_EXPORTER_ENDPOINT_CONFIG = "otel.aws.smp.exporter.endpoint";
69-
private static final String APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG =
82+
private static final String APPLICATION_SIGNALS_RUNTIME_ENABLED_CONFIG =
83+
"otel.aws.application.signals.runtime.enabled";
84+
private static final String DEPRECATED_SMP_EXPORTER_ENDPOINT_CONFIG =
85+
"otel.aws.smp.exporter.endpoint";
86+
private static final String DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG =
7087
"otel.aws.app.signals.exporter.endpoint";
7188
private static final String APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG =
7289
"otel.aws.application.signals.exporter.endpoint";
7390

91+
private static final String OTEL_JMX_TARGET_SYSTEM_CONFIG = "otel.jmx.target.system";
92+
7493
public void customize(AutoConfigurationCustomizer autoConfiguration) {
94+
autoConfiguration.addPropertiesCustomizer(this::customizeProperties);
95+
autoConfiguration.addResourceCustomizer(this::customizeResource);
7596
autoConfiguration.addSamplerCustomizer(this::customizeSampler);
7697
autoConfiguration.addTracerProviderCustomizer(this::customizeTracerProviderBuilder);
98+
autoConfiguration.addMeterProviderCustomizer(this::customizeMeterProvider);
7799
autoConfiguration.addSpanExporterCustomizer(this::customizeSpanExporter);
78100
}
79101

80102
private boolean isApplicationSignalsEnabled(ConfigProperties configProps) {
81103
return configProps.getBoolean(
82104
APPLICATION_SIGNALS_ENABLED_CONFIG,
83105
configProps.getBoolean(
84-
APP_SIGNALS_ENABLED_CONFIG, configProps.getBoolean(SMP_ENABLED_CONFIG, false)));
106+
DEPRECATED_APP_SIGNALS_ENABLED_CONFIG,
107+
configProps.getBoolean(DEPRECATED_SMP_ENABLED_CONFIG, false)));
108+
}
109+
110+
private boolean isApplicationSignalsRuntimeEnabled(ConfigProperties configProps) {
111+
return false;
112+
}
113+
114+
private Map<String, String> customizeProperties(ConfigProperties configProps) {
115+
if (isApplicationSignalsRuntimeEnabled(configProps)) {
116+
List<String> list = configProps.getList(OTEL_JMX_TARGET_SYSTEM_CONFIG);
117+
if (list.contains("jvm")) {
118+
logger.log(Level.INFO, "Found jmx in {0}", OTEL_JMX_TARGET_SYSTEM_CONFIG);
119+
return Collections.emptyMap();
120+
} else {
121+
logger.log(Level.INFO, "Configure jmx in {0}", OTEL_JMX_TARGET_SYSTEM_CONFIG);
122+
List<String> jmxTargets = new ArrayList<>(list);
123+
jmxTargets.add("jvm");
124+
Map<String, String> propsOverride = new HashMap<>(1);
125+
propsOverride.put(OTEL_JMX_TARGET_SYSTEM_CONFIG, String.join(",", jmxTargets));
126+
return propsOverride;
127+
}
128+
}
129+
return Collections.emptyMap();
130+
}
131+
132+
private Resource customizeResource(Resource resource, ConfigProperties configProps) {
133+
if (isApplicationSignalsEnabled(configProps)) {
134+
AttributesBuilder builder = Attributes.builder();
135+
AwsResourceAttributeConfigurator.setServiceAttribute(
136+
resource,
137+
builder,
138+
() -> logger.log(Level.WARNING, "Service name is undefined, use UnknownService instead"));
139+
Resource additionalResource = Resource.create((builder.build()));
140+
return resource.merge(additionalResource);
141+
}
142+
return resource;
85143
}
86144

87145
private Sampler customizeSampler(Sampler sampler, ConfigProperties configProps) {
@@ -95,20 +153,7 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
95153
SdkTracerProviderBuilder tracerProviderBuilder, ConfigProperties configProps) {
96154
if (isApplicationSignalsEnabled(configProps)) {
97155
logger.info("AWS Application Signals enabled");
98-
Duration exportInterval =
99-
configProps.getDuration("otel.metric.export.interval", DEFAULT_METRIC_EXPORT_INTERVAL);
100-
logger.log(
101-
Level.FINE,
102-
String.format("AWS Application Signals Metrics export interval: %s", exportInterval));
103-
// Cap export interval to 60 seconds. This is currently required for metrics-trace correlation
104-
// to work correctly.
105-
if (exportInterval.compareTo(DEFAULT_METRIC_EXPORT_INTERVAL) > 0) {
106-
exportInterval = DEFAULT_METRIC_EXPORT_INTERVAL;
107-
logger.log(
108-
Level.INFO,
109-
String.format(
110-
"AWS Application Signals metrics export interval capped to %s", exportInterval));
111-
}
156+
Duration exportInterval = getMetricExportInterval(configProps);
112157
// Construct and set local and remote attributes span processor
113158
tracerProviderBuilder.addSpanProcessor(
114159
AttributePropagatingSpanProcessorBuilder.create().build());
@@ -133,6 +178,67 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
133178
return tracerProviderBuilder;
134179
}
135180

181+
private SdkMeterProviderBuilder customizeMeterProvider(
182+
SdkMeterProviderBuilder sdkMeterProviderBuilder, ConfigProperties configProps) {
183+
184+
if (isApplicationSignalsRuntimeEnabled(configProps)) {
185+
Set<String> registeredScopeNames = new HashSet<>(1);
186+
String jmxRuntimeScopeName = "io.opentelemetry.jmx";
187+
registeredScopeNames.add(jmxRuntimeScopeName);
188+
189+
configureMetricFilter(configProps, sdkMeterProviderBuilder, registeredScopeNames);
190+
191+
MetricExporter metricsExporter =
192+
ApplicationSignalsExporterProvider.INSTANCE.createExporter(configProps);
193+
MetricReader metricReader =
194+
ScopeBasedPeriodicMetricReader.create(metricsExporter, registeredScopeNames)
195+
.setInterval(getMetricExportInterval(configProps))
196+
.build();
197+
sdkMeterProviderBuilder.registerMetricReader(metricReader);
198+
199+
logger.info("AWS Application Signals runtime metric collection enabled");
200+
}
201+
return sdkMeterProviderBuilder;
202+
}
203+
204+
private static void configureMetricFilter(
205+
ConfigProperties configProps,
206+
SdkMeterProviderBuilder sdkMeterProviderBuilder,
207+
Set<String> registeredScopeNames) {
208+
Set<String> exporterNames =
209+
DefaultConfigProperties.getSet(configProps, "otel.metrics.exporter");
210+
if (exporterNames.contains("none")) {
211+
for (String scope : registeredScopeNames) {
212+
sdkMeterProviderBuilder.registerView(
213+
InstrumentSelector.builder().setMeterName(scope).build(),
214+
View.builder().setAggregation(Aggregation.defaultAggregation()).build());
215+
216+
logger.log(Level.FINE, "Registered scope {0}", scope);
217+
}
218+
sdkMeterProviderBuilder.registerView(
219+
InstrumentSelector.builder().setName("*").build(),
220+
View.builder().setAggregation(Aggregation.drop()).build());
221+
}
222+
}
223+
224+
private static Duration getMetricExportInterval(ConfigProperties configProps) {
225+
Duration exportInterval =
226+
configProps.getDuration("otel.metric.export.interval", DEFAULT_METRIC_EXPORT_INTERVAL);
227+
logger.log(
228+
Level.FINE,
229+
String.format("AWS Application Signals Metrics export interval: %s", exportInterval));
230+
// Cap export interval to 60 seconds. This is currently required for metrics-trace correlation
231+
// to work correctly.
232+
if (exportInterval.compareTo(DEFAULT_METRIC_EXPORT_INTERVAL) > 0) {
233+
exportInterval = DEFAULT_METRIC_EXPORT_INTERVAL;
234+
logger.log(
235+
Level.INFO,
236+
String.format(
237+
"AWS Application Signals metrics export interval capped to %s", exportInterval));
238+
}
239+
return exportInterval;
240+
}
241+
136242
private SpanExporter customizeSpanExporter(
137243
SpanExporter spanExporter, ConfigProperties configProps) {
138244
if (isApplicationSignalsEnabled(configProps)) {
@@ -159,33 +265,35 @@ public MetricExporter createExporter(ConfigProperties configProps) {
159265
configProps.getString(
160266
APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
161267
configProps.getString(
162-
APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
268+
DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
163269
configProps.getString(
164-
SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4316/v1/metrics")));
270+
DEPRECATED_SMP_EXPORTER_ENDPOINT_CONFIG,
271+
"http://localhost:4316/v1/metrics")));
165272
logger.log(
166273
Level.FINE,
167274
String.format(
168275
"AWS Application Signals export endpoint: %s", applicationSignalsEndpoint));
169276
return OtlpHttpMetricExporter.builder()
170277
.setEndpoint(applicationSignalsEndpoint)
171278
.setDefaultAggregationSelector(this::getAggregation)
172-
.setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred())
279+
.setAggregationTemporalitySelector(CloudWatchTemporalitySelector.alwaysDelta())
173280
.build();
174281
} else if (protocol.equals(OtlpConfigUtil.PROTOCOL_GRPC)) {
175282
applicationSignalsEndpoint =
176283
configProps.getString(
177284
APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
178285
configProps.getString(
179-
APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
180-
configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4315")));
286+
DEPRECATED_APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG,
287+
configProps.getString(
288+
DEPRECATED_SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4315")));
181289
logger.log(
182290
Level.FINE,
183291
String.format(
184292
"AWS Application Signals export endpoint: %s", applicationSignalsEndpoint));
185293
return OtlpGrpcMetricExporter.builder()
186294
.setEndpoint(applicationSignalsEndpoint)
187295
.setDefaultAggregationSelector(this::getAggregation)
188-
.setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred())
296+
.setAggregationTemporalitySelector(CloudWatchTemporalitySelector.alwaysDelta())
189297
.build();
190298
}
191299
throw new ConfigurationException(

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
package software.amazon.opentelemetry.javaagent.providers;
1717

18-
import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_NAME;
1918
import static io.opentelemetry.semconv.SemanticAttributes.DB_CONNECTION_STRING;
2019
import static io.opentelemetry.semconv.SemanticAttributes.DB_NAME;
2120
import static io.opentelemetry.semconv.SemanticAttributes.DB_OPERATION;
@@ -64,7 +63,6 @@
6463
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_OPERATION;
6564
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
6665
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
67-
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_SERVICE;
6866
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isAwsSDKSpan;
6967
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isDBSpan;
7068
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isKeyPresent;
@@ -119,11 +117,6 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
119117

120118
private static final String DB_CONNECTION_RESOURCE_TYPE = "DB::Connection";
121119

122-
// As per
123-
// https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#opentelemetry-resource
124-
// If service name is not specified, SDK defaults the service name to unknown_service:java
125-
private static final String OTEL_UNKNOWN_SERVICE = "unknown_service:java";
126-
127120
// This method is used by the AwsSpanMetricsProcessor to generate service and dependency metrics
128121
@Override
129122
public Map<String, Attributes> generateMetricAttributeMapFromSpan(
@@ -167,14 +160,8 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re
167160

168161
/** Service is always derived from {@link ResourceAttributes#SERVICE_NAME} */
169162
private static void setService(Resource resource, SpanData span, AttributesBuilder builder) {
170-
String service = resource.getAttribute(SERVICE_NAME);
171-
172-
// In practice the service name is never null, but we can be defensive here.
173-
if (service == null || service.equals(OTEL_UNKNOWN_SERVICE)) {
174-
logUnknownAttribute(AWS_LOCAL_SERVICE, span);
175-
service = UNKNOWN_SERVICE;
176-
}
177-
builder.put(AWS_LOCAL_SERVICE, service);
163+
AwsResourceAttributeConfigurator.setServiceAttribute(
164+
resource, builder, () -> logUnknownAttribute(AWS_LOCAL_SERVICE, span));
178165
}
179166

180167
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 static io.opentelemetry.semconv.ResourceAttributes.SERVICE_NAME;
19+
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE;
20+
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_SERVICE;
21+
22+
import io.opentelemetry.api.common.AttributesBuilder;
23+
import io.opentelemetry.sdk.resources.Resource;
24+
25+
public class AwsResourceAttributeConfigurator {
26+
// As per
27+
// https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#opentelemetry-resource
28+
// If service name is not specified, SDK defaults the service name to unknown_service:java
29+
private static final String OTEL_UNKNOWN_SERVICE = "unknown_service:java";
30+
31+
public static void setServiceAttribute(
32+
Resource resource, AttributesBuilder attributesBuilder, Runnable handleUnknownService) {
33+
String service = resource.getAttribute(AWS_LOCAL_SERVICE);
34+
if (service == null) {
35+
service = resource.getAttribute(SERVICE_NAME);
36+
// In practice the service name is never null, but we can be defensive here.
37+
if (service == null || service.equals(OTEL_UNKNOWN_SERVICE)) {
38+
service = UNKNOWN_SERVICE;
39+
handleUnknownService.run();
40+
}
41+
}
42+
attributesBuilder.put(AWS_LOCAL_SERVICE, service);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.sdk.metrics.data.AggregationTemporality;
19+
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
20+
21+
public class CloudWatchTemporalitySelector {
22+
static AggregationTemporalitySelector alwaysDelta() {
23+
return (instrumentType) -> {
24+
return AggregationTemporality.DELTA;
25+
};
26+
}
27+
}

0 commit comments

Comments
 (0)