Skip to content

Add RPC semantic convention stable opt-in support#15932

Draft
zeitlinger wants to merge 17 commits intoopen-telemetry:mainfrom
zeitlinger:rpc-api-incubator
Draft

Add RPC semantic convention stable opt-in support#15932
zeitlinger wants to merge 17 commits intoopen-telemetry:mainfrom
zeitlinger:rpc-api-incubator

Conversation

@zeitlinger
Copy link
Member

@zeitlinger zeitlinger commented Jan 19, 2026

First part of #15879

This adds support for the stable RPC semantic conventions via the otel.semconv-stability.opt-in=rpc configuration option.

Key changes:

  • Add emitOldRpcSemconv()/emitStableRpcSemconv() to SemconvStability
  • Add stableRpcSystemName() mapping (apache_dubbo→dubbo, connect_rpc→connectrpc)
  • Add RpcAttributesGetter.getFullMethod() for "service/method" format
  • Update RpcCommonAttributesExtractor for dual semconv emission
  • Update RpcClientMetrics/RpcServerMetrics for dual histogram (ms→s unit change)

@zeitlinger zeitlinger self-assigned this Jan 19, 2026
@zeitlinger zeitlinger requested a review from a team as a code owner January 19, 2026 16:46
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Jan 19, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@zeitlinger
Copy link
Member Author

@trask this is now the first PR for rpc

Comment on lines +49 to +56
default String getFullMethod(REQUEST request) {
String service = getService(request);
String method = getMethod(request);
if (service == null || method == null) {
return null;
}
return service + "/" + method;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to getRpcMethod() matching convention of other getters

can we just return null from default method? (requiring choice in each RPC implementation how to structure method)

also, how to handle:

When the method is not recognized, for example, when the server receives a request for a method that is not predefined on the server, or when instrumentation is not able to reliably detect if the method is predefined, the attribute MUST be set to _OTHER. In such cases, tracing instrumentations MUST also set rpc.method_original attribute to the original method value.

@zeitlinger zeitlinger force-pushed the rpc-api-incubator branch 2 times, most recently from 2ede454 to 58a163f Compare January 20, 2026 13:30
Comment on lines +57 to +58
} else {
oldClientDurationHistogram = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default fields are initialized to null, is there anything to gain from explicitly assigning the null here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this allows the field to be final

internalSet(
attributes,
RPC_SYSTEM_NAME,
system == null ? null : SemconvStability.stableRpcSystemName(system));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it would be better to handle null in SemconvStability.stableRpcSystemName

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the param would need to be nullable - which I'd like to avoid

keys.add(RpcCommonAttributesExtractor.RPC_METHOD);

// Add status code key
if (SemconvStability.emitStableRpcSemconv()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this use stable instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method is in both stable and old


// virtual key to avoid clash with stable rpc.method
private static final AttributeKey<String> RPC_METHOD_OLD =
AttributeKey.stringKey("rpc.method.deprecated");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes me wonder whether we should bother with this at all since it is such a corner case, might as well let the attributes clash

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean not supporting rpc/dup?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under rpc/dup, stable semconv takes priority when there's a conflict like this (no need for adding this extra unspec'd attribute)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is never emitted - it's only kept in memory and gets renamed before being added as rpc.method

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is how this virtual attribute gets used:

public static Attributes getOldRpcMetricAttributes(Attributes attributes) {
if (emitStableRpcSemconv()) {
// need to copy attributes
return attributes.toBuilder().put(RPC_METHOD, attributes.get(RPC_METHOD_OLD)).build();
}
return attributes;
}

This adds support for the stable RPC semantic conventions via the
`otel.semconv-stability.opt-in=rpc` configuration option.

Key changes:
- Add emitOldRpcSemconv()/emitStableRpcSemconv() to SemconvStability
- Add stableRpcSystemName() mapping (apache_dubbo→dubbo, connect_rpc→connectrpc)
- Add RpcAttributesGetter.getFullMethod() for "service/method" format
- Update RpcCommonAttributesExtractor for dual semconv emission
- Update RpcClientMetrics/RpcServerMetrics for dual histogram (ms→s unit change)
@trask
Copy link
Member

trask commented Feb 5, 2026

I think this could be broken up into smaller PRs which would make reviews go faster, here's an AI generated suggestion that seems reasonable, though I haven't looked at it too closely:

Plan: Break PR #15932 into Smaller PRs

PR: Add RPC semantic convention stable opt-in support #15932

TL;DR: PR #15932 adds RPC stable semconv opt-in support across 25 files with +1,076/-350 changes. It can be decomposed into 5 incremental PRs that each deliver value independently while preserving backward compatibility. The key insight is that the RpcAttributesGetter interface change can be made non-breaking initially by adding a second type parameter with defaults, allowing the extractors and metrics to be updated separately from the instrumentation implementations.


PR 1: Add RPC SemconvStability infrastructure (foundation)

Files:

  • instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java
  • instrumentation-api-incubator/build.gradle.kts (test tasks only)

Changes:

  • Add emitOldRpcSemconv() / emitStableRpcSemconv() to SemconvStability
  • Add stableRpcSystemName() mapping function (apache_dubbo→dubbo, connect_rpc→connectrpc)
  • Add getOldRpcMethodAttributeKey() and getOldRpcMetricAttributes() helper methods
  • Add rpc and rpc/dup to test task JVM args

Impact: No behavioral change - just adds flags (defaults to old semconv only)


PR 2: Expand RpcAttributesGetter interface (backward-compatible API change)

Files:

  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientAttributesExtractor.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerAttributesExtractor.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSizeAttributesExtractor.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java

Changes:

  • Add RESPONSE type parameter to RpcAttributesGetter<REQUEST, RESPONSE> with wildcard ? usage in extractors where response type isn't needed
  • Deprecate getMethod(REQUEST) and add new default methods: getRpcMethod(REQUEST), getErrorType(REQUEST, RESPONSE, Throwable), isPredefined(REQUEST)
  • Update extractor method signatures to accept RpcAttributesGetter<REQUEST, RESPONSE>

Impact: No behavioral change - default implementations return null/false preserving existing behavior


PR 3: Add dual-semconv support to RPC attributes extractors

Files:

  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java
  • instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesExtractorTest.java
  • instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractorTest.java

Changes:

  • Add stable attribute keys: rpc.system.name, rpc.method_original, error.type
  • Update onStart() to emit old/stable attributes based on stability flags
  • Update onEnd() to emit error.type for stable semconv
  • Update RpcSpanNameExtractor.extract() to use getRpcMethod() for stable semconv
  • Update tests to validate both semconv modes

PR 4: Add dual-semconv support to RPC metrics

Files:

  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java
  • instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java
  • instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java
  • instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetricsTest.java

Changes:

  • Add new metrics: rpc.client.call.duration (s), rpc.server.call.duration (s)
  • Keep old metrics: rpc.client.duration (ms), rpc.server.duration (ms)
  • Update RpcMetricsAdvice with stable attribute key list (rpc.system.name, rpc.response.status_code)
  • Record to old/stable histograms based on stability flags
  • Update tests for both semconv modes

PR 5: Update instrumentation implementations

Files: All *RpcAttributesGetter implementations across instrumentation modules:

Instrumentation File
apache-dubbo-2.7 DubboRpcAttributesGetter.java
aws-sdk-1.11 AwsSdkRpcAttributesGetter.java, AwsSdkSpanNameExtractor.java
aws-sdk-2.2 AwsSdkRpcAttributesGetter.java
grpc-1.6 GrpcRpcAttributesGetter.java
gwt-2.0 GwtRpcAttributesGetter.java
rmi RmiClientAttributesGetter.java, RmiServerAttributesGetter.java
spring-rmi-4.0 ClientAttributesGetter.java, ServerAttributesGetter.java

Changes:

  • Update type signatures from RpcAttributesGetter<REQUEST> to RpcAttributesGetter<REQUEST, RESPONSE>
  • Add @Deprecated to existing getMethod() implementations
  • Optionally implement getRpcMethod(), getErrorType(), isPredefined() for richer stable semconv support
  • Add @SuppressWarnings("deprecation") to call sites like AwsSdkSpanNameExtractor

Verification

  • Build passes: ./gradlew build
  • Unit tests: ./gradlew :instrumentation-api-incubator:test
  • Test with stability flags: ./gradlew :instrumentation-api-incubator:testStableSemconv :instrumentation-api-incubator:testBothSemconv
  • Verify no public API breaks with japicmp

Design Decisions

Decision Rationale
Incremental merging over atomic Each PR delivers reviewable, testable value and can be merged independently
Interface change first Changing RpcAttributesGetter signature early with backward-compatible defaults allows extractors and instrumentations to be updated in any order
Tests inline with implementation Test updates go with their corresponding implementation PR rather than a separate test-only PR

Dependencies

PR 1 (SemconvStability)
  │
  ├── PR 2 (RpcAttributesGetter interface)
  │     │
  │     ├── PR 3 (Attributes extractors) ──┐
  │     │                                  │
  │     └── PR 4 (Metrics) ────────────────┤
  │                                        │
  └────────────────────────────────────────┴── PR 5 (Instrumentation implementations)
  • PR 1 must be merged first (provides the stability flags)
  • PR 2 must be merged before PRs 3, 4, or 5 (changes the interface)
  • PRs 3 and 4 can be merged in parallel after PR 2
  • PR 5 can be merged after PR 2 (doesn't depend on 3 or 4)

@zeitlinger
Copy link
Member Author

here's an AI generated suggestion that seems reasonable

.. I'll let the AI do it 😄

When both old and stable RPC semconv are enabled, both use the
rpc.method attribute key with different values. Previously this was
solved with a virtual rpc.method.deprecated key in SemconvStability
that leaked onto spans.

Instead, register a ContextCustomizer that stores the old method value
directly in context via a ContextKey. RpcClientMetrics/RpcServerMetrics
read from context in onStart() and use the stored value when recording
old metrics. This keeps the old method value off spans entirely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants