Skip to content

Commit daed24a

Browse files
committed
safer context manager default
1 parent 689be49 commit daed24a

File tree

3 files changed

+75
-49
lines changed

3 files changed

+75
-49
lines changed

packages/plugins/opentelemetry/src/context-stack.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { Logger } from '@graphql-mesh/types';
2+
import { trace, type Context, type ContextManager } from '@opentelemetry/api';
3+
import type { PromiseOrValue } from 'graphql-yoga';
4+
5+
type Node = {
6+
ctx: Context;
7+
previous?: Node;
8+
};
9+
10+
export class OtelContextStack {
11+
#root: Node;
12+
#current: Node;
13+
14+
constructor(root: Context) {
15+
this.#root = { ctx: root };
16+
this.#current = this.#root;
17+
}
18+
19+
get current(): Context {
20+
return this.#current.ctx;
21+
}
22+
23+
get root(): Context {
24+
return this.#root.ctx;
25+
}
26+
27+
push = (ctx: Context) => {
28+
this.#current = { ctx, previous: this.#current };
29+
};
30+
31+
pop = () => {
32+
this.#current = this.#current.previous ?? this.#root;
33+
};
34+
35+
toString() {
36+
let node: Node | undefined = this.#current;
37+
const names = [];
38+
while (node != undefined) {
39+
names.push((trace.getSpan(node.ctx) as unknown as { name: string }).name);
40+
node = node.previous;
41+
}
42+
return names.join(' -> ');
43+
}
44+
}
45+
46+
export function getContextManager(
47+
logger: Logger,
48+
useContextManager: boolean,
49+
contextManager?: false | ContextManager,
50+
): PromiseOrValue<ContextManager | false | undefined> {
51+
if (contextManager != undefined) {
52+
return contextManager;
53+
}
54+
55+
return import('@opentelemetry/context-async-hooks')
56+
.then((module) => new module.AsyncLocalStorageContextManager())
57+
.catch((err) => {
58+
if ((err as any).code === 'ERR_MODULE_NOT_FOUND') {
59+
if (useContextManager) {
60+
logger.error(
61+
"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.",
62+
);
63+
}
64+
return undefined;
65+
} else {
66+
throw err;
67+
}
68+
});
69+
}

packages/plugins/opentelemetry/src/plugin.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { DisposableSymbols } from '@whatwg-node/disposablestack';
3333
import { type OnRequestEventPayload } from '@whatwg-node/server';
3434
import type { OnParamsEventPayload, YogaInitialContext } from 'graphql-yoga';
3535
import { ATTR_SERVICE_VERSION, SEMRESATTRS_SERVICE_NAME } from './attributes';
36-
import { OtelContextStack } from './context-stack';
36+
import { getContextManager, OtelContextStack } from './context';
3737
import {
3838
getMostSpecificState,
3939
withState,
@@ -246,12 +246,11 @@ export function useOpenTelemetry(
246246
asyncAttributes,
247247
);
248248

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

256255
preparation$ = mapMaybePromise(exporters$, (exporters) => {
257256
spanProcessors = exporters;

0 commit comments

Comments
 (0)