Skip to content

Commit

Permalink
safer context manager default
Browse files Browse the repository at this point in the history
  • Loading branch information
EmrysMyrddin committed Feb 14, 2025
1 parent 689be49 commit daed24a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 49 deletions.
42 changes: 0 additions & 42 deletions packages/plugins/opentelemetry/src/context-stack.ts

This file was deleted.

69 changes: 69 additions & 0 deletions packages/plugins/opentelemetry/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Logger } from '@graphql-mesh/types';
import { trace, type Context, type ContextManager } from '@opentelemetry/api';
import type { PromiseOrValue } from 'graphql-yoga';

type Node = {
ctx: Context;
previous?: Node;
};

export class OtelContextStack {
#root: Node;
#current: Node;

constructor(root: Context) {
this.#root = { ctx: root };
this.#current = this.#root;
}

get current(): Context {
return this.#current.ctx;
}

get root(): Context {
return this.#root.ctx;
}

push = (ctx: Context) => {
this.#current = { ctx, previous: this.#current };
};

pop = () => {
this.#current = this.#current.previous ?? this.#root;
};

toString() {
let node: Node | undefined = this.#current;
const names = [];
while (node != undefined) {
names.push((trace.getSpan(node.ctx) as unknown as { name: string }).name);
node = node.previous;
}
return names.join(' -> ');
}
}

export function getContextManager(
logger: Logger,
useContextManager: boolean,
contextManager?: false | ContextManager,
): PromiseOrValue<ContextManager | false | undefined> {
if (contextManager != undefined) {
return contextManager;
}

return import('@opentelemetry/context-async-hooks')
.then((module) => new module.AsyncLocalStorageContextManager())
.catch((err) => {
if ((err as any).code === 'ERR_MODULE_NOT_FOUND') {
if (useContextManager) {
logger.error(
"AsyncLocalContext is not available: can't initialize context manager. Either disable context manager usage by providing `useContextManager: false` option or a context manager in the `contextManager` option.",
);
}
return undefined;
} else {
throw err;
}
});
}
13 changes: 6 additions & 7 deletions packages/plugins/opentelemetry/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { DisposableSymbols } from '@whatwg-node/disposablestack';
import { type OnRequestEventPayload } from '@whatwg-node/server';
import type { OnParamsEventPayload, YogaInitialContext } from 'graphql-yoga';
import { ATTR_SERVICE_VERSION, SEMRESATTRS_SERVICE_NAME } from './attributes';
import { OtelContextStack } from './context-stack';
import { getContextManager, OtelContextStack } from './context';
import {
getMostSpecificState,
withState,
Expand Down Expand Up @@ -246,12 +246,11 @@ export function useOpenTelemetry(
asyncAttributes,
);

const contextManager$ =
options.contextManager != undefined
? options.contextManager
: import('@opentelemetry/context-async-hooks').then(
(module) => new module.AsyncLocalStorageContextManager(),
);
let contextManager$ = getContextManager(
pluginLogger,
useContextManager,
options.contextManager,
);

preparation$ = mapMaybePromise(exporters$, (exporters) => {
spanProcessors = exporters;
Expand Down

0 comments on commit daed24a

Please sign in to comment.