-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add metadata attributes #59
Conversation
e60f841
to
ab9d879
Compare
ab9d879
to
17a3e65
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally seems like a good idea, especially if the Java & JS otel-grpc integrations do this. A few suggestions:
- Let's make sure we structure the options roughly the same as Java & JS. I'd expect both an allowlist and a denylist, but please do a little research and comment here with how the other implementations do it.
- No matter how we address the previous point, we need space for the allow/deny/etc config to evolve without breaking back compat. Let's separate
WithTraceRequestMetadata
andWithTraceResponseMetadata
and have each take a config struct (so we have space to add more config struct fields over time). - Please model the allowlists as sets instead of slices, so we have O(1) membership checks.
On further thought, mixing config structs and functional options seems bad. LMK what your research into the OTel-gRPC integrations in Java and JS turns up and we can decide how to proceed. |
on grpc java it's done as follows: /** Sets which metadata request values should be captured as span attributes on client spans. */
@CanIgnoreReturnValue
public GrpcTelemetryBuilder setCapturedClientRequestMetadata(
List<String> capturedClientRequestMetadata) {
this.capturedClientRequestMetadata = capturedClientRequestMetadata;
return this;
}
/** Sets which metadata request values should be captured as span attributes on server spans. */
@CanIgnoreReturnValue
public GrpcTelemetryBuilder setCapturedServerRequestMetadata(
List<String> capturedServerRequestMetadata) {
this.capturedServerRequestMetadata = capturedServerRequestMetadata;
return this;
} and constructing this: @Override
protected ManagedChannelBuilder<?> configureClient(ManagedChannelBuilder<?> client) {
return client.intercept(
GrpcTelemetry.builder(testing.getOpenTelemetry())
.setCapturedClientRequestMetadata(
Collections.singletonList(CLIENT_REQUEST_METADATA_KEY))
.build()
.newClientInterceptor()); getting the metadata: List<String> metadataValue(GrpcRequest request, String key) {
if (request.getMetadata() == null) {
return Collections.emptyList();
}
if (key == null || key.isEmpty()) {
return Collections.emptyList();
}
Iterable<String> values =
request.getMetadata().getAll(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
if (values == null) {
return Collections.emptyList();
}
return StreamSupport.stream(values.spliterator(), false).collect(Collectors.toList());
} calling the metadata function: for (String key : capturedRequestMetadata) {
List<String> value = getter.metadataValue(request, key);
if (!value.isEmpty()) {
attributes.put(requestAttributeKey(key), value);
}
} This is basically the same way that this PR implements it:
|
Question about this; there is never a case where a single lookup is done; in each request the keys of metadataReqKeys and metadataResKeys are checked against The only benefit of having a map is that duplicate keys aren't logged, but that could be done in the option, keeping the option parameter a []string, then remove duplicates, then continue with a []string EDIT: plus it also makes testing awkward because it's non deterministic to iterate through a map |
interceptor.go
Outdated
// metadata until the send function is called. This means that traces are created without the request | ||
// metadata attributes, so samplers don't have access to metadata attributes in streaming clients. | ||
span.SetAttributes(headerAttributes(protocol, requestKey, conn.RequestHeader(), i.config.metadataReqKeys)...) | ||
span.SetAttributes(headerAttributes(protocol, responseKey, conn.ResponseHeader(), i.config.metadataResKeys)...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like we ought to move span creation to the first call to Send
or the first call to Close
, whichever happens first? That'll also make the RPC duration match network activity more closely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed through createSpanOnce
that gets called in both close
and send
: 1f41e4c
return &streamingClientInterceptor{ | ||
StreamingClientConn: conn, | ||
onClose: func() { | ||
createSpanOnce.Do(createSpan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look to me like this is thread-safe. onClose
is called from CloseResponse
, which must be safe to call concurrently with Send
. (This is documented on the StreamingClientConn
interface.) Using a sync.Once
guarantees that createSpan
is only called once, but it does not guarantee that the span has actually been created - it's possible for a call to CloseResponse
to be in the midst of creating the span while a concurrent call to Send
skips over the Once
and attempts to use the nonexistent span.
Let's pair program this afternoon and try to fix this up!
Adds metadata attributes to all types. Metadata attributes are _only_ defined in the spec for trace span attributes. The ones defined are: - `rpc.<protocol>.request.metadata.<normalised key>` - `rpc.<protocol>.response.metadata.<normalised key>` Where; - The `<normalised key>` are specified via the `WithTraceMetadataAttributes(requestKeys, responseKeys []string)` Option. - protocol is one of `grpc`, `grpc_web` or `buf_connect` - "normalised" is `<key> being the normalized gRPC Metadata key (lowercase, with - characters replaced by _), the value being the metadata values.` as per the [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md#grpc-request-and-response-metadata). This is implemented [here](https://github.com/bufbuild/connect-opentelemetry-go/pull/59/files#r1072737068) - The value formatting is in the format `rpc.request.metadata.my_custom_metadata_attribute=["1.2.3.4", "1.2.3.5"]`, which is implemented [here](https://github.com/bufbuild/connect-opentelemetry-go/pull/59/files#r1072738606) Closes: https://github.com/bufbuild/connect-opentelemetry-go/issues/52
Adds metadata attributes to all types.
Metadata attributes are only defined in the spec for trace span attributes.
The ones defined are:
rpc.<protocol>.request.metadata.<normalised key>
rpc.<protocol>.response.metadata.<normalised key>
Where;
<normalised key>
are specified via theWithTraceMetadataAttributes(requestKeys, responseKeys []string)
Option.grpc
,grpc_web
orbuf_connect
<key> being the normalized gRPC Metadata key (lowercase, with - characters replaced by _), the value being the metadata values.
as per the spec. This is implemented hererpc.request.metadata.my_custom_metadata_attribute=["1.2.3.4", "1.2.3.5"]
, which is implemented hereCloses: https://github.com/bufbuild/connect-opentelemetry-go/issues/52