diff --git a/packages/plugins/opentelemetry/README.md b/packages/plugins/opentelemetry/README.md index 24ba30a9d1..1333ed77b7 100644 --- a/packages/plugins/opentelemetry/README.md +++ b/packages/plugins/opentelemetry/README.md @@ -1,70 +1 @@ -## `@envelop/opentelemetry` - -This plugins integrates [Open Telemetry](https://opentelemetry.io/) tracing with your GraphQL -execution. It also collects GraphQL execution errors and reports it as Exceptions. - -You can use this plugin with any kind of Open Telemetry -[tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer), -and integrate it to any tracing/metric platform that supports this standard. - -## Getting Started - -``` -yarn add @envelop/opentelemetry -``` - -## Usage Example - -By default, this plugin prints the collected telemetry to the console: - -```ts -import { execute, parse, specifiedRules, subscribe, validate } from 'graphql' -import { envelop, useEngine } from '@envelop/core' -import { useOpenTelemetry } from '@envelop/opentelemetry' - -const getEnveloped = envelop({ - plugins: [ - useEngine({ parse, validate, specifiedRules, execute, subscribe }), - // ... other plugins ... - useOpenTelemetry({ - resolvers: true, // Tracks resolvers calls, and tracks resolvers thrown errors - variables: true, // Includes the operation variables values as part of the metadata collected - result: true // Includes execution result object as part of the metadata collected - }) - ] -}) -``` - -If you wish to use custom tracer/exporter, create it and pass it. This example integrates Jaeger -tracer: - -```ts -import { execute, parse, specifiedRules, subscribe, validate } from 'graphql' -import { envelop, useEngine } from '@envelop/core' -import { useOpenTelemetry } from '@envelop/opentelemetry' -import { JaegerExporter } from '@opentelemetry/exporter-jaeger' -import { BasicTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base' - -const exporter = new JaegerExporter({ - serviceName: 'my-service-name' -}) - -const provider = new BasicTracerProvider() -provider.addSpanProcessor(new SimpleSpanProcessor(exporter)) -provider.register() - -const getEnveloped = envelop({ - plugins: [ - useEngine({ parse, validate, specifiedRules, execute, subscribe }), - // ... other plugins ... - useOpenTelemetry( - { - resolvers: true, // Tracks resolvers calls, and tracks resolvers thrown errors - variables: true, // Includes the operation variables values as part of the metadata collected - result: true // Includes execution result object as part of the metadata collected - }, - provider - ) - ] -}) -``` +TODO diff --git a/packages/plugins/opentelemetry/package.json b/packages/plugins/opentelemetry/package.json index 34c99bf06e..251b70a773 100644 --- a/packages/plugins/opentelemetry/package.json +++ b/packages/plugins/opentelemetry/package.json @@ -49,12 +49,20 @@ }, "dependencies": { "@envelop/on-resolve": "^2.0.6", - "@opentelemetry/api": "^1.0.0", - "@opentelemetry/sdk-trace-base": "^1.11.0", + "@opentelemetry/core": "^1.7.0", + "graphql-otel": "^0.0.9", "tslib": "^2.5.0" }, "devDependencies": { "@envelop/core": "^3.0.6", + "@graphql-tools/schema": "8.5.1", + "@opentelemetry/api": "^1.2.0", + "@opentelemetry/context-async-hooks": "^1.7.0", + "@opentelemetry/instrumentation": "^0.33.0", + "@opentelemetry/otlp-exporter-base": "^0.33.0", + "@opentelemetry/resources": "^1.7.0", + "@opentelemetry/sdk-trace-base": "^1.7.0", + "@opentelemetry/semantic-conventions": "^1.12.0", "graphql": "16.6.0", "typescript": "4.8.4" }, diff --git a/packages/plugins/opentelemetry/src/index.ts b/packages/plugins/opentelemetry/src/index.ts index bf0fad9b4e..1b845b3ace 100644 --- a/packages/plugins/opentelemetry/src/index.ts +++ b/packages/plugins/opentelemetry/src/index.ts @@ -1,143 +1,34 @@ -import { print } from 'graphql'; -import { isAsyncIterable, OnExecuteHookResult, Plugin } from '@envelop/core'; -import { useOnResolve } from '@envelop/on-resolve'; -import { SpanAttributes, SpanKind, TracerProvider } from '@opentelemetry/api'; +import { GraphQLOTELContext, traceDirective } from 'graphql-otel'; +import { Plugin } from '@envelop/core'; import * as opentelemetry from '@opentelemetry/api'; -import { - BasicTracerProvider, - ConsoleSpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/sdk-trace-base'; - -export enum AttributeName { - EXECUTION_ERROR = 'graphql.execute.error', - EXECUTION_RESULT = 'graphql.execute.result', - RESOLVER_EXCEPTION = 'graphql.resolver.exception', - RESOLVER_FIELD_NAME = 'graphql.resolver.fieldName', - RESOLVER_TYPE_NAME = 'graphql.resolver.typeName', - RESOLVER_RESULT_TYPE = 'graphql.resolver.resultType', - RESOLVER_ARGS = 'graphql.resolver.args', - EXECUTION_OPERATION_NAME = 'graphql.execute.operationName', - EXECUTION_OPERATION_DOCUMENT = 'graphql.execute.document', - EXECUTION_VARIABLES = 'graphql.execute.variables', -} const tracingSpanSymbol = Symbol('OPEN_TELEMETRY_GRAPHQL'); -export type TracingOptions = { - resolvers: boolean; - variables: boolean; - result: boolean; -}; - type PluginContext = { [tracingSpanSymbol]: opentelemetry.Span; }; -export const useOpenTelemetry = ( - options: TracingOptions, - tracingProvider?: TracerProvider, - spanKind: SpanKind = SpanKind.SERVER, - spanAdditionalAttributes: SpanAttributes = {}, - serviceName = 'graphql', -): Plugin => { - if (!tracingProvider) { - const basicTraceProvider = new BasicTracerProvider(); - basicTraceProvider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); - basicTraceProvider.register(); - tracingProvider = basicTraceProvider; - } - - const tracer = tracingProvider.getTracer(serviceName); +const graphqlMiddlewareAppliedTransformSymbol = Symbol('graphqlMiddleware.appliedTransform'); +export const useOpenTelemetry = (): Plugin => { return { - onPluginInit({ addPlugin }) { - if (options.resolvers) { - addPlugin( - useOnResolve(({ info, context, args }) => { - if (context && typeof context === 'object' && context[tracingSpanSymbol]) { - const ctx = opentelemetry.trace.setSpan( - opentelemetry.context.active(), - context[tracingSpanSymbol], - ); - const { fieldName, returnType, parentType } = info; - - const resolverSpan = tracer.startSpan( - `${parentType.name}.${fieldName}`, - { - attributes: { - [AttributeName.RESOLVER_FIELD_NAME]: fieldName, - [AttributeName.RESOLVER_TYPE_NAME]: parentType.toString(), - [AttributeName.RESOLVER_RESULT_TYPE]: returnType.toString(), - [AttributeName.RESOLVER_ARGS]: JSON.stringify(args || {}), - }, - }, - ctx, - ); - - return ({ result }) => { - if (result instanceof Error) { - resolverSpan.recordException({ - name: AttributeName.RESOLVER_EXCEPTION, - message: JSON.stringify(result), - }); - } else { - resolverSpan.end(); - } - }; - } - - return () => {}; - }), - ); - } - }, - onExecute({ args, extendContext }) { - const executionSpan = tracer.startSpan(`${args.operationName || 'Anonymous Operation'}`, { - kind: spanKind, - attributes: { - ...spanAdditionalAttributes, - [AttributeName.EXECUTION_OPERATION_NAME]: args.operationName ?? undefined, - [AttributeName.EXECUTION_OPERATION_DOCUMENT]: print(args.document), - ...(options.variables - ? { [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(args.variableValues ?? {}) } - : {}), - }, + onContextBuilding({ extendContext }) { + extendContext({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - dafuck + GraphQLOTELContext: new GraphQLOTELContext(), }); + }, + onSchemaChange({ schema, replaceSchema }) { + if (schema.extensions?.[graphqlMiddlewareAppliedTransformSymbol]) { + return; + } - const resultCbs: OnExecuteHookResult = { - onExecuteDone({ result }) { - if (isAsyncIterable(result)) { - executionSpan.end(); - // eslint-disable-next-line no-console - console.warn( - `Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`, - ); - return; - } - - if (result.data && options.result) { - executionSpan.setAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result)); - } - - if (result.errors && result.errors.length > 0) { - executionSpan.recordException({ - name: AttributeName.EXECUTION_ERROR, - message: JSON.stringify(result.errors), - }); - } - - executionSpan.end(); - }, - }; + const directive = traceDirective('trace'); - if (options.resolvers) { - extendContext({ - [tracingSpanSymbol]: executionSpan, - }); - } + const transformedSchema = directive.transformer(schema); - return resultCbs; + replaceSchema(transformedSchema); }, }; }; diff --git a/packages/plugins/opentelemetry/test/use-open-telemetry.spec.ts b/packages/plugins/opentelemetry/test/use-open-telemetry.spec.ts index 58fb38344b..247c4c3696 100644 --- a/packages/plugins/opentelemetry/test/use-open-telemetry.spec.ts +++ b/packages/plugins/opentelemetry/test/use-open-telemetry.spec.ts @@ -1,81 +1,121 @@ -import { buildSchema } from 'graphql'; -import { assertSingleExecutionValue, createTestkit } from '@envelop/testing'; -import { - BasicTracerProvider, - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/sdk-trace-base'; +import { traceDirective } from 'graphql-otel'; +import { createTestkit } from '@envelop/testing'; +import { makeExecutableSchema } from '@graphql-tools/schema'; +import { InMemorySpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base'; import { useOpenTelemetry } from '../src/index.js'; +import { buildSpanTree, cleanSpanTreeForSnapshot, otelSetup } from './utils'; -function createTraceProvider(exporter: InMemorySpanExporter) { - const provider = new BasicTracerProvider(); - const processor = new SimpleSpanProcessor(exporter); - provider.addSpanProcessor(processor); - provider.register(); - return provider; -} +const inMemorySpanExporter = otelSetup() as InMemorySpanExporter; describe('useOpenTelemetry', () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - ping: String + beforeEach(() => { + inMemorySpanExporter.reset(); + }); + + const trace = traceDirective('trace'); + + const typeDefs = /* GraphQL */ ` + ${trace.typeDefs} + + type User { + name: String + posts: [Post] @trace } - `); - const query = /* GraphQL */ ` - query { - ping + + type Post { + title: String + comments: [Comment] @trace } - `; - const useTestOpenTelemetry = (exporter?: InMemorySpanExporter, options?: any) => - useOpenTelemetry( - { - resolvers: false, - result: false, - variables: false, - ...(options ?? {}), - }, - exporter ? createTraceProvider(exporter) : undefined, - ); + type Comment { + text: String + } - it('Should override execute function', async () => { - const onExecuteSpy = jest.fn(); - const testInstance = createTestkit( - [ - useTestOpenTelemetry(), - { - onExecute: onExecuteSpy, - }, + type Query { + users: [User] @trace + } + `; + + const resolvers = { + Query: { + users: () => [ + { name: 'foobar', posts: [{ title: 'foobar', comments: [{ text: 'foobar' }] }] }, ], - schema, - ); + }, + }; - const result = await testInstance.execute(query); - assertSingleExecutionValue(result); - expect(onExecuteSpy).toHaveBeenCalledTimes(1); - }); + const schema = makeExecutableSchema({ typeDefs, resolvers }); + + const query = /* GraphQL */ ` + query { + users { + name + posts { + title + comments { + text + } + } + } + } + `; - it('Should add execution span', async () => { - const exporter = new InMemorySpanExporter(); - const testInstance = createTestkit([useTestOpenTelemetry(exporter)], schema); + it('Should wrap the traced fields in a span and assert the tree', async () => { + const testInstance = createTestkit([useOpenTelemetry()], schema); await testInstance.execute(query); - const actual = exporter.getFinishedSpans(); - expect(actual.length).toBe(1); - expect(actual[0].name).toBe('Anonymous Operation'); - }); + const spans = inMemorySpanExporter.getFinishedSpans(); + const rootSpan = spans.find(span => !span.parentSpanId) as ReadableSpan; + const spanTree = buildSpanTree({ span: rootSpan, children: [] }, spans); - it('Should add resolver span if requested', async () => { - const exporter = new InMemorySpanExporter(); - const testInstance = createTestkit( - [useTestOpenTelemetry(exporter, { resolvers: true })], - schema, - ); + const cleanTree = cleanSpanTreeForSnapshot(spanTree); - await testInstance.execute(query); - const actual = exporter.getFinishedSpans(); - expect(actual.length).toBe(2); - expect(actual[0].name).toBe('Query.ping'); - expect(actual[1].name).toBe('Anonymous Operation'); + expect(JSON.stringify(cleanTree, null, 2)).toMatchInlineSnapshot(` + "{ + \\"span\\": { + \\"attributes\\": { + \\"query\\": \\"{\\\\n users {\\\\n name\\\\n posts {\\\\n title\\\\n comments {\\\\n text\\\\n }\\\\n }\\\\n }\\\\n}\\" + }, + \\"links\\": [], + \\"events\\": [], + \\"status\\": { + \\"code\\": 0 + }, + \\"name\\": \\"Query:users\\", + \\"kind\\": 0 + }, + \\"children\\": [ + { + \\"span\\": { + \\"attributes\\": {}, + \\"links\\": [], + \\"events\\": [], + \\"status\\": { + \\"code\\": 0 + }, + \\"name\\": \\"User:posts\\", + \\"parentSpanId\\": \\"\\", + \\"kind\\": 0 + }, + \\"children\\": [ + { + \\"span\\": { + \\"attributes\\": {}, + \\"links\\": [], + \\"events\\": [], + \\"status\\": { + \\"code\\": 0 + }, + \\"name\\": \\"Post:comments\\", + \\"parentSpanId\\": \\"\\", + \\"kind\\": 0 + }, + \\"children\\": [] + } + ] + } + ] + }" + `); }); }); diff --git a/packages/plugins/opentelemetry/test/utils.ts b/packages/plugins/opentelemetry/test/utils.ts new file mode 100644 index 0000000000..feed7d9b61 --- /dev/null +++ b/packages/plugins/opentelemetry/test/utils.ts @@ -0,0 +1,92 @@ +import * as api from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { Resource } from '@opentelemetry/resources'; +import { + BasicTracerProvider, + InMemorySpanExporter, + ReadableSpan, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-base'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +type Tree = { + span: ReadableSpan; + children?: Tree[]; +}; + +export function otelSetup(): InMemorySpanExporter { + const exporter = new InMemorySpanExporter(); + + const contextManager = new AsyncHooksContextManager().enable(); + + api.context.setGlobalContextManager(contextManager); + + const provider = new BasicTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: 'graphql-otel', + [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', + }), + }); + + provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); + + provider.register(); + + return exporter; +} + +export type SpanTree = { + span: ReadableSpan; + children: SpanTree[]; +}; + +export function buildSpanTree(tree: SpanTree, spans: ReadableSpan[]): SpanTree { + const childrenSpans = spans.filter(span => span.parentSpanId === tree.span.spanContext().spanId); + + if (childrenSpans.length) { + tree.children = childrenSpans.map(span => buildSpanTree({ span, children: [] }, spans)); + } else { + tree.children = []; + } + + const simpleTree = JSON.stringify( + tree, + (key, value) => { + const removedKeys = [ + 'endTime', + 'startTime', + '_spanLimits', + 'instrumentationLibrary', + '_spanProcessor', + '_attributeValueLengthLimit', + '_duration', + ]; + + if (removedKeys.includes(key)) { + return undefined; + } else { + return value; + } + }, + 2, + ); + + return JSON.parse(simpleTree); +} + +export function cleanSpanTreeForSnapshot(tree: Tree) { + return JSON.parse(JSON.stringify(tree), (key, value) => { + if (key[0] === '_') return undefined; + if (key === 'parentSpanId') return ''; + if (key === 'itx_id') return ''; + if (key === 'endTime') return ''; + if (key === 'startTime') return ''; + if (key === 'db.type') return ''; + if (key === 'db.statement') return ''; + if (key === 'resource') return undefined; + if (key === 'spanId') return ''; + if (key === 'traceId') return ''; + + return value; + }); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6076cb0e8..b3f9e94475 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1136,12 +1136,12 @@ importers: '@envelop/on-resolve': specifier: ^2.0.6 version: link:../on-resolve - '@opentelemetry/api': - specifier: ^1.0.0 - version: 1.0.3 - '@opentelemetry/sdk-trace-base': - specifier: ^1.11.0 - version: 1.11.0(@opentelemetry/api@1.0.3) + '@opentelemetry/core': + specifier: ^1.7.0 + version: 1.12.0(@opentelemetry/api@1.4.1) + graphql-otel: + specifier: ^0.0.9 + version: 0.0.9 tslib: specifier: ^2.5.0 version: 2.5.0 @@ -1149,6 +1149,30 @@ importers: '@envelop/core': specifier: ^3.0.6 version: link:../../core + '@graphql-tools/schema': + specifier: 8.5.1 + version: 8.5.1(graphql@16.6.0) + '@opentelemetry/api': + specifier: ^1.2.0 + version: 1.4.1 + '@opentelemetry/context-async-hooks': + specifier: ^1.7.0 + version: 1.12.0(@opentelemetry/api@1.4.1) + '@opentelemetry/instrumentation': + specifier: ^0.33.0 + version: 0.33.0(@opentelemetry/api@1.4.1) + '@opentelemetry/otlp-exporter-base': + specifier: ^0.33.0 + version: 0.33.0(@opentelemetry/api@1.4.1) + '@opentelemetry/resources': + specifier: ^1.7.0 + version: 1.12.0(@opentelemetry/api@1.4.1) + '@opentelemetry/sdk-trace-base': + specifier: ^1.7.0 + version: 1.11.0(@opentelemetry/api@1.4.1) + '@opentelemetry/semantic-conventions': + specifier: ^1.12.0 + version: 1.12.0 graphql: specifier: 16.6.0 version: 16.6.0 @@ -5881,14 +5905,43 @@ packages: rimraf: 3.0.2 dev: true + /@opentelemetry/api-metrics@0.33.0: + resolution: + { + integrity: sha512-78evfPRRRnJA6uZ3xuBuS3VZlXTO/LRs+Ff1iv3O/7DgibCtq9k27T6Zlj8yRdJDFmcjcbQrvC0/CpDpWHaZYA==, + } + engines: { node: '>=14' } + dependencies: + '@opentelemetry/api': 1.4.1 + /@opentelemetry/api@1.0.3: resolution: { integrity: sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==, } engines: { node: '>=8.0.0' } + dev: true + + /@opentelemetry/api@1.4.1: + resolution: + { + integrity: sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==, + } + engines: { node: '>=8.0.0' } + + /@opentelemetry/context-async-hooks@1.12.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-PmwAanPNWCyS9JYFzhzVzHgviLhc0UHjOwdth+hp3HgQQ9XZZNE635P8JhAUHZmbghW9/qQFafRWOS4VN9VVnQ==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.5.0' + dependencies: + '@opentelemetry/api': 1.4.1 + dev: true - /@opentelemetry/core@1.11.0(@opentelemetry/api@1.0.3): + /@opentelemetry/core@1.11.0(@opentelemetry/api@1.4.1): resolution: { integrity: sha512-aP1wHSb+YfU0pM63UAkizYPuS4lZxzavHHw5KJfFNN2oWQ79HSm6JR3CzwFKHwKhSzHN8RE9fgP1IdVJ8zmo1w==, @@ -5897,11 +5950,65 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.5.0' dependencies: - '@opentelemetry/api': 1.0.3 + '@opentelemetry/api': 1.4.1 '@opentelemetry/semantic-conventions': 1.11.0 - dev: false - /@opentelemetry/resources@1.11.0(@opentelemetry/api@1.0.3): + /@opentelemetry/core@1.12.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-4DWYNb3dLs2mSCGl65jY3aEgbvPWSHVQV/dmDWiYeWUrMakZQFcymqZOSUNZO0uDrEJoxMu8O5tZktX6UKFwag==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.5.0' + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/semantic-conventions': 1.12.0 + + /@opentelemetry/core@1.7.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.3.0' + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/semantic-conventions': 1.7.0 + dev: true + + /@opentelemetry/instrumentation@0.33.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-8joPjKJ6TznNt04JbnzZG+m1j/4wm1OIrX7DEw/V5lyZ9/2fahIqG72jeZ26VKOZnLOpVzUUnU/dweURqBzT3Q==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/api-metrics': 0.33.0 + require-in-the-middle: 5.2.0 + semver: 7.3.8 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + /@opentelemetry/otlp-exporter-base@0.33.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-st+nsgv23BXSARFwugy6pheulDfOKjIFvzoYOUzPQDVhQtU8+l7dc50rIEybwXghb13o7mZs6Nb8KOvRk57qww==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/core': 1.7.0(@opentelemetry/api@1.4.1) + dev: true + + /@opentelemetry/resources@1.11.0(@opentelemetry/api@1.4.1): resolution: { integrity: sha512-y0z2YJTqk0ag+hGT4EXbxH/qPhDe8PfwltYb4tXIEsozgEFfut/bqW7H7pDvylmCjBRMG4NjtLp57V1Ev++brA==, @@ -5910,12 +6017,25 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.5.0' dependencies: - '@opentelemetry/api': 1.0.3 - '@opentelemetry/core': 1.11.0(@opentelemetry/api@1.0.3) + '@opentelemetry/api': 1.4.1 + '@opentelemetry/core': 1.11.0(@opentelemetry/api@1.4.1) '@opentelemetry/semantic-conventions': 1.11.0 - dev: false - /@opentelemetry/sdk-trace-base@1.11.0(@opentelemetry/api@1.0.3): + /@opentelemetry/resources@1.12.0(@opentelemetry/api@1.4.1): + resolution: + { + integrity: sha512-gunMKXG0hJrR0LXrqh7BVbziA/+iJBL3ZbXCXO64uY+SrExkwoyJkpiq9l5ismkGF/A20mDEV7tGwh+KyPw00Q==, + } + engines: { node: '>=14' } + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.5.0' + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/core': 1.12.0(@opentelemetry/api@1.4.1) + '@opentelemetry/semantic-conventions': 1.12.0 + dev: true + + /@opentelemetry/sdk-trace-base@1.11.0(@opentelemetry/api@1.4.1): resolution: { integrity: sha512-DV8e5/Qo42V8FMBlQ0Y0Liv6Hl/Pp5bAZ73s7r1euX8w4bpRes1B7ACiA4yujADbWMJxBgSo4fGbi4yjmTMG2A==, @@ -5924,11 +6044,10 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.5.0' dependencies: - '@opentelemetry/api': 1.0.3 - '@opentelemetry/core': 1.11.0(@opentelemetry/api@1.0.3) - '@opentelemetry/resources': 1.11.0(@opentelemetry/api@1.0.3) + '@opentelemetry/api': 1.4.1 + '@opentelemetry/core': 1.11.0(@opentelemetry/api@1.4.1) + '@opentelemetry/resources': 1.11.0(@opentelemetry/api@1.4.1) '@opentelemetry/semantic-conventions': 1.11.0 - dev: false /@opentelemetry/semantic-conventions@1.11.0: resolution: @@ -5936,7 +6055,21 @@ packages: integrity: sha512-fG4D0AktoHyHwGhFGv+PzKrZjxbKJfckJauTJdq2A+ej5cTazmNYjJVAODXXkYyrsI10muMl+B1iO2q1R6Lp+w==, } engines: { node: '>=14' } - dev: false + + /@opentelemetry/semantic-conventions@1.12.0: + resolution: + { + integrity: sha512-hO+bdeGOlJwqowUBoZF5LyP3ORUFOP1G0GRv8N45W/cztXbT2ZEXaAzfokRS9Xc9FWmYrDj32mF6SzH6wuoIyA==, + } + engines: { node: '>=14' } + + /@opentelemetry/semantic-conventions@1.7.0: + resolution: + { + integrity: sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA==, + } + engines: { node: '>=14' } + dev: true /@panva/asn1.js@1.0.0: resolution: @@ -13094,6 +13227,24 @@ packages: ramda: 0.28.0 dev: true + /graphql-otel@0.0.9: + resolution: + { + integrity: sha512-Lj4kShVInTcdtH3956r0qLyavJwxqe0iN5u/Rw/egvHRZSk3uxctW28PitCk8pf08lfsjM/AvDKrTRKDml8U+Q==, + } + engines: { node: '>=16.0.0' } + dependencies: + '@graphql-tools/utils': 8.13.1(graphql@16.6.0) + '@opentelemetry/api': 1.4.1 + '@opentelemetry/core': 1.12.0(@opentelemetry/api@1.4.1) + '@opentelemetry/instrumentation': 0.33.0(@opentelemetry/api@1.4.1) + '@opentelemetry/sdk-trace-base': 1.11.0(@opentelemetry/api@1.4.1) + graphql: 16.6.0 + safe-json-stringify: 1.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /graphql-query-complexity@0.7.2(graphql@16.6.0): resolution: { @@ -13977,7 +14128,6 @@ packages: } dependencies: has: 1.0.3 - dev: true /is-date-object@1.0.4: resolution: @@ -17140,6 +17290,12 @@ packages: hasBin: true dev: true + /module-details-from-path@1.0.3: + resolution: + { + integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==, + } + /morgan@1.6.1: resolution: { @@ -18126,7 +18282,6 @@ packages: { integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, } - dev: true /path-to-regexp@0.1.7: resolution: @@ -19738,6 +19893,19 @@ packages: } engines: { node: '>=0.10.0' } + /require-in-the-middle@5.2.0: + resolution: + { + integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==, + } + engines: { node: '>=6' } + dependencies: + debug: 4.3.4 + module-details-from-path: 1.0.3 + resolve: 1.22.1 + transitivePeerDependencies: + - supports-color + /require-main-filename@2.0.0: resolution: { @@ -19815,7 +19983,6 @@ packages: is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /response-time@2.3.2: resolution: @@ -19982,6 +20149,13 @@ packages: integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, } + /safe-json-stringify@1.2.0: + resolution: + { + integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==, + } + dev: false + /safe-regex-test@1.0.0: resolution: { @@ -20360,6 +20534,12 @@ packages: vscode-textmate: 8.0.0 dev: false + /shimmer@1.2.1: + resolution: + { + integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==, + } + /side-channel@1.0.4: resolution: { @@ -21107,7 +21287,6 @@ packages: integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, } engines: { node: '>= 0.4' } - dev: true /svgo@2.8.0: resolution: