UNPKG

newrelic

Version:
190 lines (161 loc) 6.22 kB
'use strict' var DESTINATIONS = require('../config/attribute-filter').DESTINATIONS var NAMES = require('../metrics/names') var props = require('../util/properties') var urltils = require('../util/urltils') module.exports.createError = createError module.exports.createEvent = createEvent /** * Given either or both of a transaction and an exception, generate an error * trace in the JSON format expected by the collector. Since this will be * used by both the HTTP instrumentation, which uses HTTP status codes to * determine whether a transaction is in error, and the domain-based error * handler, which traps actual instances of Error, try to set sensible * defaults for everything. * * @param {Transaction} transaction The agent transaction, presumably * coming out of the instrumentation. * @param {Error} exception Something trapped by an error listener. * @param {object} customAttributes Any custom attributes associated with * the request (optional). */ function createError(transaction, exception, customAttributes, config) { var name = 'Unknown' var message = '' var type = 'Error' var params = { userAttributes: Object.create(null), agentAttributes: Object.create(null), intrinsics: Object.create(null) } // String errors do not provide us with as much information to provide to the // user, but it is a common pattern. if (typeof exception === 'string') { message = exception } else if ( exception !== null && typeof exception === 'object' && exception.message && !config.high_security && !config.strip_exception_messages.enabled ) { message = exception.message if (exception.name) { type = exception.name } else if (exception.constructor && exception.constructor.name) { type = exception.constructor.name } } else if (transaction && transaction.statusCode && urltils.isError(config, transaction.statusCode)) { message = 'HttpError ' + transaction.statusCode } if (transaction) { // transaction.getName is expensive due to running normalizers and ignore // rules if a name hasn't been assigned yet. var txName = transaction.getFullName() if (txName) { name = txName } // Copy all of the parameters off of the transaction. params.agentAttributes = transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT) params.intrinsics = transaction.getIntrinsicAttributes() // There should be no attributes to copy in HSM, but check status anyway if (!config.high_security) { var custom = transaction.trace.custom.get(DESTINATIONS.ERROR_EVENT) urltils.overwriteParameters(custom, params.userAttributes) } } if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) { for (var key in customAttributes) { if (props.hasOwn(customAttributes, key)) { var dest = config.attributeFilter.filter(DESTINATIONS.ERROR_EVENT, key) if (dest & DESTINATIONS.ERROR_EVENT) { params.userAttributes[key] = customAttributes[key] } } } } var stack = exception && exception.stack if (stack) { params.stack_trace = ('' + stack).split(/[\n\r]/g) if (config.high_security || config.strip_exception_messages.enabled) { params.stack_trace[0] = exception.name + ': <redacted>' } } var res = [0, name, message, type, params] if (transaction) { res.transaction = transaction.id } return res } /** * Creates a structure for error event that is sent to the collector. * The error parameter is an output of the createError() function for a given exception. */ function createEvent(transaction, error, timestamp, config) { var message = error[2] var errorClass = error[3] var errorParams = error[4] var intrinsicAttributes = _getErrorEventIntrinsicAttrs( transaction, errorClass, message, timestamp, config ) // the error structure created by createError() already performs filtering of custom // and agent attributes, so it is ok to just copy them var userAttributes = Object.assign(Object.create(null), errorParams.userAttributes) var agentAttributes = Object.assign(Object.create(null), errorParams.agentAttributes) var errorEvent = [ intrinsicAttributes, userAttributes, agentAttributes ] return errorEvent } function _getErrorEventIntrinsicAttrs(transaction, errorClass, message, timestamp, conf) { // the server expects seconds instead of milliseconds if (timestamp) timestamp = timestamp / 1000 var attributes = { type: "TransactionError", "error.class": errorClass, "error.message": conf.high_security ? '' : message, timestamp: timestamp } if (transaction) { attributes.transactionName = transaction.getFullName() attributes.duration = transaction.timer.getDurationInMillis() / 1000 var metric = transaction.metrics.getMetric(NAMES.QUEUETIME) if (metric) { attributes.queueDuration = metric.total } metric = transaction.metrics.getMetric(NAMES.EXTERNAL.ALL) if (metric) { attributes.externalDuration = metric.total attributes.externalCallCount = metric.callCount } metric = transaction.metrics.getMetric(NAMES.DB.ALL) if (metric) { attributes.databaseDuration = metric.total attributes.databaseCallCount = metric.callCount } if (transaction.syntheticsData) { attributes["nr.syntheticsResourceId"] = transaction.syntheticsData.resourceId attributes["nr.syntheticsJobId"] = transaction.syntheticsData.jobId attributes["nr.syntheticsMonitorId"] = transaction.syntheticsData.monitorId } if (transaction.agent.config.distributed_tracing.enabled) { transaction.addDistributedTraceIntrinsics(attributes) } else { attributes['nr.referringTransactionGuid'] = transaction.referringTransactionGuid } attributes['nr.transactionGuid'] = transaction.id if (transaction.port) { attributes.port = transaction.port } } else { attributes.transactionName = 'Unknown' } return attributes }