Skip to content
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

Ensure is_recording is false after span ends #265

Closed
jtescher opened this issue Oct 12, 2020 · 8 comments · Fixed by #341
Closed

Ensure is_recording is false after span ends #265

jtescher opened this issue Oct 12, 2020 · 8 comments · Fixed by #341
Labels
help wanted Good for taking. Extra help will be provided by maintainers/approvers

Comments

@jtescher
Copy link
Member

See open-telemetry/opentelemetry-specification#1011 for details.

@jtescher jtescher added the help wanted Good for taking. Extra help will be provided by maintainers/approvers label Oct 21, 2020
@morigs
Copy link
Contributor

morigs commented Oct 22, 2020

It seems currently span can be ended in two ways:

Right now is_recording handles only the first case. So I would like to suggest just to replace this line with something like this:

match self.with_data(|data| data.end_time.is_some()) {
  Some(ended) => !ended,
  None => false,
}

@jtescher
Copy link
Member Author

@morigs yeah additionally it may be a good idea to move the logic from drop to end_with_timestamp so other methods like setting attributes are no-ops as defined by the spec

@morigs
Copy link
Contributor

morigs commented Oct 26, 2020

@jtescher I think it's better not to move logic from drop because this will not allow to get SpanContext after end.
As an alternative I could suggest adding additional check to every method (check end_time for example)

@morigs
Copy link
Contributor

morigs commented Oct 26, 2020

Oh-oh, I forgot that end_time is always set. May be we should define some new bool (is_ended) and set it in end?

@jtescher
Copy link
Member Author

Actually it's probably preferable to move span context outside the mutex, would also allow span context to return a reference instead of a clone which would be an improvement. I have a branch that does this that I could clean up and push.

@morigs
Copy link
Contributor

morigs commented Oct 26, 2020

I have added some tests in #309
Some of them related to this issue

jtescher added a commit that referenced this issue Nov 1, 2020
Currently the `Span` trait in the API has its `span_context` method
return an owned `SpanContext`. In order to be more consistent with the
rest of the APIs, this patch updates the trait to instead return a
reference, and moves the `SpanContext` data outside of the mutex in the
SDK to implement the trait. This also allows #265 to be implemented
without having to lock.
jtescher added a commit that referenced this issue Nov 1, 2020
Currently the `Span` trait in the API has its `span_context` method
return an owned `SpanContext`. In order to be more consistent with the
rest of the APIs, this patch updates the trait to instead return a
reference, and moves the `SpanContext` data outside of the mutex in the
SDK to implement the trait. This also allows #265 to be implemented
without having to lock.

Additionally the spec now states that the `on_start` method for
`SpanProcessor`s must be passed a span and the parent context so those
are updated as well.
jtescher added a commit that referenced this issue Nov 1, 2020
Currently the `Span` trait in the API has its `span_context` method
return an owned `SpanContext`. In order to be more consistent with the
rest of the APIs, this patch updates the trait to instead return a
reference, and moves the `SpanContext` data outside of the mutex in the
SDK to implement the trait. This also allows #265 to be implemented
without having to lock.

Additionally the spec now states that the `on_start` method for
`SpanProcessor`s must be passed a span and the parent context so those
are updated as well.
jtescher added a commit that referenced this issue Nov 1, 2020
Currently the `Span` trait in the API has its `span_context` method
return an owned `SpanContext`. In order to be more consistent with the
rest of the APIs, this patch updates the trait to instead return a
reference, and moves the `SpanContext` data outside of the mutex in the
SDK to implement the trait. This also allows #265 to be implemented
without having to lock.

Additionally the spec now states that the `on_start` method for
`SpanProcessor`s must be passed a span and the parent context so those
are updated as well.
@jtescher jtescher mentioned this issue Nov 2, 2020
15 tasks
@morigs
Copy link
Contributor

morigs commented Nov 3, 2020

@jtescher Hi. I'm working on moving logic from drop to end_with_timestamp. Two points here:

  • We still must end span on drop. So I've replaced impl Drop for SpanInner with impl Drop for Span because SpanInner doesn't have end method
  • end_with_timestamp will require mutable reference to take ownership of inner.data. This conflicts with api::trace::span::Span trait. Is it ok to change Span trait in the api?
Click to expand
error[E0053]: method `end_with_timestamp` has an incompatible type for trait
   --> opentelemetry\src\sdk\trace\span.rs:142:27
    |
142 |     fn end_with_timestamp(&mut self, timestamp: SystemTime) {
    |                           ^^^^^^^^^ types differ in mutability        
    | 
   ::: opentelemetry\src\api\trace\span.rs:165:27
    |
165 |     fn end_with_timestamp(&self, timestamp: SystemTime);
    |                           ----- type in trait
    |
    = note: expected fn pointer `fn(&sdk::trace::span::Span, std::time::SystemTime)`
               found fn pointer `fn(&mut sdk::trace::span::Span, std::time::SystemTime)`

@jtescher
Copy link
Member Author

jtescher commented Nov 3, 2020

@morigs I believe drop has to be on SpanInner because you can have many clones of a span and the first drop does not necessarily imply span end. It should be possible currently if you move the logic in drop to a function that can be called from span or on inner drop. Maybe something along the lines of:

impl SpanInner {
    fn end_with_timestamp(&self, timestamp: Option<SystemTime>) {
        // Calling end on unsampled span or already ended span is ignored
        if let Some(data) = &self.data {
            if let Ok(mut span_data) = data.lock().map(|mut data| data.take()) {

                // Ensure end time is set via explicit end or implicitly on drop
                if let Some(span_data) = span_data.as_mut() {
                    if let Some(timestamp) = timestamp {
                        span_data.end_time = timestamp;
                    } else if span_data.end_time == span_data.start_time {
                        span_data.end_time = SystemTime::now();
                    }
                }

                // Notify each span processor that the span has ended
                if let Some(provider) = self.tracer.provider() {
                    let mut processors = provider.span_processors().iter().peekable();
                    while let Some(processor) = processors.next() {
                        let span_data = if processors.peek().is_none() {
                            // last loop or single processor/exporter, move data
                            span_data.take()
                        } else {
                            // clone so each exporter gets owned data
                            span_data.clone()
                        };

                        if let Some(span_data) = span_data {
                            processor.on_end(build_export_data(
                                span_data,
                                self.span_context.clone(),
                                &self.tracer,
                            ));
                        }
                    }
                }
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Good for taking. Extra help will be provided by maintainers/approvers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants