notify.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const tslib_1 = require("tslib");
  4. const configuration_1 = require("../configuration");
  5. const serviceManager_1 = require("../serviceManager");
  6. const debug_1 = require("debug");
  7. const semver = require("semver");
  8. const stackParser_1 = require("../utils/stackParser");
  9. const fs = require("fs");
  10. const path = require("path");
  11. class NotifyOptions {
  12. }
  13. exports.NotifyOptions = NotifyOptions;
  14. class ErrorContext {
  15. }
  16. exports.ErrorContext = ErrorContext;
  17. const optionsDefault = {
  18. catchExceptions: true
  19. };
  20. class NotifyFeature {
  21. constructor() {
  22. this.logger = debug_1.default('axm:features:notify');
  23. }
  24. init(options) {
  25. if (options === undefined) {
  26. options = optionsDefault;
  27. }
  28. this.logger('init');
  29. this.transport = serviceManager_1.ServiceManager.get('transport');
  30. if (this.transport === undefined) {
  31. return this.logger(`Failed to load transporter service`);
  32. }
  33. configuration_1.default.configureModule({
  34. error: true
  35. });
  36. if (options.catchExceptions === false)
  37. return;
  38. this.logger('Registering hook to catch unhandled exception/rejection');
  39. this.cache = new stackParser_1.Cache({
  40. miss: (key) => {
  41. try {
  42. const content = fs.readFileSync(path.resolve(key));
  43. return content.toString().split(/\r?\n/);
  44. }
  45. catch (err) {
  46. this.logger('Error while trying to get file from FS : %s', err.message || err);
  47. return null;
  48. }
  49. },
  50. ttl: 30 * 60
  51. });
  52. this.stackParser = new stackParser_1.StackTraceParser({
  53. cache: this.cache,
  54. contextSize: 5
  55. });
  56. this.catchAll();
  57. }
  58. destroy() {
  59. process.removeListener('uncaughtException', this.onUncaughtException);
  60. process.removeListener('unhandledRejection', this.onUnhandledRejection);
  61. this.logger('destroy');
  62. }
  63. getSafeError(err) {
  64. if (err instanceof Error)
  65. return err;
  66. let message;
  67. try {
  68. message = `Non-error value: ${JSON.stringify(err)}`;
  69. }
  70. catch (e) {
  71. try {
  72. message = `Unserializable non-error value: ${String(e)}`;
  73. }
  74. catch (e2) {
  75. message = `Unserializable non-error value that cannot be converted to a string`;
  76. }
  77. }
  78. if (message.length > 1000)
  79. message = message.substr(0, 1000) + '...';
  80. return new Error(message);
  81. }
  82. notifyError(err, context) {
  83. if (typeof context !== 'object') {
  84. context = {};
  85. }
  86. if (this.transport === undefined) {
  87. return this.logger(`Tried to send error without having transporter available`);
  88. }
  89. const safeError = this.getSafeError(err);
  90. let stackContext = null;
  91. if (err instanceof Error) {
  92. stackContext = this.stackParser.retrieveContext(err);
  93. }
  94. const payload = Object.assign({
  95. message: safeError.message,
  96. stack: safeError.stack,
  97. name: safeError.name,
  98. metadata: context
  99. }, stackContext === null ? {} : stackContext);
  100. return this.transport.send('process:exception', payload);
  101. }
  102. onUncaughtException(error) {
  103. if (semver.satisfies(process.version, '< 6')) {
  104. console.error(error.stack);
  105. }
  106. else {
  107. console.error(error);
  108. }
  109. const safeError = this.getSafeError(error);
  110. let stackContext = null;
  111. if (error instanceof Error) {
  112. stackContext = this.stackParser.retrieveContext(error);
  113. }
  114. const payload = Object.assign({
  115. message: safeError.message,
  116. stack: safeError.stack,
  117. name: safeError.name
  118. }, stackContext === null ? {} : stackContext);
  119. if (serviceManager_1.ServiceManager.get('transport')) {
  120. serviceManager_1.ServiceManager.get('transport').send('process:exception', payload);
  121. }
  122. if (process.listeners('uncaughtException').length === 1) {
  123. process.exit(1);
  124. }
  125. }
  126. onUnhandledRejection(error) {
  127. if (error === undefined)
  128. return;
  129. console.error(error);
  130. const safeError = this.getSafeError(error);
  131. let stackContext = null;
  132. if (error instanceof Error) {
  133. stackContext = this.stackParser.retrieveContext(error);
  134. }
  135. const payload = Object.assign({
  136. message: safeError.message,
  137. stack: safeError.stack,
  138. name: safeError.name
  139. }, stackContext === null ? {} : stackContext);
  140. if (serviceManager_1.ServiceManager.get('transport')) {
  141. serviceManager_1.ServiceManager.get('transport').send('process:exception', payload);
  142. }
  143. }
  144. catchAll() {
  145. if (process.env.exec_mode === 'cluster_mode') {
  146. return false;
  147. }
  148. process.on('uncaughtException', this.onUncaughtException.bind(this));
  149. process.on('unhandledRejection', this.onUnhandledRejection.bind(this));
  150. }
  151. expressErrorHandler() {
  152. const self = this;
  153. configuration_1.default.configureModule({
  154. error: true
  155. });
  156. return function errorHandler(err, req, res, next) {
  157. const safeError = self.getSafeError(err);
  158. const payload = {
  159. message: safeError.message,
  160. stack: safeError.stack,
  161. name: safeError.name,
  162. metadata: {
  163. http: {
  164. url: req.url,
  165. params: req.params,
  166. method: req.method,
  167. query: req.query,
  168. body: req.body,
  169. path: req.path,
  170. route: req.route && req.route.path ? req.route.path : undefined
  171. },
  172. custom: {
  173. user: typeof req.user === 'object' ? req.user.id : undefined
  174. }
  175. }
  176. };
  177. if (serviceManager_1.ServiceManager.get('transport')) {
  178. serviceManager_1.ServiceManager.get('transport').send('process:exception', payload);
  179. }
  180. return next(err);
  181. };
  182. }
  183. koaErrorHandler() {
  184. const self = this;
  185. configuration_1.default.configureModule({
  186. error: true
  187. });
  188. return function (ctx, next) {
  189. return tslib_1.__awaiter(this, void 0, void 0, function* () {
  190. try {
  191. yield next();
  192. }
  193. catch (err) {
  194. const safeError = self.getSafeError(err);
  195. const payload = {
  196. message: safeError.message,
  197. stack: safeError.stack,
  198. name: safeError.name,
  199. metadata: {
  200. http: {
  201. url: ctx.request.url,
  202. params: ctx.params,
  203. method: ctx.request.method,
  204. query: ctx.request.query,
  205. body: ctx.request.body,
  206. path: ctx.request.path,
  207. route: ctx._matchedRoute
  208. },
  209. custom: {
  210. user: typeof ctx.user === 'object' ? ctx.user.id : undefined
  211. }
  212. }
  213. };
  214. if (serviceManager_1.ServiceManager.get('transport')) {
  215. serviceManager_1.ServiceManager.get('transport').send('process:exception', payload);
  216. }
  217. throw err;
  218. }
  219. });
  220. };
  221. }
  222. }
  223. exports.NotifyFeature = NotifyFeature;
  224. //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm90aWZ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2ZlYXR1cmVzL25vdGlmeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZLENBQUE7OztBQUdaLG9EQUE0QztBQUM1QyxzREFBa0Q7QUFDbEQsaUNBQXlCO0FBRXpCLGlDQUFnQztBQUNoQyxzREFBNEU7QUFDNUUseUJBQXdCO0FBQ3hCLDZCQUE0QjtBQUU1QixNQUFhLGFBQWE7Q0FFekI7QUFGRCxzQ0FFQztBQUVELE1BQWEsWUFBWTtDQVV4QjtBQVZELG9DQVVDO0FBRUQsTUFBTSxjQUFjLEdBQWtCO0lBQ3BDLGVBQWUsRUFBRSxJQUFJO0NBQ3RCLENBQUE7QUFFRCxNQUFhLGFBQWE7SUFBMUI7UUFFVSxXQUFNLEdBQWEsZUFBSyxDQUFDLHFCQUFxQixDQUFDLENBQUE7SUFxT3pELENBQUM7SUFoT0MsSUFBSSxDQUFFLE9BQXVCO1FBQzNCLElBQUksT0FBTyxLQUFLLFNBQVMsRUFBRTtZQUN6QixPQUFPLEdBQUcsY0FBYyxDQUFBO1NBQ3pCO1FBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUNuQixJQUFJLENBQUMsU0FBUyxHQUFHLCtCQUFjLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFBO1FBQ2hELElBQUksSUFBSSxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUU7WUFDaEMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLG9DQUFvQyxDQUFDLENBQUE7U0FDekQ7UUFFRCx1QkFBYSxDQUFDLGVBQWUsQ0FBQztZQUM1QixLQUFLLEVBQUcsSUFBSTtTQUNiLENBQUMsQ0FBQTtRQUNGLElBQUksT0FBTyxDQUFDLGVBQWUsS0FBSyxLQUFLO1lBQUUsT0FBTTtRQUM3QyxJQUFJLENBQUMsTUFBTSxDQUFDLHlEQUF5RCxDQUFDLENBQUE7UUFDdEUsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLG1CQUFLLENBQUM7WUFDckIsSUFB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