Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

Store child spans in CLS context #256

Open
m1o1 opened this issue Dec 22, 2018 · 0 comments
Open

Store child spans in CLS context #256

m1o1 opened this issue Dec 22, 2018 · 0 comments

Comments

@m1o1
Copy link

m1o1 commented Dec 22, 2018

Is your feature request related to a problem? Please describe.
It would be convenient to have more control over child spans instead of just having root spans for incoming requests and child spans for outgoing requests. For example, if I have a client library that makes several requests to other services for authentication/authorization, I'd like to group them together in a single span.

#105 mentions the inability to create child spans from child spans, but this is a request to specifically store this information in a CLS namespace in a way that is accessible from anywhere (via tracer), as well as a request to adapt the design to have arbitrary spans not specific to receiving/sending requests.

Describe the solution you'd like
I'd like to have something like a withSpan(() => { ... }) function where the span is accessible via CLS (instead of coming as an argument in the callback), perhaps with tracer.currentSpan(). Maybe this could just wrap the function in a try/finally block to ensure that the span is closed. It would have to work with async and synchronous functions.

If there were a way to make use of decorators, that'd be even better, so marking a function with @Span with some configuration would create child spans (or follows-from spans once OpenTracing support is added) that start when the function starts and end when the function ends (or throws, etc.). I don't know enough about decorators to say how this would work / if it's possible.

Describe alternatives you've considered
I've considered writing my own solution using CLS, but this is a feature that I think would benefit others and I believe would belong in OpenCensus-node.

I came up with a (half-working) solution that maintained which span was active by maintaining a stack of spans on the CLS namespace. When a span was created, it was added to the stack, and it was removed when the span ended. I was not sure how well this would work for spans that start in one continuation and end in another, or how event-based spans would work.

I also considered just creating a new namespace context when a span is created (which would essentially be like maintaining a stack, since I think that maintains a stack internally). This would be cleaner than maintaining the stack myself, but has similar potential problems to above.

One other problem I ran into... I had a withSpan(...) function working that was generic and returned whatever type the callback returned, but this had the unfortunate effect where I could not figure out how to return from a function like:

function someFunction() {
  // START GROUP OF TASKS
  console.log("(some common group...");
  console.log("...of function...");
  if (some condition) {
    return false;
  }
  console.log("...calls)");
  a = resultOfTheseTasks();
  // END GROUP OF TASKS
  console.log(`Some task with ${a}`);
  return true;
}

When I move the group of tasks into something like const a = withSpan(...) callback, returning from the callback no longer has the effect I wanted (it's designed to return what I should store in a, so I can't return from the calling function with false), making the code more complex. I had to return a tuple, where the first argument was "isDone", which I'd use to see if I should return from the calling function or not. This made the code pretty sloppy, and I wasn't sure how to fix it. I guess I could have used closures, but wanted to be able to use const.

Additional context
In my attempted solution, I ran into a problem where (what OpenCensus calls) root spans would not be modifiable, since all that was available was the SpanContext (using OpenTracing) which is immutable. I resorted to creating child spans in middleware, so the root span was never accessible, and there was always a Span available to modify with tags and logs.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants