Skip to content

Expose the asyncLocalStorage for context propagation in tracing channels #6088

@logaretm

Description

@logaretm

We've been testing using tracing channels for some new instrumentations we are working on at Sentry, at first I thought the context propagation wasn't working and tried some of the stuff discussed in #1263 (comment)

However it does work with bindStore bound to the OTEL local storage and a transform function that returns the OTEL context.

// Get OTel's AsyncLocalStorage for bindStore
const otelStorage = (context as any)._getContextManager()._asyncLocalStorage;

if (otelStorage) {
  channel.start.bindStore(otelStorage, (data) => {
    const span = Sentry.startSpanManual(
      {
        // attrs...
      },
      (span) => span
    );

    data.span = span;

    // Return the context to store in AsyncLocalStorage
    return trace.setSpan(context.active(), span);
  });
}

channel.subscribe({
  // do stuff and handle span end/error
});

The problematic part here is using private APIs to grab the AsyncLocalStorage

const otelStorage = (context as any)._getContextManager()._asyncLocalStorage;

We think it would be great to offer a public API that let's us grab it for use-cases like that, I'm proposing either a:

  • A getter
import { context, trace } from '@opentelemetry/api';

// or `__dangerouslyXXX` if we are worried about abuse
const otelStorage = context.getAsyncLocalStorage();
  • Or a function that preps a tracing channel for context propagation:
function tracingChannelWithOtelContext(
  channel: TracingChannel,
  transform: (data: any) => Span
) {
  // Get OTel's AsyncLocalStorage for bindStore
  const otelStorage = 'Get the storage internally somehow...';

  channel.start.bindStore(otelStorage, (data) => {
    const span = transform(data);

    return trace.setSpan(context.active(), span);
  });
  return channel;
}

const channel = tracingChannelWithOtelContext(
  tracingChannel('unjs.unstorage'),
  (data) => {
    const span = Sentry.startSpanManual(
      {
        // attrs...
      },
      (span) => span
    );

    return span;
  }
);

I'm not aware of the implications of exposing this, so I think the public function approach is a little better since it keeps the private internals private, and allows SDKs to use tracing channels. At the same time I think exposing it is helpful for other usecases around AsyncLocalStorage.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions