UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

319 lines (315 loc) 10.6 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const api = require('@opentelemetry/api'); const instrumentation = require('@opentelemetry/instrumentation'); const _enum = require('./enum.js'); const AttributeNames = require('./enums/AttributeNames.js'); const symbols = require('./symbols.js'); const internalTypes = require('./internal-types.js'); const utils = require('./utils.js'); const core = require('@sentry/core'); const PACKAGE_NAME = "@sentry/instrumentation-graphql"; const DEFAULT_CONFIG = { mergeItems: false, depth: -1, allowValues: false, ignoreResolveSpans: false }; const supportedVersions = [">=14.0.0 <17"]; class GraphQLInstrumentation extends instrumentation.InstrumentationBase { constructor(config = {}) { super(PACKAGE_NAME, core.SDK_VERSION, { ...DEFAULT_CONFIG, ...config }); } setConfig(config = {}) { super.setConfig({ ...DEFAULT_CONFIG, ...config }); } init() { const module = new instrumentation.InstrumentationNodeModuleDefinition("graphql", supportedVersions); module.files.push(this._addPatchingExecute()); module.files.push(this._addPatchingParser()); module.files.push(this._addPatchingValidate()); return module; } _addPatchingExecute() { return new instrumentation.InstrumentationNodeModuleFile( "graphql/execution/execute.js", supportedVersions, // cannot make it work with appropriate type as execute function has 2 //types and/cannot import function but only types (moduleExports) => { if (instrumentation.isWrapped(moduleExports.execute)) { this._unwrap(moduleExports, "execute"); } this._wrap(moduleExports, "execute", this._patchExecute(moduleExports.defaultFieldResolver)); return moduleExports; }, (moduleExports) => { if (moduleExports) { this._unwrap(moduleExports, "execute"); } } ); } _addPatchingParser() { return new instrumentation.InstrumentationNodeModuleFile( "graphql/language/parser.js", supportedVersions, (moduleExports) => { if (instrumentation.isWrapped(moduleExports.parse)) { this._unwrap(moduleExports, "parse"); } this._wrap(moduleExports, "parse", this._patchParse()); return moduleExports; }, (moduleExports) => { if (moduleExports) { this._unwrap(moduleExports, "parse"); } } ); } _addPatchingValidate() { return new instrumentation.InstrumentationNodeModuleFile( "graphql/validation/validate.js", supportedVersions, (moduleExports) => { if (instrumentation.isWrapped(moduleExports.validate)) { this._unwrap(moduleExports, "validate"); } this._wrap(moduleExports, "validate", this._patchValidate()); return moduleExports; }, (moduleExports) => { if (moduleExports) { this._unwrap(moduleExports, "validate"); } } ); } _patchExecute(defaultFieldResolved) { const instrumentation$1 = this; return function execute(original) { return function patchExecute() { let processedArgs; if (arguments.length >= 2) { const args = arguments; processedArgs = instrumentation$1._wrapExecuteArgs( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], defaultFieldResolved ); } else { const args = arguments[0]; processedArgs = instrumentation$1._wrapExecuteArgs( args.schema, args.document, args.rootValue, args.contextValue, args.variableValues, args.operationName, args.fieldResolver, args.typeResolver, defaultFieldResolved ); } const operation = utils.getOperation(processedArgs.document, processedArgs.operationName); const span = instrumentation$1._createExecuteSpan(operation, processedArgs); processedArgs.contextValue[symbols.OTEL_GRAPHQL_DATA_SYMBOL] = { source: processedArgs.document ? processedArgs.document || processedArgs.document[symbols.OTEL_GRAPHQL_DATA_SYMBOL] : void 0, span, fields: {} }; return api.context.with(api.trace.setSpan(api.context.active(), span), () => { return instrumentation.safeExecuteInTheMiddle( () => { return original.apply(this, [processedArgs]); }, (err, result) => { instrumentation$1._handleExecutionResult(span, err, result); } ); }); }; }; } _handleExecutionResult(span, err, result) { const config = this.getConfig(); if (result === void 0 || err) { utils.endSpan(span, err); return; } if (utils.isPromise(result)) { result.then( (resultData) => { if (typeof config.responseHook !== "function") { utils.endSpan(span); return; } this._executeResponseHook(span, resultData); }, (error) => { utils.endSpan(span, error); } ); } else { if (typeof config.responseHook !== "function") { utils.endSpan(span); return; } this._executeResponseHook(span, result); } } _executeResponseHook(span, result) { const { responseHook } = this.getConfig(); if (!responseHook) { return; } instrumentation.safeExecuteInTheMiddle( () => { responseHook(span, result); }, (err) => { if (err) { this._diag.error("Error running response hook", err); } utils.endSpan(span, void 0); }, true ); } _patchParse() { const instrumentation = this; return function parse(original) { return function patchParse(source, options) { return instrumentation._parse(this, original, source, options); }; }; } _patchValidate() { const instrumentation = this; return function validate(original) { return function patchValidate(schema, documentAST, rules, options, typeInfo) { return instrumentation._validate(this, original, schema, documentAST, rules, typeInfo, options); }; }; } _parse(obj, original, source, options) { const config = this.getConfig(); const span = this.tracer.startSpan(_enum.SpanNames.PARSE); return api.context.with(api.trace.setSpan(api.context.active(), span), () => { return instrumentation.safeExecuteInTheMiddle( () => { return original.call(obj, source, options); }, (err, result) => { if (result) { const operation = utils.getOperation(result); if (!operation) { span.updateName(_enum.SpanNames.SCHEMA_PARSE); } else if (result.loc) { utils.addSpanSource(span, result.loc, config.allowValues); } } utils.endSpan(span, err); } ); }); } _validate(obj, original, schema, documentAST, rules, typeInfo, options) { const span = this.tracer.startSpan(_enum.SpanNames.VALIDATE, {}); return api.context.with(api.trace.setSpan(api.context.active(), span), () => { return instrumentation.safeExecuteInTheMiddle( () => { return original.call(obj, schema, documentAST, rules, options, typeInfo); }, (err, errors) => { if (!documentAST.loc) { span.updateName(_enum.SpanNames.SCHEMA_VALIDATE); } if (errors && errors.length) { span.recordException({ name: AttributeNames.AttributeNames.ERROR_VALIDATION_NAME, message: JSON.stringify(errors) }); } utils.endSpan(span, err); } ); }); } _createExecuteSpan(operation, processedArgs) { const config = this.getConfig(); const span = this.tracer.startSpan(_enum.SpanNames.EXECUTE, {}); if (operation) { const { operation: operationType, name: nameNode } = operation; span.setAttribute(AttributeNames.AttributeNames.OPERATION_TYPE, operationType); const operationName = nameNode?.value; if (operationName) { span.setAttribute(AttributeNames.AttributeNames.OPERATION_NAME, operationName); span.updateName(`${operationType} ${operationName}`); } else { span.updateName(operationType); } } else { let operationName = " "; if (processedArgs.operationName) { operationName = ` "${processedArgs.operationName}" `; } operationName = internalTypes.OPERATION_NOT_SUPPORTED.replace("$operationName$", operationName); span.setAttribute(AttributeNames.AttributeNames.OPERATION_NAME, operationName); } if (processedArgs.document?.loc) { utils.addSpanSource(span, processedArgs.document.loc, config.allowValues); } if (processedArgs.variableValues && config.allowValues) { utils.addInputVariableAttributes(span, processedArgs.variableValues); } return span; } _wrapExecuteArgs(schema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver, defaultFieldResolved) { if (!contextValue) { contextValue = {}; } if (contextValue[symbols.OTEL_GRAPHQL_DATA_SYMBOL] || this.getConfig().ignoreResolveSpans) { return { schema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver }; } const isUsingDefaultResolver = fieldResolver == null; const fieldResolverForExecute = fieldResolver ?? defaultFieldResolved; fieldResolver = utils.wrapFieldResolver( this.tracer, () => this.getConfig(), fieldResolverForExecute, isUsingDefaultResolver ); if (schema) { utils.wrapFields(schema.getQueryType(), this.tracer, () => this.getConfig()); utils.wrapFields(schema.getMutationType(), this.tracer, () => this.getConfig()); } return { schema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver }; } } exports.GraphQLInstrumentation = GraphQLInstrumentation; //# sourceMappingURL=instrumentation.js.map