@envelop/newrelic 3.1.0 → 3.2.1

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/README.md CHANGED
@@ -44,6 +44,7 @@ const getEnveloped = envelop({
44
44
  trackResolvers: true, // default `false`. When set to `true`, track resolvers as segments to monitor their performance
45
45
  includeResolverArgs: false, // default `false`. When set to `true`, includes all the arguments passed to resolvers with their values
46
46
  rootFieldsNaming: true, // default `false`. When set to `true` append the names of operation root fields to the transaction name
47
+ skipError: error => { ... return true; }, // a function that allows you to skip reporting a given error to NewRelic. By default custom `EnvelopError`s will be skipped
47
48
  operationNameProperty: 'id', // default empty. When passed will check for the property name passed, within the document object. Will eventually use its value as operation name. Useful for custom document properties (e.g. queryId/hash)
48
49
  }),
49
50
  ],
@@ -62,9 +63,9 @@ Below is a quick example of how you can use RegEx to set up white/black listing
62
63
 
63
64
  ```ts
64
65
  useNewRelic({
65
- includeExecuteVariables: /^(client.*|application.*)$.*$/i, // whitelist, track only variables whose name starts with client or application (e.g. clientName, applicationId)
66
+ includeExecuteVariables: /client|application/i, // whitelist, track only variables whose name contains "client" or "application" (e.g. clientName, applicationId, xApplicationId)
66
67
  trackResolvers: true, // track resolvers, since we also want to track resolvers' arguments
67
- includeResolverArgs: /^(?!(name|email|password)$).*$/, // blacklist, track all arguments whose name does not match 'name', 'email' nor 'password'
68
+ includeResolverArgs: /^(?!name|email|password).*/i, // blacklist, track all arguments whose name does not match 'name', 'email' nor 'password'
68
69
  }),
69
70
  ```
70
71
 
package/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Plugin } from '@envelop/core';
2
+ import { GraphQLError } from 'graphql';
2
3
  export declare type UseNewRelicOptions = {
3
4
  includeOperationDocument?: boolean;
4
5
  includeExecuteVariables?: boolean | RegExp;
@@ -8,10 +9,10 @@ export declare type UseNewRelicOptions = {
8
9
  rootFieldsNaming?: boolean;
9
10
  operationNameProperty?: string;
10
11
  /**
11
- * Indicates whether or not to skip Sentry exception reporting for a given error.
12
- * By default, this plugin skips all `EnvelopError` errors and does not report it to Sentry.
12
+ * Indicates whether or not to skip reporting a given error to NewRelic.
13
+ * By default, this plugin skips all `EnvelopError` errors and does not report them to NewRelic.
13
14
  */
14
- skipError?: (args: Error) => boolean;
15
+ skipError?: (error: GraphQLError) => boolean;
15
16
  };
16
- export declare function defaultSkipError(error: Error): boolean;
17
+ export declare function defaultSkipError(error: GraphQLError): boolean;
17
18
  export declare const useNewRelic: (rawOptions?: UseNewRelicOptions | undefined) => Plugin;
package/index.js CHANGED
@@ -2,13 +2,28 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
5
+ function _interopNamespace(e) {
6
+ if (e && e.__esModule) { return e; } else {
7
+ var n = {};
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: function () {
14
+ return e[k];
15
+ }
16
+ });
17
+ });
18
+ }
19
+ n['default'] = e;
20
+ return n;
21
+ }
22
+ }
6
23
 
7
- const newRelic = _interopDefault(require('newrelic'));
8
24
  const core = require('@envelop/core');
9
25
  const graphql = require('graphql');
10
26
 
11
- const { shim: instrumentationApi } = newRelic;
12
27
  var AttributeName;
