Skip to content

Commit f417417

Browse files
authored
Rename enduser attributes for v3 preview (#18795)
1 parent be68ff0 commit f417417

37 files changed

Lines changed: 1093 additions & 467 deletions

File tree

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public final class CommonConfig {
2525
private final List<String> serverRequestHeaders;
2626
private final List<String> serverResponseHeaders;
2727
private final Set<String> knownHttpRequestMethods;
28-
private final EnduserConfig enduserConfig;
28+
private final UserConfig userConfig;
2929
private final boolean emitExperimentalHttpClientTelemetry;
3030
private final boolean emitExperimentalHttpServerTelemetry;
3131
private final Set<String> sensitiveQueryParameters;
@@ -87,14 +87,14 @@ public CommonConfig(OpenTelemetry openTelemetry) {
8787
.get("http")
8888
.get("server")
8989
.getBoolean("emit_experimental_telemetry/development", false);
90-
enduserConfig = new EnduserConfig(commonConfig);
90+
v3Preview = commonConfig.getBoolean("v3_preview", false);
91+
userConfig = new UserConfig(commonConfig, v3Preview);
9192
loggingTraceIdKey =
9293
commonConfig.get("logging").getString("trace_id", LoggingContextConstants.TRACE_ID);
9394
loggingSpanIdKey =
9495
commonConfig.get("logging").getString("span_id", LoggingContextConstants.SPAN_ID);
9596
loggingTraceFlagsKey =
9697
commonConfig.get("logging").getString("trace_flags", LoggingContextConstants.TRACE_FLAGS);
97-
v3Preview = commonConfig.getBoolean("v3_preview", false);
9898
}
9999

100100
public List<String> getClientRequestHeaders() {
@@ -117,8 +117,8 @@ public Set<String> getKnownHttpRequestMethods() {
117117
return knownHttpRequestMethods;
118118
}
119119

120-
public EnduserConfig getEnduserConfig() {
121-
return enduserConfig;
120+
public UserConfig getUserConfig() {
121+
return userConfig;
122122
}
123123

124124
public boolean shouldEmitExperimentalHttpClientTelemetry() {

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.config.internal;
7+
8+
import static java.util.Objects.requireNonNull;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
12+
/**
13+
* Configuration that controls capturing the {@code enduser.*} or {@code user.*} semantic
14+
* attributes.
15+
*
16+
* <p>When the v3 preview is enabled, this configuration reads the corresponding {@code user.*}
17+
* properties instead. Legacy {@code enduser.*} properties and their associated attributes are not
18+
* supported when v3 preview is enabled.
19+
*
20+
* <p>The {@code enduser.*} semantic attributes are not captured by default, due to this text in the
21+
* specification:
22+
*
23+
* <blockquote>
24+
*
25+
* <p>Given the sensitive nature of this information, SDKs and exporters SHOULD drop these
26+
* attributes by default and then provide a configuration parameter to turn on retention for use
27+
* cases where the information is required and would not violate any policies or regulations.
28+
*
29+
* </blockquote>
30+
*
31+
* <p>Capturing of the {@code enduser.*} semantic attributes can be individually enabled by
32+
* configuring the following properties:
33+
*
34+
* <pre>
35+
* otel.instrumentation.common.enduser.id.enabled=true
36+
* otel.instrumentation.common.enduser.role.enabled=true
37+
* otel.instrumentation.common.enduser.scope.enabled=true
38+
* </pre>
39+
*
40+
* <p>When v3 preview is enabled, capturing of the {@code user.*} semantic attributes can be
41+
* individually enabled by configuring the following properties:
42+
*
43+
* <pre>
44+
* otel.instrumentation.common.user.name.enabled=true
45+
* otel.instrumentation.common.user.roles.enabled=true
46+
* </pre>
47+
*
48+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
49+
* at any time.
50+
*/
51+
public class UserConfig {
52+
53+
private final boolean nameEnabled;
54+
private final boolean rolesEnabled;
55+
private final boolean scopeEnabled;
56+
57+
UserConfig(DeclarativeConfigProperties commonConfig, boolean v3Preview) {
58+
requireNonNull(commonConfig, "commonConfig must not be null");
59+
60+
/*
61+
* Capturing enduser.* attributes is disabled by default, because of this requirement in the specification:
62+
*
63+
* Given the sensitive nature of this information, SDKs and exporters SHOULD drop these attributes by default and then provide a configuration parameter to turn on retention for use cases where the information is required and would not violate any policies or regulations.
64+
*
65+
* https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-identity-attributes
66+
*/
67+
if (v3Preview) {
68+
this.nameEnabled = commonConfig.get("user").get("name").getBoolean("enabled", false);
69+
this.rolesEnabled = commonConfig.get("user").get("roles").getBoolean("enabled", false);
70+
this.scopeEnabled = false;
71+
} else {
72+
this.nameEnabled = commonConfig.get("enduser").get("id").getBoolean("enabled", false);
73+
this.rolesEnabled = commonConfig.get("enduser").get("role").getBoolean("enabled", false);
74+
this.scopeEnabled = commonConfig.get("enduser").get("scope").getBoolean("enabled", false);
75+
}
76+
}
77+
78+
/**
79+
* Returns true if capturing of any identity semantic attribute is enabled.
80+
*
81+
* <p>This flag can be used by capturing instrumentations to bypass all identity attribute
82+
* capturing. In v3 preview mode, this corresponds to the {@code user.*} attributes; otherwise it
83+
* corresponds to the {@code enduser.*} attributes.
84+
*/
85+
public boolean isAnyEnabled() {
86+
return this.nameEnabled || this.rolesEnabled || this.scopeEnabled;
87+
}
88+
89+
/**
90+
* Returns true if capturing the id semantic attribute is enabled.
91+
*
92+
* <p>In v3 preview mode, this controls the {@code user.name} attribute; otherwise it controls the
93+
* {@code enduser.id} attribute.
94+
*/
95+
public boolean isNameEnabled() {
96+
return this.nameEnabled;
97+
}
98+
99+
/**
100+
* Returns true if capturing the role(s) semantic attribute is enabled.
101+
*
102+
* <p>In v3 preview mode, this controls the {@code user.roles} attribute; otherwise it controls
103+
* the {@code enduser.role} attribute.
104+
*/
105+
public boolean isRolesEnabled() {
106+
return this.rolesEnabled;
107+
}
108+
109+
/**
110+
* Returns true if capturing the {@code enduser.scope} semantic attribute is enabled.
111+
*
112+
* <p>This is always disabled in v3 preview mode, since {@code enduser.scope} has no {@code
113+
* user.*} equivalent.
114+
*/
115+
public boolean isScopeEnabled() {
116+
return this.scopeEnabled;
117+
}
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.config.internal;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.when;
12+
13+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
14+
import org.junit.jupiter.api.Test;
15+
16+
class UserConfigTest {
17+
18+
@Test
19+
void readsEnduserConfigWhenV3PreviewIsDisabled() {
20+
DeclarativeConfigProperties commonConfig =
21+
mock(DeclarativeConfigProperties.class, RETURNS_DEEP_STUBS);
22+
when(commonConfig.get("enduser").get("id").getBoolean("enabled", false)).thenReturn(false);
23+
when(commonConfig.get("enduser").get("role").getBoolean("enabled", false)).thenReturn(true);
24+
when(commonConfig.get("enduser").get("scope").getBoolean("enabled", false)).thenReturn(true);
25+
when(commonConfig.get("user").get("name").getBoolean("enabled", false)).thenReturn(true);
26+
when(commonConfig.get("user").get("roles").getBoolean("enabled", false)).thenReturn(false);
27+
28+
UserConfig userConfig = new UserConfig(commonConfig, false);
29+
30+
assertThat(userConfig.isNameEnabled()).isFalse();
31+
assertThat(userConfig.isRolesEnabled()).isTrue();
32+
assertThat(userConfig.isScopeEnabled()).isTrue();
33+
}
34+
35+
@Test
36+
void readsUserConfigWhenV3PreviewIsEnabled() {
37+
DeclarativeConfigProperties commonConfig =
38+
mock(DeclarativeConfigProperties.class, RETURNS_DEEP_STUBS);
39+
when(commonConfig.get("enduser").get("id").getBoolean("enabled", false)).thenReturn(true);
40+
when(commonConfig.get("enduser").get("role").getBoolean("enabled", false)).thenReturn(true);
41+
when(commonConfig.get("enduser").get("scope").getBoolean("enabled", false)).thenReturn(true);
42+
when(commonConfig.get("user").get("name").getBoolean("enabled", false)).thenReturn(false);
43+
when(commonConfig.get("user").get("roles").getBoolean("enabled", false)).thenReturn(true);
44+
45+
UserConfig userConfig = new UserConfig(commonConfig, true);
46+
47+
assertThat(userConfig.isNameEnabled()).isFalse();
48+
assertThat(userConfig.isRolesEnabled()).isTrue();
49+
assertThat(userConfig.isScopeEnabled()).isFalse();
50+
}
51+
}

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private static boolean resolveV3Preview(OpenTelemetry openTelemetry) {
7070
if (value != null) {
7171
return value;
7272
}
73-
return ConfigPropertiesUtil.getBoolean("otel.semconv-stability.v3-preview", false);
73+
return ConfigPropertiesUtil.getBoolean("otel.instrumentation.common.v3-preview", false);
7474
}
7575

7676
@SuppressWarnings("deprecation") // using deprecated config property fallback

instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/AgentServletInstrumenterBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public Instrumenter<ServletRequestContext<REQUEST>, ServletResponseContext<RESPO
6868
instrumentationName, GlobalOpenTelemetry.get(), httpAttributesGetter, accessor)
6969
.setCaptureRequestParameters(CAPTURE_REQUEST_PARAMETERS)
7070
.setCaptureExperimentalAttributes(CAPTURE_EXPERIMENTAL_ATTRIBUTES)
71-
.setCaptureEnduserId(AgentCommonConfig.get().getEnduserConfig().isIdEnabled());
71+
.setCaptureEnduserId(AgentCommonConfig.get().getUserConfig().isNameEnabled());
7272
for (ContextCustomizer<? super ServletRequestContext<REQUEST>> contextCustomizer :
7373
contextCustomizers) {
7474
builder.addContextCustomizer(contextCustomizer);

instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/BaseServletHelper.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
package io.opentelemetry.javaagent.instrumentation.servlet.common;
77

8+
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.v3Preview;
89
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.SERVER;
910
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.SERVER_FILTER;
1011
import static io.opentelemetry.semconv.incubating.EnduserIncubatingAttributes.ENDUSER_ID;
12+
import static io.opentelemetry.semconv.incubating.UserIncubatingAttributes.USER_NAME;
1113
import static java.util.Collections.emptyList;
1214

1315
import io.opentelemetry.api.GlobalOpenTelemetry;
@@ -29,6 +31,7 @@
2931
import io.opentelemetry.javaagent.bootstrap.servlet.ServletAsyncContext;
3032
import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath;
3133
import io.opentelemetry.semconv.incubating.EnduserIncubatingAttributes;
34+
import io.opentelemetry.semconv.incubating.UserIncubatingAttributes;
3235
import java.security.Principal;
3336
import java.util.List;
3437
import java.util.function.Function;
@@ -166,21 +169,22 @@ private void captureRequestParameters(Span serverSpan, REQUEST request) {
166169
}
167170

168171
/**
169-
* Capture {@link EnduserIncubatingAttributes#ENDUSER_ID} as span attributes when SERVER span is
170-
* not created by servlet instrumentation.
172+
* Capture {@link EnduserIncubatingAttributes#ENDUSER_ID}, or {@link
173+
* UserIncubatingAttributes#USER_NAME} when v3 preview is enabled, as a span attribute when SERVER
174+
* span is not created by servlet instrumentation.
171175
*
172176
* <p>When SERVER span is created by servlet instrumentation we register {@link
173177
* ServletAdditionalAttributesExtractor} as an attribute extractor. When SERVER span is not
174178
* created by servlet instrumentation we call this method on exit from the last servlet or filter.
175179
*/
176180
private void captureEnduserId(Span serverSpan, REQUEST request) {
177-
if (!AgentCommonConfig.get().getEnduserConfig().isIdEnabled()) {
181+
if (!AgentCommonConfig.get().getUserConfig().isNameEnabled()) {
178182
return;
179183
}
180184

181185
Principal principal = accessor.getRequestUserPrincipal(request);
182186
if (principal != null) {
183-
serverSpan.setAttribute(ENDUSER_ID, principal.getName());
187+
serverSpan.setAttribute(v3Preview() ? USER_NAME : ENDUSER_ID, principal.getName());
184188
}
185189
}
186190

instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/common/internal/ServletAdditionalAttributesExtractor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.opentelemetry.api.common.AttributesBuilder;
1212
import io.opentelemetry.context.Context;
1313
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
14+
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
1415
import java.security.Principal;
1516
import javax.annotation.Nullable;
1617

@@ -24,6 +25,8 @@ public class ServletAdditionalAttributesExtractor<REQUEST, RESPONSE>
2425

2526
// copied from EnduserIncubatingAttributes
2627
private static final AttributeKey<String> ENDUSER_ID = AttributeKey.stringKey("enduser.id");
28+
// copied from UserIncubatingAttributes
29+
private static final AttributeKey<String> USER_NAME = AttributeKey.stringKey("user.name");
2730
private static final AttributeKey<Long> SERVLET_TIMEOUT = longKey("servlet.timeout");
2831

2932
private final ServletAccessor<REQUEST, RESPONSE> accessor;
@@ -55,7 +58,7 @@ public void onEnd(
5558
if (captureEnduserId) {
5659
Principal principal = accessor.getRequestUserPrincipal(requestContext.request());
5760
if (principal != null) {
58-
attributes.put(ENDUSER_ID, principal.getName());
61+
attributes.put(SemconvStability.v3Preview() ? USER_NAME : ENDUSER_ID, principal.getName());
5962
}
6063
}
6164
if (!captureExperimentalAttributes) {

0 commit comments

Comments
 (0)