UNPKG

@graphql-mesh/plugin-newrelic

Version:
130 lines (125 loc) 6.91 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } const newrelic = require('@envelop/newrelic'); const stringInterpolation = require('@graphql-mesh/string-interpolation'); const crossHelpers = require('@graphql-mesh/cross-helpers'); const recordExternal = _interopDefault(require('newrelic/lib/metrics/recorders/http_external')); const NAMES = _interopDefault(require('newrelic/lib/metrics/names')); const cat = _interopDefault(require('newrelic/lib/util/cat')); const utils = require('@graphql-mesh/utils'); const newrelic$1 = require('newrelic'); const EnvelopAttributeName = 'Envelop_NewRelic_Plugin'; function useMeshNewrelic(options) { if (!(newrelic$1.shim === null || newrelic$1.shim === void 0 ? void 0 : newrelic$1.shim.agent)) { options.logger.error('Agent unavailable. Please check your New Relic Agent configuration and ensure New Relic is enabled.'); return {}; } newrelic$1.shim.agent.metrics .getOrCreateMetric(`Supportability/ExternalModules/${EnvelopAttributeName}`) .incrementCallCount(); const logger = newrelic$1.shim.logger.child({ component: EnvelopAttributeName }); const segmentByRequestContext = new WeakMap(); return { onPluginInit({ addPlugin }) { addPlugin(newrelic.useNewRelic({ ...options, extractOperationName: options.extractOperationName ? context => stringInterpolation.stringInterpolator.parse(options.extractOperationName, { context, env: crossHelpers.process.env, }) : undefined, })); }, onExecute({ args: { contextValue } }) { const operationSegment = newrelic$1.shim.getActiveSegment() || newrelic$1.shim.getSegment(); segmentByRequestContext.set(contextValue.request || contextValue, operationSegment); }, onDelegate({ sourceName, fieldName, args, context, key }) { var _a, _b; const parentSegment = newrelic$1.shim.getActiveSegment() || newrelic$1.shim.getSegment() || segmentByRequestContext.get(context.request || context); const delimiter = ((_b = (_a = parentSegment === null || parentSegment === void 0 ? void 0 : parentSegment.transaction) === null || _a === void 0 ? void 0 : _a.nameState) === null || _b === void 0 ? void 0 : _b.delimiter) || '/'; const sourceSegment = newrelic$1.shim.createSegment(`source${delimiter}${sourceName || 'unknown'}${delimiter}${fieldName}`, null, parentSegment); if (options.includeResolverArgs) { if (args) { sourceSegment.addAttribute('args', JSON.stringify(args)); } if (key) { sourceSegment.addAttribute('key', JSON.stringify(key)); } } sourceSegment.start(); return ({ result }) => { if (options.includeRawResult) { sourceSegment.addAttribute('result', JSON.stringify(result)); } sourceSegment.end(); }; }, onFetch({ url, options, context }) { const agent = newrelic$1.shim === null || newrelic$1.shim === void 0 ? void 0 : newrelic$1.shim.agent; const parentSegment = newrelic$1.shim.getActiveSegment() || newrelic$1.shim.getSegment() || (context ? segmentByRequestContext.get(context.request || context) : undefined); const parsedUrl = new URL(url); const name = NAMES.EXTERNAL.PREFIX + parsedUrl.host + parsedUrl.pathname; const httpDetailSegment = newrelic$1.shim.createSegment(name, recordExternal(parsedUrl.host, 'graphql-mesh'), parentSegment); if (!httpDetailSegment) { logger.error(`Unable to create segment for external request: ${name}`); return undefined; } httpDetailSegment.start(); httpDetailSegment.addAttribute('url', url); parsedUrl.searchParams.forEach((value, key) => { httpDetailSegment.addAttribute(`request.parameters.${key}`, value); }); httpDetailSegment.addAttribute('procedure', options.method || 'GET'); const transaction = parentSegment === null || parentSegment === void 0 ? void 0 : parentSegment.transaction; if (transaction) { const outboundHeaders = Object.create(null); if (agent.config.encoding_key && transaction.syntheticsHeader) { outboundHeaders['x-newrelic-synthetics'] = transaction.syntheticsHeader; } if (agent.config.distributed_tracing.enabled) { transaction.insertDistributedTraceHeaders(outboundHeaders); } else if (agent.config.cross_application_tracer.enabled) { cat.addCatHeaders(agent.config, transaction, outboundHeaders); } else { logger.trace('Both DT and CAT are disabled, not adding headers!'); } for (const key in outboundHeaders) { options.headers[key] = outboundHeaders[key]; } } for (const key in options.headers) { httpDetailSegment.addAttribute(`request.headers.${key}`, options.headers[key]); } return ({ response }) => { httpDetailSegment.addAttribute('http.statusCode', response.status); httpDetailSegment.addAttribute('http.statusText', response.statusText); const responseHeadersObj = utils.getHeadersObj(response.headers); for (const key in responseHeadersObj) { httpDetailSegment.addAttribute(`response.headers.${key}`, responseHeadersObj[key]); } if (agent.config.cross_application_tracer.enabled && !agent.config.distributed_tracing.enabled) { try { const { appData } = cat.extractCatHeaders(responseHeadersObj); const decodedAppData = cat.parseAppData(agent.config, appData); const attrs = httpDetailSegment.getAttributes(); const url = new URL(attrs.url); cat.assignCatToSegment(decodedAppData, httpDetailSegment, url.host); } catch (err) { logger.warn(err, 'Cannot add CAT data to segment'); } } httpDetailSegment.end(); }; }, }; } module.exports = useMeshNewrelic;