@envelop/opentelemetry 6.1.0 → 6.2.0-alpha-20240118112454-9c258182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/has-inline-argument.js +30 -0
- package/cjs/index.js +132 -24
- package/esm/has-inline-argument.js +26 -0
- package/esm/index.js +135 -27
- package/package.json +1 -1
- package/typings/has-inline-argument.d.cts +2 -0
- package/typings/has-inline-argument.d.ts +2 -0
- package/typings/index.d.cts +6 -3
- package/typings/index.d.ts +6 -3
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasInlineArgument = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
function hasInlineArgument(doc) {
|
|
6
|
+
let seen = false;
|
|
7
|
+
const leave = () => {
|
|
8
|
+
seen = true;
|
|
9
|
+
return graphql_1.BREAK;
|
|
10
|
+
};
|
|
11
|
+
(0, graphql_1.visit)(doc, {
|
|
12
|
+
StringValue: {
|
|
13
|
+
leave,
|
|
14
|
+
},
|
|
15
|
+
BooleanValue: {
|
|
16
|
+
leave,
|
|
17
|
+
},
|
|
18
|
+
FloatValue: {
|
|
19
|
+
leave,
|
|
20
|
+
},
|
|
21
|
+
EnumValue: {
|
|
22
|
+
leave,
|
|
23
|
+
},
|
|
24
|
+
IntValue: {
|
|
25
|
+
leave,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return seen;
|
|
29
|
+
}
|
|
30
|
+
exports.hasInlineArgument = hasInlineArgument;
|
package/cjs/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const on_resolve_1 = require("@envelop/on-resolve");
|
|
|
8
8
|
const api_1 = require("@opentelemetry/api");
|
|
9
9
|
const opentelemetry = tslib_1.__importStar(require("@opentelemetry/api"));
|
|
10
10
|
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
11
|
+
const has_inline_argument_js_1 = require("./has-inline-argument.js");
|
|
11
12
|
var AttributeName;
|
|
12
13
|
(function (AttributeName) {
|
|
13
14
|
AttributeName["EXECUTION_ERROR"] = "graphql.execute.error";
|
|
@@ -18,6 +19,7 @@ var AttributeName;
|
|
|
18
19
|
AttributeName["RESOLVER_RESULT_TYPE"] = "graphql.resolver.resultType";
|
|
19
20
|
AttributeName["RESOLVER_ARGS"] = "graphql.resolver.args";
|
|
20
21
|
AttributeName["EXECUTION_OPERATION_NAME"] = "graphql.execute.operationName";
|
|
22
|
+
AttributeName["EXECUTION_OPERATION_TYPE"] = "graphql.execute.operationType";
|
|
21
23
|
AttributeName["EXECUTION_OPERATION_DOCUMENT"] = "graphql.execute.document";
|
|
22
24
|
AttributeName["EXECUTION_VARIABLES"] = "graphql.execute.variables";
|
|
23
25
|
})(AttributeName || (exports.AttributeName = AttributeName = {}));
|
|
@@ -30,12 +32,14 @@ const useOpenTelemetry = (options, tracingProvider, spanKind = api_1.SpanKind.SE
|
|
|
30
32
|
tracingProvider = basicTraceProvider;
|
|
31
33
|
}
|
|
32
34
|
const tracer = tracingProvider.getTracer(serviceName);
|
|
35
|
+
const spanByContext = new WeakMap();
|
|
33
36
|
return {
|
|
34
37
|
onPluginInit({ addPlugin }) {
|
|
35
38
|
if (options.resolvers) {
|
|
36
39
|
addPlugin((0, on_resolve_1.useOnResolve)(({ info, context, args }) => {
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
const parentSpan = spanByContext.get(context);
|
|
41
|
+
if (parentSpan) {
|
|
42
|
+
const ctx = opentelemetry.trace.setSpan(opentelemetry.context.active(), parentSpan);
|
|
39
43
|
const { fieldName, returnType, parentType } = info;
|
|
40
44
|
const resolverSpan = tracer.startSpan(`${parentType.name}.${fieldName}`, {
|
|
41
45
|
attributes: {
|
|
@@ -61,45 +65,149 @@ const useOpenTelemetry = (options, tracingProvider, spanKind = api_1.SpanKind.SE
|
|
|
61
65
|
}));
|
|
62
66
|
}
|
|
63
67
|
},
|
|
64
|
-
onExecute({ args
|
|
65
|
-
const
|
|
68
|
+
onExecute({ args }) {
|
|
69
|
+
const operationAst = (0, graphql_1.getOperationAST)(args.document, args.operationName);
|
|
70
|
+
if (!operationAst) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const operationType = operationAst.operation;
|
|
74
|
+
let isDocumentLoggable;
|
|
75
|
+
if (options.document == null || options.document === true) {
|
|
76
|
+
if (options.variables) {
|
|
77
|
+
isDocumentLoggable = true;
|
|
78
|
+
}
|
|
79
|
+
else if (!(0, has_inline_argument_js_1.hasInlineArgument)(args.document)) {
|
|
80
|
+
isDocumentLoggable = true;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
isDocumentLoggable = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
isDocumentLoggable = false;
|
|
88
|
+
}
|
|
89
|
+
const operationName = operationAst.name?.value || 'anonymous';
|
|
90
|
+
const executionSpan = tracer.startSpan(`${operationType}.${operationName}`, {
|
|
66
91
|
kind: spanKind,
|
|
67
92
|
attributes: {
|
|
68
93
|
...spanAdditionalAttributes,
|
|
69
|
-
[AttributeName.EXECUTION_OPERATION_NAME]:
|
|
70
|
-
[AttributeName.
|
|
94
|
+
[AttributeName.EXECUTION_OPERATION_NAME]: operationName,
|
|
95
|
+
[AttributeName.EXECUTION_OPERATION_TYPE]: operationType,
|
|
96
|
+
[AttributeName.EXECUTION_OPERATION_DOCUMENT]: isDocumentLoggable
|
|
97
|
+
? (0, core_1.getDocumentString)(args.document, graphql_1.print)
|
|
98
|
+
: undefined,
|
|
71
99
|
...(options.variables
|
|
72
100
|
? { [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(args.variableValues ?? {}) }
|
|
73
101
|
: {}),
|
|
74
102
|
},
|
|
75
103
|
});
|
|
104
|
+
const otelContext = opentelemetry.trace.setSpan(opentelemetry.context.active(), executionSpan);
|
|
76
105
|
const resultCbs = {
|
|
77
|
-
onExecuteDone({ result }) {
|
|
78
|
-
if ((0, core_1.isAsyncIterable)(result)) {
|
|
106
|
+
onExecuteDone({ result, setResult }) {
|
|
107
|
+
if (!(0, core_1.isAsyncIterable)(result)) {
|
|
108
|
+
if (result.data && options.result) {
|
|
109
|
+
executionSpan.setAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
|
|
110
|
+
}
|
|
111
|
+
if (options.traceIdInResult) {
|
|
112
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
113
|
+
}
|
|
114
|
+
markError(executionSpan, result);
|
|
79
115
|
executionSpan.end();
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.warn(`Plugin "opentelemetry" encountered an AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (result.data && options.result) {
|
|
85
|
-
executionSpan.setAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
|
|
86
116
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
return {
|
|
118
|
+
// handles async iterator
|
|
119
|
+
onNext: ({ result, setResult }) => {
|
|
120
|
+
if (options.traceIdInResult) {
|
|
121
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
122
|
+
}
|
|
123
|
+
markError(executionSpan, result);
|
|
124
|
+
},
|
|
125
|
+
onEnd: () => {
|
|
126
|
+
executionSpan.end();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
94
129
|
},
|
|
95
130
|
};
|
|
96
131
|
if (options.resolvers) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
132
|
+
spanByContext.set(args.contextValue, executionSpan);
|
|
133
|
+
}
|
|
134
|
+
return resultCbs;
|
|
135
|
+
},
|
|
136
|
+
onSubscribe({ args }) {
|
|
137
|
+
const operationAst = (0, graphql_1.getOperationAST)(args.document, args.operationName);
|
|
138
|
+
if (!operationAst) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const operationType = 'subscription';
|
|
142
|
+
let isDocumentLoggable;
|
|
143
|
+
if (options.variables) {
|
|
144
|
+
isDocumentLoggable = true;
|
|
145
|
+
}
|
|
146
|
+
else if (!(0, has_inline_argument_js_1.hasInlineArgument)(args.document)) {
|
|
147
|
+
isDocumentLoggable = true;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
isDocumentLoggable = false;
|
|
151
|
+
}
|
|
152
|
+
const operationName = operationAst.name?.value || 'anonymous';
|
|
153
|
+
const subscriptionSpan = tracer.startSpan(`${operationType}.${operationName}`, {
|
|
154
|
+
kind: spanKind,
|
|
155
|
+
attributes: {
|
|
156
|
+
...spanAdditionalAttributes,
|
|
157
|
+
[AttributeName.EXECUTION_OPERATION_NAME]: operationName,
|
|
158
|
+
[AttributeName.EXECUTION_OPERATION_TYPE]: operationType,
|
|
159
|
+
[AttributeName.EXECUTION_OPERATION_DOCUMENT]: isDocumentLoggable
|
|
160
|
+
? (0, core_1.getDocumentString)(args.document, graphql_1.print)
|
|
161
|
+
: undefined,
|
|
162
|
+
...(options.variables
|
|
163
|
+
? { [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(args.variableValues ?? {}) }
|
|
164
|
+
: {}),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
const otelContext = opentelemetry.trace.setSpan(opentelemetry.context.active(), subscriptionSpan);
|
|
168
|
+
const resultCbs = {
|
|
169
|
+
onSubscribeError: ({ error }) => {
|
|
170
|
+
if (error)
|
|
171
|
+
subscriptionSpan.setStatus({ code: api_1.SpanStatusCode.ERROR });
|
|
172
|
+
},
|
|
173
|
+
onSubscribeResult() {
|
|
174
|
+
return {
|
|
175
|
+
// handles async iterator
|
|
176
|
+
onNext: ({ result, setResult }) => {
|
|
177
|
+
if (options.traceIdInResult) {
|
|
178
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
179
|
+
}
|
|
180
|
+
markError(subscriptionSpan, result);
|
|
181
|
+
},
|
|
182
|
+
onEnd: () => {
|
|
183
|
+
subscriptionSpan.end();
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
if (options.resolvers) {
|
|
189
|
+
spanByContext.set(args.contextValue, subscriptionSpan);
|
|
100
190
|
}
|
|
101
191
|
return resultCbs;
|
|
102
192
|
},
|
|
103
193
|
};
|
|
104
194
|
};
|
|
105
195
|
exports.useOpenTelemetry = useOpenTelemetry;
|
|
196
|
+
function addTraceIdToResult(ctx, result, traceIdProp) {
|
|
197
|
+
return {
|
|
198
|
+
...result,
|
|
199
|
+
extensions: {
|
|
200
|
+
...result.extensions,
|
|
201
|
+
[traceIdProp]: opentelemetry.trace.getSpan(ctx)?.spanContext().traceId,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function markError(executionSpan, result) {
|
|
206
|
+
if (result.errors && result.errors.length > 0) {
|
|
207
|
+
executionSpan.setStatus({ code: opentelemetry.SpanStatusCode.ERROR });
|
|
208
|
+
executionSpan.recordException({
|
|
209
|
+
name: AttributeName.EXECUTION_ERROR,
|
|
210
|
+
message: JSON.stringify(result.errors),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BREAK, visit } from 'graphql';
|
|
2
|
+
export function hasInlineArgument(doc) {
|
|
3
|
+
let seen = false;
|
|
4
|
+
const leave = () => {
|
|
5
|
+
seen = true;
|
|
6
|
+
return BREAK;
|
|
7
|
+
};
|
|
8
|
+
visit(doc, {
|
|
9
|
+
StringValue: {
|
|
10
|
+
leave,
|
|
11
|
+
},
|
|
12
|
+
BooleanValue: {
|
|
13
|
+
leave,
|
|
14
|
+
},
|
|
15
|
+
FloatValue: {
|
|
16
|
+
leave,
|
|
17
|
+
},
|
|
18
|
+
EnumValue: {
|
|
19
|
+
leave,
|
|
20
|
+
},
|
|
21
|
+
IntValue: {
|
|
22
|
+
leave,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return seen;
|
|
26
|
+
}
|
package/esm/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { print } from 'graphql';
|
|
2
|
-
import { getDocumentString, isAsyncIterable } from '@envelop/core';
|
|
1
|
+
import { getOperationAST, print } from 'graphql';
|
|
2
|
+
import { getDocumentString, isAsyncIterable, } from '@envelop/core';
|
|
3
3
|
import { useOnResolve } from '@envelop/on-resolve';
|
|
4
|
-
import { SpanKind } from '@opentelemetry/api';
|
|
4
|
+
import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
|
|
5
5
|
import * as opentelemetry from '@opentelemetry/api';
|
|
6
6
|
import { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor, } from '@opentelemetry/sdk-trace-base';
|
|
7
|
+
import { hasInlineArgument } from './has-inline-argument.js';
|
|
7
8
|
export var AttributeName;
|
|
8
9
|
(function (AttributeName) {
|
|
9
10
|
AttributeName["EXECUTION_ERROR"] = "graphql.execute.error";
|
|
@@ -14,6 +15,7 @@ export var AttributeName;
|
|
|
14
15
|
AttributeName["RESOLVER_RESULT_TYPE"] = "graphql.resolver.resultType";
|
|
15
16
|
AttributeName["RESOLVER_ARGS"] = "graphql.resolver.args";
|
|
16
17
|
AttributeName["EXECUTION_OPERATION_NAME"] = "graphql.execute.operationName";
|
|
18
|
+
AttributeName["EXECUTION_OPERATION_TYPE"] = "graphql.execute.operationType";
|
|
17
19
|
AttributeName["EXECUTION_OPERATION_DOCUMENT"] = "graphql.execute.document";
|
|
18
20
|
AttributeName["EXECUTION_VARIABLES"] = "graphql.execute.variables";
|
|
19
21
|
})(AttributeName || (AttributeName = {}));
|
|
@@ -26,12 +28,14 @@ export const useOpenTelemetry = (options, tracingProvider, spanKind = SpanKind.S
|
|
|
26
28
|
tracingProvider = basicTraceProvider;
|
|
27
29
|
}
|
|
28
30
|
const tracer = tracingProvider.getTracer(serviceName);
|
|
31
|
+
const spanByContext = new WeakMap();
|
|
29
32
|
return {
|
|
30
33
|
onPluginInit({ addPlugin }) {
|
|
31
34
|
if (options.resolvers) {
|
|
32
35
|
addPlugin(useOnResolve(({ info, context, args }) => {
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
const parentSpan = spanByContext.get(context);
|
|
37
|
+
if (parentSpan) {
|
|
38
|
+
const ctx = opentelemetry.trace.setSpan(opentelemetry.context.active(), parentSpan);
|
|
35
39
|
const { fieldName, returnType, parentType } = info;
|
|
36
40
|
const resolverSpan = tracer.startSpan(`${parentType.name}.${fieldName}`, {
|
|
37
41
|
attributes: {
|
|
@@ -57,44 +61,148 @@ export const useOpenTelemetry = (options, tracingProvider, spanKind = SpanKind.S
|
|
|
57
61
|
}));
|
|
58
62
|
}
|
|
59
63
|
},
|
|
60
|
-
onExecute({ args
|
|
61
|
-
const
|
|
64
|
+
onExecute({ args }) {
|
|
65
|
+
const operationAst = getOperationAST(args.document, args.operationName);
|
|
66
|
+
if (!operationAst) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const operationType = operationAst.operation;
|
|
70
|
+
let isDocumentLoggable;
|
|
71
|
+
if (options.document == null || options.document === true) {
|
|
72
|
+
if (options.variables) {
|
|
73
|
+
isDocumentLoggable = true;
|
|
74
|
+
}
|
|
75
|
+
else if (!hasInlineArgument(args.document)) {
|
|
76
|
+
isDocumentLoggable = true;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
isDocumentLoggable = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
isDocumentLoggable = false;
|
|
84
|
+
}
|
|
85
|
+
const operationName = operationAst.name?.value || 'anonymous';
|
|
86
|
+
const executionSpan = tracer.startSpan(`${operationType}.${operationName}`, {
|
|
62
87
|
kind: spanKind,
|
|
63
88
|
attributes: {
|
|
64
89
|
...spanAdditionalAttributes,
|
|
65
|
-
[AttributeName.EXECUTION_OPERATION_NAME]:
|
|
66
|
-
[AttributeName.
|
|
90
|
+
[AttributeName.EXECUTION_OPERATION_NAME]: operationName,
|
|
91
|
+
[AttributeName.EXECUTION_OPERATION_TYPE]: operationType,
|
|
92
|
+
[AttributeName.EXECUTION_OPERATION_DOCUMENT]: isDocumentLoggable
|
|
93
|
+
? getDocumentString(args.document, print)
|
|
94
|
+
: undefined,
|
|
67
95
|
...(options.variables
|
|
68
96
|
? { [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(args.variableValues ?? {}) }
|
|
69
97
|
: {}),
|
|
70
98
|
},
|
|
71
99
|
});
|
|
100
|
+
const otelContext = opentelemetry.trace.setSpan(opentelemetry.context.active(), executionSpan);
|
|
72
101
|
const resultCbs = {
|
|
73
|
-
onExecuteDone({ result }) {
|
|
74
|
-
if (isAsyncIterable(result)) {
|
|
102
|
+
onExecuteDone({ result, setResult }) {
|
|
103
|
+
if (!isAsyncIterable(result)) {
|
|
104
|
+
if (result.data && options.result) {
|
|
105
|
+
executionSpan.setAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
|
|
106
|
+
}
|
|
107
|
+
if (options.traceIdInResult) {
|
|
108
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
109
|
+
}
|
|
110
|
+
markError(executionSpan, result);
|
|
75
111
|
executionSpan.end();
|
|
76
|
-
// eslint-disable-next-line no-console
|
|
77
|
-
console.warn(`Plugin "opentelemetry" encountered an AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (result.data && options.result) {
|
|
81
|
-
executionSpan.setAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
|
|
82
112
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
return {
|
|
114
|
+
// handles async iterator
|
|
115
|
+
onNext: ({ result, setResult }) => {
|
|
116
|
+
if (options.traceIdInResult) {
|
|
117
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
118
|
+
}
|
|
119
|
+
markError(executionSpan, result);
|
|
120
|
+
},
|
|
121
|
+
onEnd: () => {
|
|
122
|
+
executionSpan.end();
|
|
123
|
+
},
|
|
124
|
+
};
|
|
90
125
|
},
|
|
91
126
|
};
|
|
92
127
|
if (options.resolvers) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
spanByContext.set(args.contextValue, executionSpan);
|
|
129
|
+
}
|
|
130
|
+
return resultCbs;
|
|
131
|
+
},
|
|
132
|
+
onSubscribe({ args }) {
|
|
133
|
+
const operationAst = getOperationAST(args.document, args.operationName);
|
|
134
|
+
if (!operationAst) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const operationType = 'subscription';
|
|
138
|
+
let isDocumentLoggable;
|
|
139
|
+
if (options.variables) {
|
|
140
|
+
isDocumentLoggable = true;
|
|
141
|
+
}
|
|
142
|
+
else if (!hasInlineArgument(args.document)) {
|
|
143
|
+
isDocumentLoggable = true;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
isDocumentLoggable = false;
|
|
147
|
+
}
|
|
148
|
+
const operationName = operationAst.name?.value || 'anonymous';
|
|
149
|
+
const subscriptionSpan = tracer.startSpan(`${operationType}.${operationName}`, {
|
|
150
|
+
kind: spanKind,
|
|
151
|
+
attributes: {
|
|
152
|
+
...spanAdditionalAttributes,
|
|
153
|
+
[AttributeName.EXECUTION_OPERATION_NAME]: operationName,
|
|
154
|
+
[AttributeName.EXECUTION_OPERATION_TYPE]: operationType,
|
|
155
|
+
[AttributeName.EXECUTION_OPERATION_DOCUMENT]: isDocumentLoggable
|
|
156
|
+
? getDocumentString(args.document, print)
|
|
157
|
+
: undefined,
|
|
158
|
+
...(options.variables
|
|
159
|
+
? { [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(args.variableValues ?? {}) }
|
|
160
|
+
: {}),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const otelContext = opentelemetry.trace.setSpan(opentelemetry.context.active(), subscriptionSpan);
|
|
164
|
+
const resultCbs = {
|
|
165
|
+
onSubscribeError: ({ error }) => {
|
|
166
|
+
if (error)
|
|
167
|
+
subscriptionSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
168
|
+
},
|
|
169
|
+
onSubscribeResult() {
|
|
170
|
+
return {
|
|
171
|
+
// handles async iterator
|
|
172
|
+
onNext: ({ result, setResult }) => {
|
|
173
|
+
if (options.traceIdInResult) {
|
|
174
|
+
setResult(addTraceIdToResult(otelContext, result, options.traceIdInResult));
|
|
175
|
+
}
|
|
176
|
+
markError(subscriptionSpan, result);
|
|
177
|
+
},
|
|
178
|
+
onEnd: () => {
|
|
179
|
+
subscriptionSpan.end();
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
if (options.resolvers) {
|
|
185
|
+
spanByContext.set(args.contextValue, subscriptionSpan);
|
|
96
186
|
}
|
|
97
187
|
return resultCbs;
|
|
98
188
|
},
|
|
99
189
|
};
|
|
100
190
|
};
|
|
191
|
+
function addTraceIdToResult(ctx, result, traceIdProp) {
|
|
192
|
+
return {
|
|
193
|
+
...result,
|
|
194
|
+
extensions: {
|
|
195
|
+
...result.extensions,
|
|
196
|
+
[traceIdProp]: opentelemetry.trace.getSpan(ctx)?.spanContext().traceId,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function markError(executionSpan, result) {
|
|
201
|
+
if (result.errors && result.errors.length > 0) {
|
|
202
|
+
executionSpan.setStatus({ code: opentelemetry.SpanStatusCode.ERROR });
|
|
203
|
+
executionSpan.recordException({
|
|
204
|
+
name: AttributeName.EXECUTION_ERROR,
|
|
205
|
+
message: JSON.stringify(result.errors),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
package/package.json
CHANGED
package/typings/index.d.cts
CHANGED
|
@@ -10,14 +10,17 @@ export declare enum AttributeName {
|
|
|
10
10
|
RESOLVER_RESULT_TYPE = "graphql.resolver.resultType",
|
|
11
11
|
RESOLVER_ARGS = "graphql.resolver.args",
|
|
12
12
|
EXECUTION_OPERATION_NAME = "graphql.execute.operationName",
|
|
13
|
+
EXECUTION_OPERATION_TYPE = "graphql.execute.operationType",
|
|
13
14
|
EXECUTION_OPERATION_DOCUMENT = "graphql.execute.document",
|
|
14
15
|
EXECUTION_VARIABLES = "graphql.execute.variables"
|
|
15
16
|
}
|
|
16
17
|
declare const tracingSpanSymbol: unique symbol;
|
|
17
18
|
export type TracingOptions = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
document?: boolean;
|
|
20
|
+
resolvers?: boolean;
|
|
21
|
+
variables?: boolean;
|
|
22
|
+
result?: boolean;
|
|
23
|
+
traceIdInResult?: string;
|
|
21
24
|
};
|
|
22
25
|
type PluginContext = {
|
|
23
26
|
[tracingSpanSymbol]: opentelemetry.Span;
|
package/typings/index.d.ts
CHANGED
|
@@ -10,14 +10,17 @@ export declare enum AttributeName {
|
|
|
10
10
|
RESOLVER_RESULT_TYPE = "graphql.resolver.resultType",
|
|
11
11
|
RESOLVER_ARGS = "graphql.resolver.args",
|
|
12
12
|
EXECUTION_OPERATION_NAME = "graphql.execute.operationName",
|
|
13
|
+
EXECUTION_OPERATION_TYPE = "graphql.execute.operationType",
|
|
13
14
|
EXECUTION_OPERATION_DOCUMENT = "graphql.execute.document",
|
|
14
15
|
EXECUTION_VARIABLES = "graphql.execute.variables"
|
|
15
16
|
}
|
|
16
17
|
declare const tracingSpanSymbol: unique symbol;
|
|
17
18
|
export type TracingOptions = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
document?: boolean;
|
|
20
|
+
resolvers?: boolean;
|
|
21
|
+
variables?: boolean;
|
|
22
|
+
result?: boolean;
|
|
23
|
+
traceIdInResult?: string;
|
|
21
24
|
};
|
|
22
25
|
type PluginContext = {
|
|
23
26
|
[tracingSpanSymbol]: opentelemetry.Span;
|