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

Add span builder #72

Merged
merged 2 commits into from
Mar 16, 2020
Merged

Add span builder #72

merged 2 commits into from
Mar 16, 2020

Conversation

jtescher
Copy link
Member

This patch adds a SpanBuilder struct that allows Tracers to provide
a method for easily configuring Span attributes before the spans are
started.

Example:

let tracer = global::trace_provider().get_tracer("example-tracer");

// Use span builder to assign span properties, note that a tracer reference
// must be passed to `start`.
let _span = tracer
    .span_builder("example-span-name")
    .with_kind(api::SpanKind::Server)
    .start(&tracer);

Resolves #13, resolves #23

@jtescher jtescher requested a review from a team February 25, 2020 05:05
Copy link
Member

@MikeGoldsmith MikeGoldsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just one query regarding passing the tracer in earlier.

/// SpanBuilder methods
impl SpanBuilder {
/// Create a new span builder from a span name
pub fn from_name(name: String) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we take a tracer reference as a parameter here? It means we wouldn't need to pass the tracer into the Start fn below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it definitely is not ideal to have to pass a reference to start. Seems like there are a few options:

The expected API would be something like:

let _span = tracer
    .span_builder("example-span-name")
    .with_kind(api::SpanKind::Server)
    .start();

The simple way to accomplish this would be for SpanBuilder to have a type parameter:

impl SpanBuilder<T: api::Tracer> {
    pub fn from_name(name: String, tracer: &T) -> Self {}
    pub fn start(self) -> T::Span {}
   ...
}

However that would mean the Tracer impl would look like:

pub trait Tracer {
    fn span_builder(&self) -> SpanBuilder<Self> {}
}

And we need Tracers to be trait objects when used in the global module so they cannot have Self in parameters or return types.

Another option could be that instead people use SpanBuilder directly:

    SpanBuilder::from_name("example-span-name", &tracer).start()

But that isn't very ergonomic either.

The current solution seems like a middle ground, Tracers can still be trait objects as their methods do not reference Self, but it requires an explicit reference on span builder's start.

Definitely open to alternatives if I've missed a better option that allows for the ideal API above. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively SpanBuilder could accept an &dyn api::Tracer and return a Box<dyn api::Span>? Tradeoff would be having an explicitly boxed span as the return type, but that might be preferable to the tracer reference?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - sorry for slow reply.

I think using Box<..> types for the tracer and span would be okay. Would it be possible to support both your initial and boxed tracer options?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah can keep trying. Might be another option that doesn't have these downsides...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeGoldsmith I think this is the best we can get currently with two options:

 // The builder can be used to create a span directly with the tracer
 let _span = tracer.build(SpanBuilder {
     name: "example-span-name".to_string(),
     span_kind: Some(SpanKind::Server),
     ..Default::default()
 });

 // Or used with builder pattern
 let _span = tracer
     .span_builder("example-span-name")
     .with_kind(SpanKind::Server)
     .start(&tracer);

The builder has a hard time taking a reference because the types can't be known ahead of time, e.g. tracer: &dyn Tracer<Span=?> can't encode that without needing the builder to be generic, which doesn't currently work in the global module.

A middle ground is having the builder impl Default so it can be used as a form of default args as I show above. The upside of the start method taking a reference to the tracer is builders can be passed around without worrying about the tracer lifetime or having to clone tracers for every builder.

Any other ideas on how this API could be nicer? or should we go with this for now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the second example, where we use the builder pattern is the most readable. Let's go in that direction for now, and we can re-visit in the future if needed - this PR has been hanging around for awhile so lets get it merged 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok great, the PR currently supports both patterns I pasted there so we should be good to go if you want to review 🎉

This patch adds a `SpanBuilder` struct that allows `Tracer`s to provide
a method for easily configuring `Span` attributes before the spans are
started.

Example:
```rust
let tracer = global::trace_provider().get_tracer("example-tracer");

// Use span builder to assign span properties, note that a tracer reference
// must be passed to `start`.
let _span = tracer
    .span_builder("example-span-name")
    .with_kind(api::SpanKind::Server)
    .start(&tracer);
```
@jtescher jtescher merged commit 57152fc into open-telemetry:master Mar 16, 2020
@jtescher jtescher deleted the feature/span-builder branch March 16, 2020 18:13
cijothomas added a commit to cijothomas/opentelemetry-rust that referenced this pull request Sep 7, 2024
Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Store span kind Add support for additional span creation options
2 participants