-
Notifications
You must be signed in to change notification settings - Fork 6
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
Conform client's tracing interceptor to OTel's conventions #25
Conversation
This API break is expected, as the interceptor has been renamed:
|
1d1f018
to
2929500
Compare
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
48530eb
to
dd7bc23
Compare
Sources/GRPCInterceptors/Tracing/ClientOTelTracingInterceptor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/SpanAttributes+RPCAttributes.swift
Outdated
Show resolved
Hide resolved
case .other(let address): | ||
// We can't nicely format the span attributes to contain the appropriate information here, | ||
// so include the whole thing as part of the server address. | ||
self.attributes.rpc.serverAddress = address |
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.
I'm not sure about this. We set serverAddress
above to serverHostname
which is a value explicitly passed in by the user so I don't think we should be overriding it here.
I think my lean is to ignore the remote peer if we can't parse it. (Meaning PeerAddress
should have a failable init rather than other
.)
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.
Actually this is a mistake - I think we should be setting network.peer.address
. I agree the server address should stay as the hostname. The port is only required if the transport supports it, which we don't know at this stage so we can just leave it blank.
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.
This one didn't get fixed.
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.
Yeah I've changed it to set "network.peer.address" instead
self.attributes.rpc.serverPort = port | ||
|
||
case .unixDomainSocket(let path): | ||
self.attributes.rpc.networkType = "unix" |
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.
Looking at the docs https://opentelemetry.io/docs/specs/semconv/attributes-registry/network/, it seems like network type should be either "ipv4"/"ipv6" and that "unix" should be a transport.
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.
Hm, so should we leave the network type empty when using UDS?
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.
Also unsure about setting the network transport here, since we're getting it as an argument when initialising the interceptor.
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.
network.type has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.
We don't have a well-known value here (they list "ipv4" and "ipv6") only. We could use a custom value but not sure that would actually add useful information (given we'd set "network.transport" to "unix") so I think we shouldn't set it.
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.
Also unsure about setting the network transport here, since we're getting it as an argument when initialising the interceptor.
What about this? IMO, we shouldn't set it, as it's the same argument as we don't want to override the server address; we don't want to override the network transport either.
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.
Sorry, forgot to reply to that one. I agree we shouldn't set it; let's leave that one up to the user.
Sources/GRPCInterceptors/Tracing/SpanAttributes+RPCAttributes.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCInterceptors/Tracing/SpanAttributes+RPCAttributes.swift
Outdated
Show resolved
Hide resolved
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.
I think a few things got missed but this is definitely much closer to where it needs to be!
self.attributes.rpc.serverPort = port | ||
|
||
case .unixDomainSocket(let path): | ||
self.attributes.rpc.networkType = "unix" |
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.
network.type has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.
We don't have a well-known value here (they list "ipv4" and "ipv6") only. We could use a custom value but not sure that would actually add useful information (given we'd set "network.transport" to "unix") so I think we shouldn't set it.
case .other(let address): | ||
// We can't nicely format the span attributes to contain the appropriate information here, | ||
// so include the whole thing as part of the server address. | ||
self.attributes.rpc.serverAddress = address |
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.
This one didn't get fixed.
Sources/GRPCInterceptors/Tracing/SpanAttributes+GRPCTracingKeys.swift
Outdated
Show resolved
Hide resolved
guard addressComponents.count == 3, let port = Int(addressComponents[2]) else { | ||
// This is some unexpected/unknown format, so we have no way of splitting it up nicely. | ||
self = .other(address) | ||
return | ||
} | ||
self = .ipv4(address: String(addressComponents[1]), port: port) |
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.
A previous comment got missed: #25 (comment)
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.
This is also fixed with the changes I brought from the server PR
Sources/GRPCInterceptors/Tracing/SpanAttributes+RPCAttributes.swift
Outdated
Show resolved
Hide resolved
@Test("Unrecognised addresses are correctly parsed") | ||
func testOther() { | ||
let address = PeerAddress("in-process:1234") | ||
#expect(address == .other("in-process:1234")) | ||
} |
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.
I'm still not sure whether we should include addresses we can't parse. I think my lean is to not include them because we're then more likely to catch parsing bugs (I think a user is more likely to report an issue if the information is missing rather than not quite appearing as it should).
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.
Hm, I'm not sure about this. I feel like silently skipping information is worse than showing it in a not-ideal way.
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.
I'm not sure I agree: we don't know what the value is so we should treat it with a bit of suspicion.
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.
I don't feel that strongly about this so I don't mind not setting it, but what is the worst that could happen if we did set it?
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.
I can think of a few things:
- If, for some reason, the string is very dynamic (i.e. changes for each RPC) then we could pollute the users telemetry collection service with thousands of potentially useless values.
- A very long could cause issues if the collector doesn't handle long strings well.
In practice these both seem pretty unlikely, but they are possible.
IMO the more compelling reason to not set it is that users are more likely to complain if the value is missing and then we can figure out why parsing failed so we can set it correctly.
I also find it weird that we can return .other
when we know it's ipv4/ipv6 but we can't parse it, because it doesn't meet our expectations of what that address type should be.
41aa5fe
to
3275f72
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.
This basically looks good; just the one nit around "[" and resolving how to handle that.
// At this point, we are looking at an address with format: [<address>]:<port> | ||
// We drop the first character ('[') and split by ']:' to keep two components: the address | ||
// and the port. | ||
let ipv6AddressComponents = addressComponents[1].dropFirst().split(separator: "]:") |
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.
I think we should be a bit stricter here and check that we actually have a "[".
We also have plenty of allocations here which are mostly avoidable (which I'm fine to punt on for now), but could you file an issue? The idea would be to avoid split
and all the extra String
conversions by using the UTF8 view.
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.
@Test("Unrecognised addresses are correctly parsed") | ||
func testOther() { | ||
let address = PeerAddress("in-process:1234") | ||
#expect(address == .other("in-process:1234")) | ||
} |
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.
I can think of a few things:
- If, for some reason, the string is very dynamic (i.e. changes for each RPC) then we could pollute the users telemetry collection service with thousands of potentially useless values.
- A very long could cause issues if the collector doesn't handle long strings well.
In practice these both seem pretty unlikely, but they are possible.
IMO the more compelling reason to not set it is that users are more likely to complain if the value is missing and then we can figure out why parsing failed so we can set it correctly.
I also find it weird that we can return .other
when we know it's ipv4/ipv6 but we can't parse it, because it doesn't meet our expectations of what that address type should be.
This PR brings the same changes made to the client in #25 to the server interceptor, conforming it to follow recommendations/conventions laid out in both: - https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans - https://opentelemetry.io/docs/specs/semconv/rpc/grpc/ --------- Co-authored-by: George Barnett <[email protected]>
This PR conforms the client tracing interceptor to follow recommendations/conventions laid out in both: