PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/node_modules/newrelic/lib/error.js

https://github.com/markba/bashhistory.io
JavaScript | 208 lines | 89 code | 20 blank | 99 comment | 28 complexity | 863337b6c5bc3e6cade9f3ff55768b0a MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, MIT
  1. 'use strict';
  2. var path = require('path')
  3. , urltils = require(path.join(__dirname, 'util', 'urltils'))
  4. , logger = require(path.join(__dirname, 'logger')).child({component : 'error_tracer'})
  5. , NAMES = require(path.join(__dirname, 'metrics', 'names'))
  6. ;
  7. /*
  8. *
  9. * CONSTANTS
  10. *
  11. */
  12. var MAX_ERRORS = 20;
  13. /**
  14. * Given either or both of a transaction and an exception, generate an error
  15. * trace in the JSON format expected by the collector. Since this will be
  16. * used by both the HTTP instrumentation, which uses HTTP status codes to
  17. * determine whether a transaction is in error, and the domain-based error
  18. * handler, which traps actual instances of Error, try to set sensible
  19. * defaults for everything.
  20. *
  21. * @param {Transaction} transaction The agent transaction, presumably
  22. * coming out of the instrumentation.
  23. * @param {Error} exception Something trapped by an error listener.
  24. * @param {object} customParameters Any custom parameters associated with
  25. * the request (optional).
  26. */
  27. function createError(transaction, exception, customParameters) {
  28. // the collector throws this out, so don't bother setting it
  29. var timestamp = 0
  30. , name = 'WebTransaction/Uri/*'
  31. , message = ''
  32. , type = 'Error'
  33. , params = {}
  34. ;
  35. if (transaction && transaction.name) name = transaction.name;
  36. // NB: anything throwing / emitting strings is buggy, but it happens
  37. if (typeof exception === 'string') {
  38. message = exception;
  39. }
  40. else if (exception && exception.message) {
  41. message = exception.message;
  42. // only care about extracting the type if it's Error-like.
  43. if (exception && exception.constructor && exception.constructor.name) {
  44. type = exception.constructor.name;
  45. }
  46. }
  47. else if (transaction &&
  48. transaction.statusCode &&
  49. urltils.isError(transaction.agent.config, transaction.statusCode)) {
  50. message = 'HttpError ' + transaction.statusCode;
  51. }
  52. if (customParameters) {
  53. var ignored = [];
  54. if (transaction) ignored = transaction.agent.config.ignored_params;
  55. Object.keys(customParameters).forEach(function cb_forEach(param) {
  56. if (ignored.indexOf(param) === -1) params[param] = customParameters[param];
  57. });
  58. }
  59. if (transaction && transaction.url) {
  60. var url = transaction.url
  61. , statusCode = transaction.statusCode || 500
  62. ;
  63. /* We need a name for this transaction, but since error-tracing can happen
  64. * in the middle of the request, and it's possible that the user will
  65. * recover from the error, name the transaction now, preserving the
  66. * necessary state to maintain any user-provided naming information.
  67. */
  68. if (!transaction.name) {
  69. var partialName = transaction.partialName;
  70. transaction.setName(url, statusCode);
  71. transaction.partialName = partialName;
  72. }
  73. name = transaction.name;
  74. params.request_uri = url;
  75. if (transaction.agent.config.capture_params) {
  76. var requestParams = urltils.parseParameters(url);
  77. // clear out ignored params
  78. transaction.agent.config.ignored_params.forEach(function cb_forEach(k) {
  79. // polymorphic hidden classes aren't an issue with data bags
  80. delete requestParams[k];
  81. });
  82. if (Object.keys(requestParams).length > 0) params.request_params = requestParams;
  83. }
  84. }
  85. var stack = exception && exception.stack;
  86. // FIXME: doing this work should not be the agent's responsibility
  87. if (stack) params.stack_trace = ('' + stack).split(/[\n\r]/g);
  88. return [timestamp, name, message, type, params];
  89. }
  90. /**
  91. * This is a fairly simple-minded tracer that converts errored-out HTTP
  92. * transactions and JS Errors into the error traces expected by the collector.
  93. *
  94. * It also acts as a collector for the traced errors.
  95. */
  96. function ErrorTracer(config) {
  97. this.config = config;
  98. this.errorCount = 0;
  99. this.errors = [];
  100. this.seen = [];
  101. }
  102. /**
  103. * Every finished transaction goes through this handler, so do as
  104. * little as possible.
  105. */
  106. ErrorTracer.prototype.onTransactionFinished = function onTransactionFinished(transaction, metrics) {
  107. if (!transaction) throw new Error("Error collector got a blank transaction.");
  108. if (!metrics) throw new Error("Error collector requires metrics to count errors.");
  109. if (urltils.isError(this.config, transaction.statusCode) ||
  110. (transaction.statusCode < 1 && transaction.exceptions.length > 0)) {
  111. if (transaction.exceptions.length > 0) {
  112. transaction.exceptions.forEach(function cb_forEach(exception) {
  113. this.add(transaction, exception);
  114. }, this);
  115. }
  116. else {
  117. this.add(transaction);
  118. }
  119. var count = metrics.getOrCreateMetric(NAMES.ERRORS.PREFIX + transaction.name);
  120. count.incrementCallCount(1);
  121. }
  122. };
  123. /**
  124. * This function uses an array of seen exceptions to ensure errors don't get
  125. * double-counted. It can also be used as an unofficial means of marking that
  126. * user errors shouldn't be traced.
  127. *
  128. * For an error to be traced, at least one of the transaction or the error
  129. * must be present.
  130. *
  131. * NOTE: this interface is unofficial and may change in future.
  132. *
  133. * @param {Transaction} transaction Transaction associated with the error
  134. * (optional).
  135. * @param {Error} exception The error to be traced (optional).
  136. * @param {object} customParameters Any custom parameters associated with
  137. * the request (optional).
  138. */
  139. ErrorTracer.prototype.add = function add(transaction, exception, customParameters) {
  140. if (!exception) {
  141. if (!transaction) return;
  142. if (!transaction.statusCode) return;
  143. if (transaction.error) return;
  144. }
  145. else {
  146. if (this.seen.indexOf(exception) !== -1) return;
  147. if (typeof exception !== 'string' && !exception.message && !exception.stack) {
  148. return;
  149. }
  150. }
  151. this.errorCount++;
  152. // allow enabling & disabling the error tracer at runtime
  153. if (!this.config.collect_errors ||
  154. !this.config.error_collector || !this.config.error_collector.enabled) return;
  155. if (exception) {
  156. logger.trace(exception, "Got exception to trace:");
  157. // put the error on the transaction to show we've already traced it
  158. if (transaction) transaction.error = exception;
  159. this.seen.push(exception);
  160. }
  161. if (this.errors.length < MAX_ERRORS) {
  162. var error = createError(transaction, exception, customParameters);
  163. logger.debug({error : error}, "Error to be sent to collector:");
  164. this.errors.push(error);
  165. }
  166. else {
  167. logger.debug("Already have %d errors to send to collector, not keeping.",
  168. MAX_ERRORS);
  169. }
  170. };
  171. /**
  172. * If the connection to the collector fails, retain as many as will fit without
  173. * overflowing the current error list.
  174. *
  175. * @param array errors Previously harvested errors.
  176. */
  177. ErrorTracer.prototype.merge = function merge(errors) {
  178. if (!errors) return;
  179. var len = Math.min(errors.length, MAX_ERRORS - this.errors.length);
  180. logger.warn("Merging %s (of %s) errors for next delivery.", len, errors.length);
  181. for (var i = 0; i < len; i++) this.errors.push(errors[i]);
  182. };
  183. module.exports = ErrorTracer;