Skip to content
This repository was archived by the owner on Aug 6, 2024. It is now read-only.

Commit 42469d3

Browse files
committed
feat: add ability to customize Error Groups
New API function called `setErrorGroupCallback` provides a way for you to customize the `error.group.name` attribute of errors that are captured by the agent. This attribute controls how the Errors Inbox functionality groups similar errors together. `setErrorGroupCallback` accepts one argument: a callback function. This function can accept an object argument, which contains metadata associated with an individual error, and must return a string, which will be used as the Error Group name. The object passed to the callback function has the following shape: ``` { error: Error, customAttributes: Object, 'request.uri': String, 'http.statusCode': String, 'http.method': String, 'error.expected': Boolean } ```
1 parent ea94e69 commit 42469d3

File tree

7 files changed

+619
-1
lines changed

7 files changed

+619
-1
lines changed

api.js

+45
Original file line numberDiff line numberDiff line change
@@ -1739,4 +1739,49 @@ function _filterAttributes(attributes, name) {
17391739
return filteredAttributes
17401740
}
17411741

1742+
/**
1743+
* Function for adding a custom callback to generate Error Group names, which
1744+
* will be used by the Errors Inbox to group similar errors together via the `error.group.name`
1745+
* agent attribute.
1746+
*
1747+
* Provided functions must return a string, and receive an object as an argument,
1748+
* which contains information related to the Error that occurred, and has the
1749+
* following format:
1750+
*
1751+
* ```
1752+
* {
1753+
* customAttributes: object,
1754+
* 'request.uri': string,
1755+
* 'http.statusCode': string,
1756+
* 'http.method': string,
1757+
* error: Error,
1758+
* 'error.expected': boolean
1759+
* }
1760+
* ```
1761+
*
1762+
* Calling this function multiple times will replace previously defined functions
1763+
*
1764+
* @param {Function} callback - callback function to generate `error.group.name` attribute
1765+
* @example
1766+
* function myCallback(metadata) {
1767+
* if (metadata['http.statusCode'] === '400') {
1768+
* return 'Bad User Input'
1769+
* }
1770+
* }
1771+
* newrelic.setErrorGroupCallback(myCallback)
1772+
*/
1773+
API.prototype.setErrorGroupCallback = function setErrorGroupCallback(callback) {
1774+
const metric = this.agent.metrics.getOrCreateMetric(
1775+
NAMES.SUPPORTABILITY.API + '/setErrorGroupCallback'
1776+
)
1777+
metric.incrementCallCount()
1778+
1779+
if (!this.shim.isFunction(callback)) {
1780+
logger.warn('Error Group callback must be a function, Error Group attribute will not be added')
1781+
return
1782+
}
1783+
1784+
this.agent.errors.errorGroupCallback = callback
1785+
}
1786+
17421787
module.exports = API

lib/errors/error-collector.js

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class ErrorCollector {
3636
this.seenStringsByTransaction = Object.create(null)
3737

3838
this.traceAggregator.on('starting error_data data send.', this._onSendErrorTrace.bind(this))
39+
40+
this.errorGroupCallback = null
3941
}
4042

4143
_onSendErrorTrace() {
@@ -330,6 +332,10 @@ class ErrorCollector {
330332
return false
331333
}
332334

335+
if (this.errorGroupCallback) {
336+
exception.errorGroupCallback = this.errorGroupCallback
337+
}
338+
333339
const errorTrace = createError(transaction, exception, this.config)
334340
this._maybeRecordErrorMetrics(errorTrace, transaction)
335341

lib/errors/index.js

+37
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
'use strict'
77

8+
const logger = require('../logger').child({ component: 'errors_lib' })
89
const DESTINATIONS = require('../config/attribute-filter').DESTINATIONS
910
const props = require('../util/properties')
1011
const urltils = require('../util/urltils')
@@ -25,6 +26,7 @@ class Exception {
2526
this.customAttributes = customAttributes || {}
2627
this.agentAttributes = agentAttributes || {}
2728
this._expected = expected
29+
this.errorGroupCallback = null
2830
}
2931

3032
getErrorDetails(config) {
@@ -107,9 +109,44 @@ function createError(transaction, exception, config) {
107109
params.intrinsics[ERROR_EXPECTED_PATH] =
108110
exception._expected || errorHelper.isExpected(type, message, transaction, config, urltils)
109111

112+
maybeAddAgentAttributes(params, exception)
113+
110114
return [0, name, message, type, params]
111115
}
112116

117+
function isValidErrorGroupOutput(output) {
118+
return (typeof output === 'string' || output instanceof String) && output !== ''
119+
}
120+
121+
function maybeAddAgentAttributes(attributes, exception) {
122+
if (exception.errorGroupCallback) {
123+
const callbackInput = {
124+
'error': exception.error,
125+
'customAttributes': Object.assign({}, attributes.userAttributes),
126+
'request.uri': attributes.agentAttributes['request.uri'],
127+
'http.statusCode': attributes.agentAttributes['http.statusCode'],
128+
'http.method': attributes.agentAttributes['request.method'],
129+
'error.expected': attributes.intrinsics[ERROR_EXPECTED_PATH]
130+
}
131+
132+
try {
133+
const callbackOutput = exception.errorGroupCallback(callbackInput)
134+
135+
if (!isValidErrorGroupOutput(callbackOutput)) {
136+
logger.warn('Function provided via setErrorGroupCallback return value malformed')
137+
return
138+
}
139+
140+
attributes.agentAttributes['error.group.name'] = callbackOutput
141+
} catch (err) {
142+
logger.warn(
143+
err,
144+
'Function provided via setErrorGroupCallback failed, not generating `error.group.name`'
145+
)
146+
}
147+
}
148+
}
149+
113150
function maybeAddUserAttributes(userAttributes, exception, config) {
114151
const customAttributes = exception.customAttributes
115152
if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) {

0 commit comments

Comments
 (0)