@envelop/newrelic 4.0.0-alpha-4ba70ab.0 → 4.0.0
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 +4 -3
- package/index.d.ts +10 -6
- package/index.js +62 -34
- package/index.mjs +44 -33
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,7 +44,8 @@ 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
|
-
|
|
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
|
|
48
|
+
extractOperationName: (context) => context.request.body.customOperationName, // Allows to set a custom operation name to be used as transaction name and attribute
|
|
48
49
|
}),
|
|
49
50
|
],
|
|
50
51
|
});
|
|
@@ -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:
|
|
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: /^(?!
|
|
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
|
-
import { Plugin } from '@envelop/core';
|
|
1
|
+
import { Plugin, DefaultContext } from '@envelop/core';
|
|
2
|
+
import { GraphQLError } from 'graphql';
|
|
2
3
|
export declare type UseNewRelicOptions = {
|
|
3
4
|
includeOperationDocument?: boolean;
|
|
4
5
|
includeExecuteVariables?: boolean | RegExp;
|
|
@@ -6,12 +7,15 @@ export declare type UseNewRelicOptions = {
|
|
|
6
7
|
trackResolvers?: boolean;
|
|
7
8
|
includeResolverArgs?: boolean | RegExp;
|
|
8
9
|
rootFieldsNaming?: boolean;
|
|
9
|
-
operationNameProperty?: string;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* By default, this plugin skips all `EnvelopError` errors and does not report it to Sentry.
|
|
11
|
+
* Function that returns a custom operation name to be used as transaction name and attribute
|
|
13
12
|
*/
|
|
14
|
-
|
|
13
|
+
extractOperationName?: (context: DefaultContext) => string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Indicates whether or not to skip reporting a given error to NewRelic.
|
|
16
|
+
* By default, this plugin skips all `EnvelopError` errors and does not report them to NewRelic.
|
|
17
|
+
*/
|
|
18
|
+
skipError?: (error: GraphQLError) => boolean;
|
|
15
19
|
};
|
|
16
|
-
export declare function defaultSkipError(error:
|
|
20
|
+
export declare function defaultSkipError(error: GraphQLError): boolean;
|
|
17
21
|
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
|
|
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";
|
|
@@ -31,11 +46,10 @@ const DEFAULT_OPTIONS = {
|
|
|
31
46
|
trackResolvers: false,
|
|
32
47
|
includeResolverArgs: false,
|
|
33
48
|
rootFieldsNaming: false,
|
|
34
|
-
operationNameProperty: '',
|
|
35
49
|
skipError: defaultSkipError,
|
|
36
50
|
};
|
|
37
51
|
function defaultSkipError(error) {
|
|
38
|
-
return error instanceof core.EnvelopError;
|
|
52
|
+
return error.originalError instanceof core.EnvelopError;
|
|
39
53
|
}
|
|
40
54
|
const useNewRelic = (rawOptions) => {
|
|
41
55
|
const options = {
|
|
@@ -44,23 +58,30 @@ const useNewRelic = (rawOptions) => {
|
|
|
44
58
|
};
|
|
45
59
|
options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
|
|
46
60
|
options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
|
|
51
|
-
|
|
61
|
+
const instrumentationApi$ = new Promise(function (resolve) { resolve(_interopNamespace(require('newrelic'))); })
|
|
62
|
+
.then(m => m.default || m)
|
|
63
|
+
.then(({ shim }) => {
|
|
64
|
+
shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
|
|
65
|
+
return shim;
|
|
66
|
+
});
|
|
67
|
+
const logger$ = instrumentationApi$.then(({ logger }) => {
|
|
68
|
+
const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
|
|
69
|
+
childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
|
|
70
|
+
return childLogger;
|
|
71
|
+
});
|
|
52
72
|
return {
|
|
53
|
-
onExecute({ args }) {
|
|
54
|
-
var _a;
|
|
73
|
+
async onExecute({ args }) {
|
|
74
|
+
var _a, _b;
|
|
75
|
+
const instrumentationApi = await instrumentationApi$;
|
|
55
76
|
const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
|
|
56
77
|
const spanContext = instrumentationApi.agent.tracer.getSpanContext();
|
|
57
78
|
const delimiter = transactionNameState.delimiter;
|
|
58
79
|
const rootOperation = args.document.definitions.find(definitionNode => definitionNode.kind === graphql.Kind.OPERATION_DEFINITION);
|
|
59
80
|
const operationType = rootOperation.operation;
|
|
60
81
|
const document = graphql.print(args.document);
|
|
61
|
-
const operationName =
|
|
82
|
+
const operationName = ((_a = options.extractOperationName) === null || _a === void 0 ? void 0 : _a.call(options, args.contextValue)) ||
|
|
62
83
|
args.operationName ||
|
|
63
|
-
((
|
|
84
|
+
((_b = rootOperation.name) === null || _b === void 0 ? void 0 : _b.value) ||
|
|
64
85
|
AttributeName.ANONYMOUS_OPERATION;
|
|
65
86
|
let rootFields = null;
|
|
66
87
|
if (options.rootFieldsNaming) {
|
|
@@ -80,7 +101,8 @@ const useNewRelic = (rawOptions) => {
|
|
|
80
101
|
}
|
|
81
102
|
const operationSegment = instrumentationApi.getActiveSegment();
|
|
82
103
|
const onResolverCalled = options.trackResolvers
|
|
83
|
-
? ({ args: resolversArgs, info }) => {
|
|
104
|
+
? async ({ args: resolversArgs, info }) => {
|
|
105
|
+
const logger = await logger$;
|
|
84
106
|
const { returnType, path, parentType } = info;
|
|
85
107
|
const formattedPath = flattenPath(path, delimiter);
|
|
86
108
|
const currentSegment = instrumentationApi.getActiveSegment();
|
|
@@ -105,14 +127,9 @@ const useNewRelic = (rawOptions) => {
|
|
|
105
127
|
resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
|
|
106
128
|
}
|
|
107
129
|
return ({ result }) => {
|
|
108
|
-
var _a;
|
|
109
130
|
if (options.includeRawResult) {
|
|
110
131
|
resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
|
|
111
132
|
}
|
|
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
133
|
resolverSegment.end();
|
|
117
134
|
};
|
|
118
135
|
}
|
|
@@ -120,23 +137,34 @@ const useNewRelic = (rawOptions) => {
|
|
|
120
137
|
return {
|
|
121
138
|
onResolverCalled,
|
|
122
139
|
onExecuteDone({ result }) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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));
|
|
140
|
+
const sendResult = (singularResult) => {
|
|
141
|
+
var _a;
|
|
142
|
+
if (singularResult.data && options.includeRawResult) {
|
|
143
|
+
spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
|
|
137
144
|
}
|
|
145
|
+
if (singularResult.errors && singularResult.errors.length > 0) {
|
|
146
|
+
const agent = instrumentationApi.agent;
|
|
147
|
+
const transaction = instrumentationApi.tracer.getTransaction();
|
|
148
|
+
for (const error of singularResult.errors) {
|
|
149
|
+
if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
|
|
150
|
+
continue;
|
|
151
|
+
agent.errors.add(transaction, JSON.stringify(error));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
if (core.isAsyncIterable(result)) {
|
|
156
|
+
return {
|
|
157
|
+
onNext: ({ result: singularResult }) => {
|
|
158
|
+
sendResult(singularResult);
|
|
159
|
+
},
|
|
160
|
+
onEnd: () => {
|
|
161
|
+
operationSegment.end();
|
|
162
|
+
},
|
|
163
|
+
};
|
|
138
164
|
}
|
|
165
|
+
sendResult(result);
|
|
139
166
|
operationSegment.end();
|
|
167
|
+
return {};
|
|
140
168
|
},
|
|
141
169
|
};
|
|
142
170
|
},
|
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";
|
|
@@ -25,11 +23,10 @@ const DEFAULT_OPTIONS = {
|
|
|
25
23
|
trackResolvers: false,
|
|
26
24
|
includeResolverArgs: false,
|
|
27
25
|
rootFieldsNaming: false,
|
|
28
|
-
operationNameProperty: '',
|
|
29
26
|
skipError: defaultSkipError,
|
|
30
27
|
};
|
|
31
28
|
function defaultSkipError(error) {
|
|
32
|
-
return error instanceof EnvelopError;
|
|
29
|
+
return error.originalError instanceof EnvelopError;
|
|
33
30
|
}
|
|
34
31
|
const useNewRelic = (rawOptions) => {
|
|
35
32
|
const options = {
|
|
@@ -38,23 +35,30 @@ const useNewRelic = (rawOptions) => {
|
|
|
38
35
|
};
|
|
39
36
|
options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
|
|
40
37
|
options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
|
|
45
|
-
|
|
38
|
+
const instrumentationApi$ = import('newrelic')
|
|
39
|
+
.then(m => m.default || m)
|
|
40
|
+
.then(({ shim }) => {
|
|
41
|
+
shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
|
|
42
|
+
return shim;
|
|
43
|
+
});
|
|
44
|
+
const logger$ = instrumentationApi$.then(({ logger }) => {
|
|
45
|
+
const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
|
|
46
|
+
childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
|
|
47
|
+
return childLogger;
|
|
48
|
+
});
|
|
46
49
|
return {
|
|
47
|
-
onExecute({ args }) {
|
|
48
|
-
var _a;
|
|
50
|
+
async onExecute({ args }) {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
const instrumentationApi = await instrumentationApi$;
|
|
49
53
|
const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
|
|
50
54
|
const spanContext = instrumentationApi.agent.tracer.getSpanContext();
|
|
51
55
|
const delimiter = transactionNameState.delimiter;
|
|
52
56
|
const rootOperation = args.document.definitions.find(definitionNode => definitionNode.kind === Kind.OPERATION_DEFINITION);
|
|
53
57
|
const operationType = rootOperation.operation;
|
|
54
58
|
const document = print(args.document);
|
|
55
|
-
const operationName =
|
|
59
|
+
const operationName = ((_a = options.extractOperationName) === null || _a === void 0 ? void 0 : _a.call(options, args.contextValue)) ||
|
|
56
60
|
args.operationName ||
|
|
57
|
-
((
|
|
61
|
+
((_b = rootOperation.name) === null || _b === void 0 ? void 0 : _b.value) ||
|
|
58
62
|
AttributeName.ANONYMOUS_OPERATION;
|
|
59
63
|
let rootFields = null;
|
|
60
64
|
if (options.rootFieldsNaming) {
|
|
@@ -74,7 +78,8 @@ const useNewRelic = (rawOptions) => {
|
|
|
74
78
|
}
|
|
75
79
|
const operationSegment = instrumentationApi.getActiveSegment();
|
|
76
80
|
const onResolverCalled = options.trackResolvers
|
|
77
|
-
? ({ args: resolversArgs, info }) => {
|
|
81
|
+
? async ({ args: resolversArgs, info }) => {
|
|
82
|
+
const logger = await logger$;
|
|
78
83
|
const { returnType, path, parentType } = info;
|
|
79
84
|
const formattedPath = flattenPath(path, delimiter);
|
|
80
85
|
const currentSegment = instrumentationApi.getActiveSegment();
|
|
@@ -99,14 +104,9 @@ const useNewRelic = (rawOptions) => {
|
|
|
99
104
|
resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
|
|
100
105
|
}
|
|
101
106
|
return ({ result }) => {
|
|
102
|
-
var _a;
|
|
103
107
|
if (options.includeRawResult) {
|
|
104
108
|
resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
|
|
105
109
|
}
|
|
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
110
|
resolverSegment.end();
|
|
111
111
|
};
|
|
112
112
|
}
|
|
@@ -114,23 +114,34 @@ const useNewRelic = (rawOptions) => {
|
|
|
114
114
|
return {
|
|
115
115
|
onResolverCalled,
|
|
116
116
|
onExecuteDone({ result }) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
agent.errors.add(transaction, JSON.stringify(error));
|
|
117
|
+
const sendResult = (singularResult) => {
|
|
118
|
+
var _a;
|
|
119
|
+
if (singularResult.data && options.includeRawResult) {
|
|
120
|
+
spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
|
|
121
|
+
}
|
|
122
|
+
if (singularResult.errors && singularResult.errors.length > 0) {
|
|
123
|
+
const agent = instrumentationApi.agent;
|
|
124
|
+
const transaction = instrumentationApi.tracer.getTransaction();
|
|
125
|
+
for (const error of singularResult.errors) {
|
|
126
|
+
if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
|
|
127
|
+
continue;
|
|
128
|
+
agent.errors.add(transaction, JSON.stringify(error));
|
|
129
|
+
}
|
|
131
130
|
}
|
|
131
|
+
};
|
|
132
|
+
if (isAsyncIterable(result)) {
|
|
133
|
+
return {
|
|
134
|
+
onNext: ({ result: singularResult }) => {
|
|
135
|
+
sendResult(singularResult);
|
|
136
|
+
},
|
|
137
|
+
onEnd: () => {
|
|
138
|
+
operationSegment.end();
|
|
139
|
+
},
|
|
140
|
+
};
|
|
132
141
|
}
|
|
142
|
+
sendResult(result);
|
|
133
143
|
operationSegment.end();
|
|
144
|
+
return {};
|
|
134
145
|
},
|
|
135
146
|
};
|
|
136
147
|
},
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/newrelic",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "2.1
|
|
6
|
+
"@envelop/core": "^2.3.1",
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
8
8
|
"newrelic": "^7 || ^8.0.0"
|
|
9
9
|
},
|