Skip to content

Commit

Permalink
Support stable HTTP semconv
Browse files Browse the repository at this point in the history
  • Loading branch information
trask committed Jan 6, 2024
1 parent e3858f8 commit 4979fdc
Show file tree
Hide file tree
Showing 20 changed files with 79 additions and 161 deletions.
1 change: 0 additions & 1 deletion agent/agent-bootstrap/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ dependencies {
// needed to access io.opentelemetry.instrumentation.api.aisdk.MicrometerUtil
// TODO (heya) remove this when updating to upstream micrometer instrumentation
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")
compileOnly("io.opentelemetry:opentelemetry-semconv")
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv")
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public Map<String, String> apply(ConfigProperties otelConfig) {
Configuration configuration = FirstEntryPoint.getConfiguration();

Map<String, String> properties = new HashMap<>();
properties.put("otel.semconv-stability.opt-in", "http,jvm");

properties.put(
"applicationinsights.internal.micrometer.step.millis",
Long.toString(SECONDS.toMillis(configuration.metricIntervalSeconds)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import com.azure.monitor.opentelemetry.exporter.implementation.AiSemanticAttributes;
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
Expand Down Expand Up @@ -39,9 +40,11 @@ public Context onStart(Context context, R request, Attributes startAttributes) {

Context newContext = context;

String target = startAttributes.get(SemanticAttributes.HTTP_TARGET);
String path =
getStableAttribute(
startAttributes, SemanticAttributes.URL_PATH, SemanticAttributes.HTTP_TARGET);

String connectionStringOverride = getConnectionStringOverride(target);
String connectionStringOverride = getConnectionStringOverride(path);
if (connectionStringOverride != null) {
newContext = newContext.with(AiContextKeys.CONNECTION_STRING, connectionStringOverride);
// InheritedConnectionStringSpanProcessor will stamp connection string attribute from the
Expand All @@ -51,7 +54,7 @@ public Context onStart(Context context, R request, Attributes startAttributes) {
span.setAttribute(AiSemanticAttributes.INTERNAL_CONNECTION_STRING, connectionStringOverride);
}

String roleNameOverride = getRoleNameOverride(target);
String roleNameOverride = getRoleNameOverride(path);
if (roleNameOverride != null) {
newContext = newContext.with(AiContextKeys.ROLE_NAME, roleNameOverride);
// InheritedRoleNameSpanProcessor will stamp role name attribute from the
Expand Down Expand Up @@ -89,4 +92,13 @@ private String getRoleNameOverride(String target) {
}
return null;
}

private static <T> T getStableAttribute(
Attributes attributes, AttributeKey<T> stable, AttributeKey<T> old) {
T value = attributes.get(stable);
if (value != null) {
return value;
}
return attributes.get(old);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,13 @@
import com.microsoft.applicationinsights.agent.internal.processors.ExporterWithLogProcessor;
import com.microsoft.applicationinsights.agent.internal.processors.ExporterWithSpanProcessor;
import com.microsoft.applicationinsights.agent.internal.processors.LogExporterWithAttributeProcessor;
import com.microsoft.applicationinsights.agent.internal.processors.MySpanData;
import com.microsoft.applicationinsights.agent.internal.processors.SpanExporterWithAttributeProcessor;
import com.microsoft.applicationinsights.agent.internal.profiler.triggers.AlertTriggerSpanProcessor;
import com.microsoft.applicationinsights.agent.internal.sampling.SamplingOverrides;
import com.microsoft.applicationinsights.agent.internal.telemetry.BatchItemProcessor;
import com.microsoft.applicationinsights.agent.internal.telemetry.MetricFilter;
import com.microsoft.applicationinsights.agent.internal.telemetry.TelemetryClient;
import com.microsoft.applicationinsights.agent.internal.telemetry.TelemetryObservers;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
Expand All @@ -63,14 +60,11 @@
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder;
import io.opentelemetry.sdk.metrics.internal.view.AiViewRegistry;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.semconv.SemanticAttributes;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -595,10 +589,6 @@ private static SpanExporter wrapSpanExporter(
"Not an expected ProcessorType: " + processorConfig.type);
}
}

// this is temporary until semantic attributes stabilize and we make breaking change
// then can use java.util.functions.Predicate<Attributes>
spanExporter = new BackCompatHttpUrlProcessor(spanExporter);
}

return spanExporter;
Expand Down Expand Up @@ -758,47 +748,4 @@ private static SdkMeterProviderBuilder configureMetrics(
}
return builder.registerMetricReader(metricReader);
}

private static class BackCompatHttpUrlProcessor implements SpanExporter {

private final SpanExporter delegate;

private BackCompatHttpUrlProcessor(SpanExporter delegate) {
this.delegate = delegate;
}

@Override
public CompletableResultCode export(Collection<SpanData> spans) {
List<SpanData> copy = new ArrayList<>();
for (SpanData span : spans) {
copy.add(addBackCompatHttpUrl(span));
}
return delegate.export(copy);
}

private static SpanData addBackCompatHttpUrl(SpanData span) {
Attributes attributes = span.getAttributes();
if (attributes.get(SemanticAttributes.HTTP_URL) != null) {
// already has http.url
return span;
}
String httpUrl = SpanDataMapper.getHttpUrlFromServerSpan(attributes);
if (httpUrl == null) {
return span;
}
AttributesBuilder builder = attributes.toBuilder();
builder.put(SemanticAttributes.HTTP_URL, httpUrl);
return new MySpanData(span, builder.build());
}

@Override
public CompletableResultCode flush() {
return delegate.flush();
}

@Override
public CompletableResultCode shutdown() {
return delegate.shutdown();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

package com.microsoft.applicationinsights.agent.internal.sampling;

import com.azure.monitor.opentelemetry.exporter.implementation.SpanDataMapper;
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration.MatchType;
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration.SamplingOverride;
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration.SamplingOverrideAttribute;
Expand All @@ -13,6 +12,7 @@
import io.opentelemetry.semconv.SemanticAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.slf4j.Logger;
Expand All @@ -33,9 +33,8 @@ public SamplingOverrides(List<SamplingOverride> overrides) {

@Nullable
public Sampler getOverride(Attributes attributes) {
LazyHttpUrl lazyHttpUrl = new LazyHttpUrl(attributes);
for (MatcherGroup matcherGroups : matcherGroups) {
if (matcherGroups.matches(attributes, lazyHttpUrl)) {
if (matcherGroups.matches(attributes)) {
return matcherGroups.getSampler();
}
}
Expand All @@ -46,15 +45,15 @@ public Sampler getOverride(Attributes attributes) {
@Nullable
public Double getOverridePercentage(Attributes attributes) {
for (MatcherGroup matcherGroups : matcherGroups) {
if (matcherGroups.matches(attributes, null)) {
if (matcherGroups.matches(attributes)) {
return matcherGroups.getPercentage();
}
}
return null;
}

private static class MatcherGroup {
private final List<TempPredicate> predicates;
private final List<Predicate<Attributes>> predicates;
private final Sampler sampler;
// for now only support fixed percentage, but could extend sampling overrides to support
// rate-limited sampling
Expand All @@ -63,7 +62,7 @@ private static class MatcherGroup {
private MatcherGroup(SamplingOverride override) {
predicates = new ArrayList<>();
for (SamplingOverrideAttribute attribute : override.attributes) {
TempPredicate predicate = toPredicate(attribute);
Predicate<Attributes> predicate = toPredicate(attribute);
if (predicate != null) {
predicates.add(predicate);
}
Expand All @@ -80,9 +79,9 @@ Sampler getSampler() {
return samplingPercentage.get();
}

private boolean matches(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl) {
for (TempPredicate predicate : predicates) {
if (!predicate.test(attributes, lazyHttpUrl)) {
private boolean matches(Attributes attributes) {
for (Predicate<Attributes> predicate : predicates) {
if (!predicate.test(attributes)) {
return false;
}
}
Expand All @@ -99,7 +98,7 @@ static String getValueIncludingThreadName(
}

@Nullable
private static TempPredicate toPredicate(SamplingOverrideAttribute attribute) {
private static Predicate<Attributes> toPredicate(SamplingOverrideAttribute attribute) {
if (attribute.matchType == MatchType.STRICT) {
if (isHttpHeaderAttribute(attribute)) {
return new StrictArrayContainsMatcher(attribute.key, attribute.value);
Expand All @@ -126,7 +125,7 @@ private static boolean isHttpHeaderAttribute(SamplingOverrideAttribute attribute
}
}

private static class StrictMatcher implements TempPredicate {
private static class StrictMatcher implements Predicate<Attributes> {
private final AttributeKey<String> key;
private final String value;

Expand All @@ -136,16 +135,13 @@ private StrictMatcher(String key, String value) {
}

@Override
public boolean test(Attributes attributes, LazyHttpUrl lazyHttpUrl) {
public boolean test(Attributes attributes) {
String val = MatcherGroup.getValueIncludingThreadName(attributes, key);
if (val == null && key.getKey().equals(SemanticAttributes.HTTP_URL.getKey())) {
val = lazyHttpUrl.get();
}
return value.equals(val);
}
}

private static class StrictArrayContainsMatcher implements TempPredicate {
private static class StrictArrayContainsMatcher implements Predicate<Attributes> {
private final AttributeKey<List<String>> key;
private final String value;

Expand All @@ -155,13 +151,13 @@ private StrictArrayContainsMatcher(String key, String value) {
}

@Override
public boolean test(Attributes attributes, LazyHttpUrl lazyHttpUrl) {
public boolean test(Attributes attributes) {
List<String> val = attributes.get(key);
return val != null && val.contains(value);
}
}

private static class RegexpMatcher implements TempPredicate {
private static class RegexpMatcher implements Predicate<Attributes> {
private final AttributeKey<String> key;
private final Pattern value;

Expand All @@ -171,18 +167,13 @@ private RegexpMatcher(String key, String value) {
}

@Override
public boolean test(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl) {
public boolean test(Attributes attributes) {
String val = MatcherGroup.getValueIncludingThreadName(attributes, key);
if (val == null
&& key.getKey().equals(SemanticAttributes.HTTP_URL.getKey())
&& lazyHttpUrl != null) {
val = lazyHttpUrl.get();
}
return val != null && value.matcher(val).matches();
}
}

private static class RegexpArrayContainsMatcher implements TempPredicate {
private static class RegexpArrayContainsMatcher implements Predicate<Attributes> {
private final AttributeKey<List<String>> key;
private final Pattern value;

Expand All @@ -192,7 +183,7 @@ private RegexpArrayContainsMatcher(String key, String value) {
}

@Override
public boolean test(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl) {
public boolean test(Attributes attributes) {
List<String> val = attributes.get(key);
if (val == null) {
return false;
Expand All @@ -206,47 +197,17 @@ public boolean test(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl) {
}
}

private static class KeyOnlyMatcher implements TempPredicate {
private static class KeyOnlyMatcher implements Predicate<Attributes> {
private final AttributeKey<String> key;

private KeyOnlyMatcher(String key) {
this.key = AttributeKey.stringKey(key);
}

@Override
public boolean test(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl) {
public boolean test(Attributes attributes) {
String val = MatcherGroup.getValueIncludingThreadName(attributes, key);
if (val == null
&& key.getKey().equals(SemanticAttributes.HTTP_URL.getKey())
&& lazyHttpUrl != null) {
val = lazyHttpUrl.get();
}
return val != null;
}
}

// this is temporary until semantic attributes stabilize and we make breaking change
private static class LazyHttpUrl {
private final Attributes attributes;
private boolean initialized;
@Nullable private String value;

private LazyHttpUrl(Attributes attributes) {
this.attributes = attributes;
}

private String get() {
if (!initialized) {
value = SpanDataMapper.getHttpUrlFromServerSpan(attributes);
initialized = true;
}
return value;
}
}

// this is temporary until semantic attributes stabilize and we make breaking change
// then can use java.util.functions.Predicate<Attributes>
private interface TempPredicate {
boolean test(Attributes attributes, @Nullable LazyHttpUrl lazyHttpUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import java.util.Set;

enum MetricView {
HTTP_CLIENT_VIEW("http.client.duration", httpClientDurationAttributeKeys(), false),
HTTP_SERVER_VIEW("http.server.duration", httpServerDurationAttributeKeys(), true),
HTTP_CLIENT_VIEW("http.client.request.duration", httpClientDurationAttributeKeys(), false),
HTTP_SERVER_VIEW("http.server.request.duration", httpServerDurationAttributeKeys(), true),
RPC_CLIENT_VIEW("rpc.client.duration", rpcClientDurationAttributeKeys(), false),
RPC_SERVER_VIEW("rpc.server.duration", rpcServerDurationAttributeKeys(), false);

Expand Down Expand Up @@ -41,23 +41,23 @@ boolean isCaptureSynthetic() {

private static Set<AttributeKey<?>> httpClientDurationAttributeKeys() {
Set<AttributeKey<?>> view = new HashSet<>(3);
view.add(SemanticAttributes.HTTP_STATUS_CODE);
view.add(SemanticAttributes.NET_PEER_NAME);
view.add(SemanticAttributes.NET_PEER_PORT);
view.add(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE);
view.add(SemanticAttributes.SERVER_ADDRESS);
view.add(SemanticAttributes.SERVER_PORT);
return view;
}

private static Set<AttributeKey<?>> httpServerDurationAttributeKeys() {
Set<AttributeKey<?>> view = new HashSet<>(1);
view.add(SemanticAttributes.HTTP_STATUS_CODE);
view.add(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE);
return view;
}

private static Set<AttributeKey<?>> rpcClientDurationAttributeKeys() {
Set<AttributeKey<?>> view = new HashSet<>(3);
view.add(SemanticAttributes.RPC_SYSTEM);
view.add(SemanticAttributes.NET_PEER_NAME);
view.add(SemanticAttributes.NET_PEER_PORT);
view.add(SemanticAttributes.SERVER_ADDRESS);
view.add(SemanticAttributes.SERVER_PORT);
return view;
}

Expand Down
Loading

0 comments on commit 4979fdc

Please sign in to comment.