Skip to content

Commit 5b34e71

Browse files
committed
Add Application Signals runtime metrics with feature disabled
1 parent 4b13e06 commit 5b34e71

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
/**
Lines changed: 44 additions & 0 deletions
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+
}
Lines changed: 27 additions & 0 deletions
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)