13
28
  (function (AttributeName) {
14
29
  AttributeName["COMPONENT_NAME"] = "Envelop_NewRelic_Plugin";
@@ -35,7 +50,7 @@ const DEFAULT_OPTIONS = {
35
50
  skipError: defaultSkipError,
36
51
  };
37
52
  function defaultSkipError(error) {
38
- return error instanceof core.EnvelopError;
53
+ return error.originalError instanceof core.EnvelopError;
39
54
  }
40
55
  const useNewRelic = (rawOptions) => {
41
56
  const options = {
@@ -44,14 +59,21 @@ const useNewRelic = (rawOptions) => {
44
59
  };
45
60
  options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
46
61
  options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
47
- const logger = instrumentationApi.logger.child({ component: AttributeName.COMPONENT_NAME });
48
- logger.info(`${AttributeName.COMPONENT_NAME} registered`);
49
- instrumentationApi.agent.metrics
50
- .getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
51
- .incrementCallCount();
62
+ const instrumentationApi$ = new Promise(function (resolve) { resolve(_interopNamespace(require('newrelic'))); })
63
+ .then(m => m.default || m)
64
+ .then(({ shim }) => {
65
+ shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
66
+ return shim;
67
+ });
68
+ const logger$ = instrumentationApi$.then(({ logger }) => {
69
+ const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
70
+ childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
71
+ return childLogger;
72
+ });
52
73
  return {
53
- onExecute({ args }) {
74
+ async onExecute({ args }) {
54
75
  var _a;
76
+ const instrumentationApi = await instrumentationApi$;
55
77
  const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
56
78
  const spanContext = instrumentationApi.agent.tracer.getSpanContext();
57
79
  const delimiter = transactionNameState.delimiter;
@@ -80,7 +102,8 @@ const useNewRelic = (rawOptions) => {
80
102
  }
81
103
  const operationSegment = instrumentationApi.getActiveSegment();
82
104
  const onResolverCalled = options.trackResolvers
83
- ? ({ args: resolversArgs, info }) => {
105
+ ? async ({ args: resolversArgs, info }) => {
106
+ const logger = await logger$;
84
107
  const { returnType, path, parentType } = info;
85
108
  const formattedPath = flattenPath(path, delimiter);
86
109
  const currentSegment = instrumentationApi.getActiveSegment();
@@ -105,14 +128,9 @@ const useNewRelic = (rawOptions) => {
105
128
  resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
106
129
  }
107
130
  return ({ result }) => {
108
- var _a;
109
131
  if (options.includeRawResult) {
110
132
  resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
111
133
  }
112
- if (result instanceof Error && !((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, result))) {
113
- const transaction = instrumentationApi.tracer.getTransaction();
114
- instrumentationApi.agent.errors.add(transaction, JSON.stringify(result));
115
- }
116
134
  resolverSegment.end();
117
135
  };
118
136
  }
@@ -120,23 +138,34 @@ const useNewRelic = (rawOptions) => {
120
138
  return {
121
139
  onResolverCalled,
122
140
  onExecuteDone({ result }) {
123
- if (core.isAsyncIterable(result)) {
124
- operationSegment.end();
125
- // eslint-disable-next-line no-console
126
- console.warn(`Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
127
- return;
128
- }
129
- if (result.data && options.includeRawResult) {
130
- spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
131
- }
132
- if (result.errors && result.errors.length > 0) {
133
- const agent = instrumentationApi.agent;
134
- const transaction = instrumentationApi.tracer.getTransaction();
135
- for (const error of result.errors) {
136
- agent.errors.add(transaction, JSON.stringify(error));
141
+ const sendResult = (singularResult) => {
142
+ var _a;
143
+ if (singularResult.data && options.includeRawResult) {
144
+ spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
137
145
  }
146
+ if (singularResult.errors && singularResult.errors.length > 0) {
147
+ const agent = instrumentationApi.agent;
148
+ const transaction = instrumentationApi.tracer.getTransaction();
149
+ for (const error of singularResult.errors) {
150
+ if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
151
+ continue;
152
+ agent.errors.add(transaction, JSON.stringify(error));
153
+ }
154
+ }
155
+ };
156
+ if (core.isAsyncIterable(result)) {
157
+ return {
158
+ onNext: ({ result: singularResult }) => {
159
+ sendResult(singularResult);
160
+ },
161
+ onEnd: () => {
162
+ operationSegment.end();
163
+ },
164
+ };
138
165
  }
166
+ sendResult(result);
139
167
  operationSegment.end();
168
+ return {};
140
169
  },
141
170
  };
142
171
  },
package/index.mjs CHANGED
@@ -1,8 +1,6 @@
1
- import newRelic from 'newrelic';
2
1
  import { EnvelopError, isAsyncIterable } from '@envelop/core';
3
2
  import { Kind, print } from 'graphql';
4
3
 
5
- const { shim: instrumentationApi } = newRelic;
6
4
  var AttributeName;
7
5
  (function (AttributeName) {
8
6
  AttributeName["COMPONENT_NAME"] = "Envelop_NewRelic_Plugin";
@@ -29,7 +27,7 @@ const DEFAULT_OPTIONS = {
29
27
  skipError: defaultSkipError,
30
28
  };
31
29
  function defaultSkipError(error) {
32
- return error instanceof EnvelopError;
30
+ return error.originalError instanceof EnvelopError;
33
31
  }
34
32
  const useNewRelic = (rawOptions) => {
35
33
  const options = {
@@ -38,14 +36,21 @@ const useNewRelic = (rawOptions) => {
38
36
  };
39
37
  options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
40
38
  options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
41
- const logger = instrumentationApi.logger.child({ component: AttributeName.COMPONENT_NAME });
42
- logger.info(`${AttributeName.COMPONENT_NAME} registered`);
43
- instrumentationApi.agent.metrics
44
- .getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
45
- .incrementCallCount();
39
+ const instrumentationApi$ = import('newrelic')
40
+ .then(m => m.default || m)
41
+ .then(({ shim }) => {
42
+ shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
43
+ return shim;
44
+ });
45
+ const logger$ = instrumentationApi$.then(({ logger }) => {
46
+ const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
47
+ childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
48
+ return childLogger;
49
+ });
46
50
  return {
47
- onExecute({ args }) {
51
+ async onExecute({ args }) {
48
52
  var _a;
53
+ const instrumentationApi = await instrumentationApi$;
49
54
  const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
50
55
  const spanContext = instrumentationApi.agent.tracer.getSpanContext();
51
56
  const delimiter = transactionNameState.delimiter;
@@ -74,7 +79,8 @@ const useNewRelic = (rawOptions) => {
74
79
  }
75
80
  const operationSegment = instrumentationApi.getActiveSegment();
76
81
  const onResolverCalled = options.trackResolvers
77
- ? ({ args: resolversArgs, info }) => {
82
+ ? async ({ args: resolversArgs, info }) => {
83
+ const logger = await logger$;
78
84
  const { returnType, path, parentType } = info;
79
85
  const formattedPath = flattenPath(path, delimiter);
80
86
  const currentSegment = instrumentationApi.getActiveSegment();
@@ -99,14 +105,9 @@ const useNewRelic = (rawOptions) => {
99
105
  resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
100
106
  }
101
107
  return ({ result }) => {
102
- var _a;
103
108
  if (options.includeRawResult) {
104
109
  resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
105
110
  }
106
- if (result instanceof Error && !((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, result))) {
107
- const transaction = instrumentationApi.tracer.getTransaction();
108
- instrumentationApi.agent.errors.add(transaction, JSON.stringify(result));
109
- }
110
111
  resolverSegment.end();
111
112
  };
112
113
  }
@@ -114,23 +115,34 @@ const useNewRelic = (rawOptions) => {
114
115
  return {
115
116
  onResolverCalled,
116
117
  onExecuteDone({ result }) {
117
- if (isAsyncIterable(result)) {
118
- operationSegment.end();
119
- // eslint-disable-next-line no-console
120
- console.warn(`Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
121
- return;
122
- }
123
- if (result.data && options.includeRawResult) {
124
- spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
125
- }
126
- if (result.errors && result.errors.length > 0) {
127
- const agent = instrumentationApi.agent;
128
- const transaction = instrumentationApi.tracer.getTransaction();
129
- for (const error of result.errors) {
130
- agent.errors.add(transaction, JSON.stringify(error));
118
+ const sendResult = (singularResult) => {
119
+ var _a;
120
+ if (singularResult.data && options.includeRawResult) {
121
+ spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
122
+ }
123
+ if (singularResult.errors && singularResult.errors.length > 0) {
124
+ const agent = instrumentationApi.agent;
125
+ const transaction = instrumentationApi.tracer.getTransaction();
126
+ for (const error of singularResult.errors) {
127
+ if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
128
+ continue;
129
+ agent.errors.add(transaction, JSON.stringify(error));
130
+ }
131
131
  }
132
+ };
133
+ if (isAsyncIterable(result)) {
134
+ return {
135
+ onNext: ({ result: singularResult }) => {
136
+ sendResult(singularResult);
137
+ },
138
+ onEnd: () => {
139
+ operationSegment.end();
140
+ },
141
+ };
132
142
  }
143
+ sendResult(result);
133
144
  operationSegment.end();
145
+ return {};
134
146
  },
135
147
  };
136
148
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envelop/newrelic",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "@envelop/core": "^2.1.0",