opentelemetry-go
opentelemetry-go copied to clipboard
Let users create ReadOnlySpan instances from outside the SDK
Problem Statement
Instead of exposing the mutable SpanSnapshot struct, the SDK now uses the ReadOnlySpan interface. This interface has an un-exported func, which prevents anybody outside the SDK from creating new implementations.
Creating new instances of ReadOnlySpan is valuable:
- when writing unit tests for an exporter
- when using an exporter without an SDK (e.g. in the collector)
Proposed Solution
type SpanFields {
// same as SpanSnapshot fields from before
}
func NewReadOnlySpan(f SpanFields) ReadOnlySpan {
// copy the fields to a spanSnapshot
}
This way,
- ReadOnlySpan remains immutable
- SpanSnapshot's fields remain unexported, so people can't cast their way out of the immutability
- API remains ergonomic if new fields are added to SpanFields (because they will be zero-initialized)
Alternatives
- Each user creates a copy of ReadOnlySpan and SpanSnapshot, and creates a private API to pass that in to their exporter (for example)
- Have NewReadOnlySpan take a bunch of
WithFoo(...)args. This is a lot more code to write in the SDK, and seems unnecessary. - [added] Users embed
ReadOnlySpanin a struct and override any relevant methods (for example)
For completeness, I should mention that we can embed the ReadOnlySnapshot interface in a struct as a way to implement the interface. This may lower the priority of this request, but I think it's still worth pursuing.
(Thanks to @bogdandrutu for suggesting this approach.)
We have some reasons for wanting to implement our own version of ReadOnlySpan, where the traces doesn't map properly to a typical request/response, and a function call (stack) kind of flow.
Like @punya mentioned, I'm working around it by embedding the interface into our struct, but ideally I rather prefer not doing that and just outright implement the interface methods.