@naturalcycles/backend-lib 5.17.2 → 6.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.
@@ -1,26 +1,20 @@
1
1
  import { CommonLogger, Primitive, StringMap } from '@naturalcycles/js-lib';
2
- import type { Breadcrumb, NodeOptions, SeverityLevel } from '@sentry/node';
2
+ import type { Breadcrumb, SeverityLevel } from '@sentry/node';
3
3
  import type * as SentryLib from '@sentry/node';
4
- import { BackendErrorRequestHandler, BackendRequestHandler } from '../index';
5
- export interface SentrySharedServiceCfg extends NodeOptions {
4
+ export interface SentrySharedServiceCfg {
5
+ sentry: typeof SentryLib;
6
6
  }
7
+ /**
8
+ * Recommended sentry configuration:
9
+ *
10
+ * {
11
+ * maxValueLength: 2000, // default is 250 characters
12
+ * }
13
+ *
14
+ */
7
15
  export declare class SentrySharedService {
8
- private sentryServiceCfg;
9
16
  constructor(sentryServiceCfg: SentrySharedServiceCfg);
10
- init(): void;
11
- sentry(): typeof SentryLib;
12
- /**
13
- * Currently not recommended, because it makes `void` requests throw user-facing errors.
14
- *
15
- * UPD: to be tested. Without it - request is not enriched and the error is less useful.
16
- */
17
- getRequestHandler(): BackendRequestHandler;
18
- /**
19
- * Currently not recommended, as it's replaced by our custom sentryErrorHandler.
20
- *
21
- * @deprecated
22
- */
23
- getErrorHandler(): BackendErrorRequestHandler;
17
+ sentry: typeof SentryLib;
24
18
  /**
25
19
  * For GDPR reasons we never send more information than just User ID.
26
20
  */
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SentrySharedService = void 0;
4
- const tslib_1 = require("tslib");
5
4
  const js_lib_1 = require("@naturalcycles/js-lib");
6
5
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
7
6
  const index_1 = require("../index");
@@ -17,54 +16,27 @@ const INSPECT_OPT = {
17
16
  colors: false,
18
17
  includeErrorData: true,
19
18
  };
19
+ /**
20
+ * Recommended sentry configuration:
21
+ *
22
+ * {
23
+ * maxValueLength: 2000, // default is 250 characters
24
+ * }
25
+ *
26
+ */
20
27
  class SentrySharedService {
21
28
  constructor(sentryServiceCfg) {
22
- this.sentryServiceCfg = sentryServiceCfg;
23
- }
24
- init() {
25
- this.sentry();
26
- }
27
- sentry() {
28
- // Lazy-loading `@sentry/node`
29
- // Reasons:
30
- // 1. Can be useful is this module is imported but never actually used
31
- // 2. Works around memory leak when used with Jest
32
- const sentry = require('@sentry/node');
33
- if (this.sentryServiceCfg.dsn) {
34
- // Sentry enabled
35
- console.log('SentryService init...');
36
- }
37
- sentry.init({
38
- maxValueLength: 2000, // default is 250 characters
39
- ...this.sentryServiceCfg,
40
- });
41
- return sentry;
42
- }
43
- /**
44
- * Currently not recommended, because it makes `void` requests throw user-facing errors.
45
- *
46
- * UPD: to be tested. Without it - request is not enriched and the error is less useful.
47
- */
48
- getRequestHandler() {
49
- return this.sentry().Handlers.requestHandler();
50
- }
51
- /**
52
- * Currently not recommended, as it's replaced by our custom sentryErrorHandler.
53
- *
54
- * @deprecated
55
- */
56
- getErrorHandler() {
57
- return this.sentry().Handlers.errorHandler();
29
+ this.sentry = sentryServiceCfg.sentry;
58
30
  }
59
31
  /**
60
32
  * For GDPR reasons we never send more information than just User ID.
61
33
  */
62
34
  setUserId(id) {
63
35
  if (id === null) {
64
- this.sentry().setUser(null);
36
+ this.sentry.setUser(null);
65
37
  return;
66
38
  }
67
- this.sentry().setUser({
39
+ this.sentry.setUser({
68
40
  id,
69
41
  });
70
42
  }
@@ -78,7 +50,7 @@ class SentrySharedService {
78
50
  * https://docs.sentry.io/platforms/node/enriching-events/scopes/
79
51
  */
80
52
  setTags(tags) {
81
- this.sentry().setTags(tags);
53
+ this.sentry.setTags(tags);
82
54
  }
83
55
  /**
84
56
  * Does console.log(err)
@@ -104,20 +76,20 @@ class SentrySharedService {
104
76
  // This is to avoid Sentry cutting err.message to 253 characters
105
77
  // It will log additional "breadcrumb object" before the error
106
78
  // It's a Breadcrumb, not a console.log, because console.log are NOT automatically attached as Breadcrumbs in cron-job environments (outside of Express)
107
- this.sentry().addBreadcrumb({
79
+ this.sentry.addBreadcrumb({
108
80
  message: (0, nodejs_lib_1._inspect)(err, INSPECT_OPT),
109
81
  });
110
- return this.sentry().captureException(err);
82
+ return this.sentry.captureException(err);
111
83
  }
112
84
  /**
113
85
  * Returns "eventId"
114
86
  */
115
87
  captureMessage(msg, level) {
116
88
  (0, index_1.getRequestLogger)()[sentrySeverityMap[level] || 'log']('captureMessage:', msg);
117
- return this.sentry().captureMessage(msg, level);
89
+ return this.sentry.captureMessage(msg, level);
118
90
  }
119
91
  addBreadcrumb(breadcrumb) {
120
- this.sentry().addBreadcrumb(breadcrumb);
92
+ this.sentry.addBreadcrumb(breadcrumb);
121
93
  }
122
94
  /**
123
95
  * Currently it will only use `logger.error` ("error" level) and ignore `log` and `warn`.
@@ -132,15 +104,12 @@ class SentrySharedService {
132
104
  warn: () => { }, // noop
133
105
  error: (...args) => {
134
106
  const message = args.map(arg => (0, nodejs_lib_1._inspect)(arg, INSPECT_OPT)).join(' ');
135
- this.sentry().addBreadcrumb({
107
+ this.sentry.addBreadcrumb({
136
108
  message,
137
109
  });
138
- this.sentry().captureException((0, js_lib_1._anyToError)(args.length === 1 ? args[0] : args));
110
+ this.sentry.captureException((0, js_lib_1._anyToError)(args.length === 1 ? args[0] : args));
139
111
  },
140
112
  };
141
113
  }
142
114
  }
143
115
  exports.SentrySharedService = SentrySharedService;
144
- tslib_1.__decorate([
145
- (0, js_lib_1._Memo)()
146
- ], SentrySharedService.prototype, "sentry", null);
@@ -24,12 +24,6 @@ function createDefaultApp(cfg) {
24
24
  if (!isTest) {
25
25
  app.use((0, asyncLocalStorageMiddleware_1.asyncLocalStorageMiddleware)());
26
26
  }
27
- // The request handler must be the first middleware on the app
28
- if (sentryService) {
29
- // On error - this handler will set res.headers,
30
- // which will trigger genericErrorHandler "headers already sent"
31
- app.use(sentryService.getRequestHandler());
32
- }
33
27
  app.use((0, __1.methodOverrideMiddleware)());
34
28
  app.use((0, requestTimeoutMiddleware_1.requestTimeoutMiddleware)());
35
29
  // app.use(serverStatsMiddleware()) // disabled by default
@@ -81,6 +75,9 @@ function createDefaultApp(cfg) {
81
75
  useHandlers(app, cfg.postHandlers);
82
76
  // Generic 404 handler
83
77
  app.use((0, notFoundMiddleware_1.notFoundMiddleware)());
78
+ // todo: test if it's needed!
79
+ // Add this after all routes, but before any and other error-handling middlewares are defined
80
+ // Sentry.setupExpressErrorHandler(app);
84
81
  // Generic error handler
85
82
  // It handles errors, returns proper status, does sentry.captureException(),
86
83
  // assigns err.data.errorId from sentry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
- "version": "5.17.2",
3
+ "version": "6.0.0",
4
4
  "scripts": {
5
5
  "prepare": "husky",
6
6
  "build": "dev-lib build",
@@ -18,7 +18,7 @@
18
18
  "deploy-health-check-debug2": "yarn tsn ./src/bin/deploy-health-check.ts --url https://api-master2.naturalcycles.com --thresholdUnhealthy 5"
19
19
  },
20
20
  "peerDependencies": {
21
- "@sentry/node": "^7"
21
+ "@sentry/node": "^8"
22
22
  },
23
23
  "dependencies": {
24
24
  "@naturalcycles/db-lib": "^9",
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@naturalcycles/bench-lib": "^3",
45
45
  "@naturalcycles/dev-lib": "^15",
46
- "@sentry/node": "^7",
46
+ "@sentry/node": "^8",
47
47
  "@types/ejs": "^3",
48
48
  "@types/node": "^22",
49
49
  "@types/yargs": "^16",
@@ -1,18 +1,19 @@
1
1
  import {
2
2
  _anyToError,
3
3
  _isErrorObject,
4
- _Memo,
5
4
  CommonLogger,
6
5
  CommonLogLevel,
7
6
  Primitive,
8
7
  StringMap,
9
8
  } from '@naturalcycles/js-lib'
10
9
  import { _inspect, InspectAnyOptions } from '@naturalcycles/nodejs-lib'
11
- import type { Breadcrumb, NodeOptions, SeverityLevel } from '@sentry/node'
10
+ import type { Breadcrumb, SeverityLevel } from '@sentry/node'
12
11
  import type * as SentryLib from '@sentry/node'
13
- import { BackendErrorRequestHandler, BackendRequestHandler, getRequestLogger } from '../index'
12
+ import { getRequestLogger } from '../index'
14
13
 
15
- export interface SentrySharedServiceCfg extends NodeOptions {}
14
+ export interface SentrySharedServiceCfg {
15
+ sentry: typeof SentryLib
16
+ }
16
17
 
17
18
  const sentrySeverityMap: Record<SeverityLevel, CommonLogLevel> = {
18
19
  debug: 'log',
@@ -28,62 +29,31 @@ const INSPECT_OPT: InspectAnyOptions = {
28
29
  includeErrorData: true,
29
30
  }
30
31
 
32
+ /**
33
+ * Recommended sentry configuration:
34
+ *
35
+ * {
36
+ * maxValueLength: 2000, // default is 250 characters
37
+ * }
38
+ *
39
+ */
31
40
  export class SentrySharedService {
32
- constructor(private sentryServiceCfg: SentrySharedServiceCfg) {}
33
-
34
- init(): void {
35
- this.sentry()
41
+ constructor(sentryServiceCfg: SentrySharedServiceCfg) {
42
+ this.sentry = sentryServiceCfg.sentry
36
43
  }
37
44
 
38
- @_Memo()
39
- sentry(): typeof SentryLib {
40
- // Lazy-loading `@sentry/node`
41
- // Reasons:
42
- // 1. Can be useful is this module is imported but never actually used
43
- // 2. Works around memory leak when used with Jest
44
- const sentry = require('@sentry/node') as typeof SentryLib
45
-
46
- if (this.sentryServiceCfg.dsn) {
47
- // Sentry enabled
48
- console.log('SentryService init...')
49
- }
50
-
51
- sentry.init({
52
- maxValueLength: 2000, // default is 250 characters
53
- ...this.sentryServiceCfg,
54
- })
55
-
56
- return sentry
57
- }
58
-
59
- /**
60
- * Currently not recommended, because it makes `void` requests throw user-facing errors.
61
- *
62
- * UPD: to be tested. Without it - request is not enriched and the error is less useful.
63
- */
64
- getRequestHandler(): BackendRequestHandler {
65
- return this.sentry().Handlers.requestHandler()
66
- }
67
-
68
- /**
69
- * Currently not recommended, as it's replaced by our custom sentryErrorHandler.
70
- *
71
- * @deprecated
72
- */
73
- getErrorHandler(): BackendErrorRequestHandler {
74
- return this.sentry().Handlers.errorHandler()
75
- }
45
+ sentry: typeof SentryLib
76
46
 
77
47
  /**
78
48
  * For GDPR reasons we never send more information than just User ID.
79
49
  */
80
50
  setUserId(id: string | null): void {
81
51
  if (id === null) {
82
- this.sentry().setUser(null)
52
+ this.sentry.setUser(null)
83
53
  return
84
54
  }
85
55
 
86
- this.sentry().setUser({
56
+ this.sentry.setUser({
87
57
  id,
88
58
  })
89
59
  }
@@ -98,7 +68,7 @@ export class SentrySharedService {
98
68
  * https://docs.sentry.io/platforms/node/enriching-events/scopes/
99
69
  */
100
70
  setTags(tags: StringMap<Primitive>): void {
101
- this.sentry().setTags(tags)
71
+ this.sentry.setTags(tags)
102
72
  }
103
73
 
104
74
  /**
@@ -131,11 +101,11 @@ export class SentrySharedService {
131
101
  // This is to avoid Sentry cutting err.message to 253 characters
132
102
  // It will log additional "breadcrumb object" before the error
133
103
  // It's a Breadcrumb, not a console.log, because console.log are NOT automatically attached as Breadcrumbs in cron-job environments (outside of Express)
134
- this.sentry().addBreadcrumb({
104
+ this.sentry.addBreadcrumb({
135
105
  message: _inspect(err, INSPECT_OPT),
136
106
  })
137
107
 
138
- return this.sentry().captureException(err)
108
+ return this.sentry.captureException(err)
139
109
  }
140
110
 
141
111
  /**
@@ -143,11 +113,11 @@ export class SentrySharedService {
143
113
  */
144
114
  captureMessage(msg: string, level?: SeverityLevel): string {
145
115
  getRequestLogger()[sentrySeverityMap[level!] || 'log']('captureMessage:', msg)
146
- return this.sentry().captureMessage(msg, level)
116
+ return this.sentry.captureMessage(msg, level)
147
117
  }
148
118
 
149
119
  addBreadcrumb(breadcrumb: Breadcrumb): void {
150
- this.sentry().addBreadcrumb(breadcrumb)
120
+ this.sentry.addBreadcrumb(breadcrumb)
151
121
  }
152
122
 
153
123
  /**
@@ -164,11 +134,11 @@ export class SentrySharedService {
164
134
  error: (...args) => {
165
135
  const message = args.map(arg => _inspect(arg, INSPECT_OPT)).join(' ')
166
136
 
167
- this.sentry().addBreadcrumb({
137
+ this.sentry.addBreadcrumb({
168
138
  message,
169
139
  })
170
140
 
171
- this.sentry().captureException(_anyToError(args.length === 1 ? args[0] : args))
141
+ this.sentry.captureException(_anyToError(args.length === 1 ? args[0] : args))
172
142
  },
173
143
  }
174
144
  }
@@ -34,13 +34,6 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
34
34
  app.use(asyncLocalStorageMiddleware())
35
35
  }
36
36
 
37
- // The request handler must be the first middleware on the app
38
- if (sentryService) {
39
- // On error - this handler will set res.headers,
40
- // which will trigger genericErrorHandler "headers already sent"
41
- app.use(sentryService.getRequestHandler())
42
- }
43
-
44
37
  app.use(methodOverrideMiddleware())
45
38
  app.use(requestTimeoutMiddleware())
46
39
  // app.use(serverStatsMiddleware()) // disabled by default
@@ -117,6 +110,10 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
117
110
  // Generic 404 handler
118
111
  app.use(notFoundMiddleware())
119
112
 
113
+ // todo: test if it's needed!
114
+ // Add this after all routes, but before any and other error-handling middlewares are defined
115
+ // Sentry.setupExpressErrorHandler(app);
116
+
120
117
  // Generic error handler
121
118
  // It handles errors, returns proper status, does sentry.captureException(),
122
119
  // assigns err.data.errorId from sentry