Skip to content

[Feature]: Reading spans through the public API requires cloning everything to SpanData #2939

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

Open
paullegranddc opened this issue Apr 22, 2025 · 0 comments
Labels
enhancement New feature or request triage:todo Needs to be traiged.

Comments

@paullegranddc
Copy link
Contributor

paullegranddc commented Apr 22, 2025

Related Problems?

No response

Describe the solution you'd like:

When creating a new span, the tracer calls the SpanProcessor::on_start on method and passes a &mut Span.
In the otel spec, this span parameter is read/write opentelemetry-specification.

Currently in the rust SDK, the only way to read data on the span is to call span.exported_data() -> Option<crate::trace::SpanData>, which clones all fields in the span, including things that require allocation such as the span links attributes, events and span links.

Other opentelemetry SDK implementation usually expose a ReadableSpan interface
go java providing a view over the span without mutations, and I believe something similar could be implemented over the rust Span.

This is the API I am proposing:

impl Span {
    /// Returns a read-only view of the span data
    /// This is None if the span is not recording.
    pub fn read<'a>(&'a self) -> Option<ReadableSpan<'a>> {
        Some(ReadableSpan(self.data.as_ref()?))
    }
}

#[derive(Clone, Debug)]
pub struct ReadableSpan<'a>(&'a SpanData);

impl<'a> ReadableSpan<'a> {
    pub fn parent_span_id(&self) -> SpanId {..}
    pub fn span_kind(&self) -> SpanKind {..}

    pub fn name(&self) -> &str {..}

    pub fn start_time(&self) -> SystemTime {..}

    pub fn end_time(&self) -> SystemTime {..}

    pub fn attributes(&self) -> &'a [KeyValue] {..}

    pub fn dropped_attributes_count(&self) -> u32 {..}

    pub fn events(&self) -> &'a [Event] {..}

    pub fn dropped_events_count(&self) -> u32 {..}

    pub fn links(&self) -> &'a [Link] {..}

    pub fn dropped_links_count(&self) -> u32 {..}

    pub fn status(&self) -> &Status {..}
}

POC here

Considered Alternatives

I see two alternatives:

  1. An alternative would be to implement all of the getters directly on the opentelemetry_sdk::trace::Span struct.
    This is functionally the same, and we don't have to add a separate struct to the public API of ths crate.
    This make the API a bit harder to call though because every field that references a field in SpanData would need to either return an Option, or the default value (empty attribute slice, 0 for dropped entries, SpanId::Invalid for the parent id)

  2. Make ReadableSpan<'a> a trait, and return an opaque object implementating the trait from Span

impl Span {
    /// Returns a read-only view of the span data
    /// This is None if the span is not recording.
    pub fn read<'a>(&'a self) -> Option<impl ReadableSpan<'a>> {
        Some(ReadableSpan(self.data.as_ref()?))
    }
}


trait ReadableSpan<'a> {...}

This is more complex, as the value become un-nameable and thus cannot be stored easily in collections.
I don't really know what this buys us, and who else would implement the trait....

Additional Context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request triage:todo Needs to be traiged.
Projects
None yet
Development

No branches or pull requests

1 participant