Skip to content
This repository has been archived by the owner on Nov 10, 2022. It is now read-only.

feat: add tracer.startActiveSpan() helper #14

Closed
wants to merge 1 commit into from

Conversation

naseemkullah
Copy link
Member

fixes: open-telemetry/opentelemetry-js#1923

Continuing on @Flarna's work from #7

Though it seems like open-telemetry/opentelemetry-js#1923 is where namespaced vs non-namespaced decision will be made. I will post there and ask for a tl;dr as to which it should be.

Add withSpan helper to execute a function on a context holding a given span.
@codecov
Copy link

codecov bot commented Feb 27, 2021

Codecov Report

Merging #14 (2816c4c) into main (493daa1) will increase coverage by 0.06%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #14      +/-   ##
==========================================
+ Coverage   94.65%   94.71%   +0.06%     
==========================================
  Files          39       39              
  Lines         505      511       +6     
  Branches       80       80              
==========================================
+ Hits          478      484       +6     
  Misses         27       27              
Impacted Files Coverage Δ
src/context/context.ts 93.47% <100.00%> (+0.97%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 493daa1...2816c4c. Read the comment docs.

*/
export function withSpan<
A extends unknown[],
F extends (...args: A) => ReturnType<F>
Copy link
Member

Choose a reason for hiding this comment

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

It would be great if F was also passed the span instance so you could write code like

withSpan(tracer.startSpan('name'), async (span: Span) => {
  // ...
});

and avoid having to assign the span to a variable first like:

let span = tracer.startSpan('foo');
withSpan(span, () => {...});

span = tracer.startSpan('bar');
withSpan(span, () => {...});

Copy link
Member

Choose a reason for hiding this comment

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

The main idea of putting a span into a context and set this context active is to avoid the need to pass spans around manually. Instead you fetch it via getSpan(context.active()).

Copy link
Member

Choose a reason for hiding this comment

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

To clarify, I don't mean so it's available to pass around to other functions. I just mean as a convenience in the "with" scope to avoid boilerplate like getSpan(context.active()).setAttribute('foo', 'bar') everywhere.

Copy link
Member

Choose a reason for hiding this comment

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

The recommended solution for that would be

withSpan(tracer.startSpan('name', { attributes }), async () => {
  // ...
});

Copy link
Member

Choose a reason for hiding this comment

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

Isn't a closure scope exactly providing what you want?

const span = tracer.startSpan('name');
withSpan(span, async () => {
  // ... span is accessible here
});

The current with() proposal allows to use any existing function without modification as you can pass thisArg and arguments as needed. Having a requirement to accept a span as first argument would be quite limiting or requires everyone to implement wrappers/adaptors.

Copy link
Member

@aabmass aabmass Apr 2, 2021

Choose a reason for hiding this comment

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

@dyladan right, but often you want to set attributes and span status, events, etc. conditionally and then always finally span.end().

I'm not claiming it's not possible to do as is, but this PR is intended to be a convenience API so why not make it even more convenient? It also avoids having to name a bunch of variables and have them all in scope when dealing with concurrent operations (which could be pretty error prone):

const span1 = tracer.startSpan('part1');
const span2 = tracer.startSpan('part2');
await Promise.all(
  withSpan(span1, async () => {
    // ...
    span1.end();
  }),
  withSpan(span2, async () => {
    // ...
    span2.end();
  })
);

vs

await Promise.all(
  withSpan(tracer.startSpan('part1'), async (span: Span) => {
    // ...
    span.end();
  }),
  withSpan(tracer.startSpan('part2'), async (span: Span) => {
    // ...
    span.end();
  })
);

@dyladan
Copy link
Member

dyladan commented Apr 29, 2021

Based on the spec in Span Creation https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span-creation

In languages with implicit Context propagation, Span creation MUST NOT set the newly created Span as the active Span in the current Context by default, but this functionality MAY be offered additionally as a separate operation.

I think we should have tracer.startActiveSpan() which takes all the same arguments as startSpan, but also takes as a final argument a callback which is called in the context of the span and receives the newly created span as an argument.

@naseemkullah naseemkullah changed the title feat: add withSpan helper feat: add tracer.startActiveSpan() helper May 1, 2021
@naseemkullah
Copy link
Member Author

closing and will open a new since suggested approach differs from original work and I cannot figure out This commit does not belong to any branch on this repository. right now

@naseemkullah naseemkullah deleted the with-span branch May 1, 2021 20:06
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

context.withSpan - proposition
4 participants