@naturalcycles/backend-lib 9.4.0 → 9.5.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.
- package/dist/sentry/sentry.shared.service.d.ts +1 -1
- package/dist/sentry/sentry.shared.service.js +3 -5
- package/dist/server/genericErrorMiddleware.js +15 -7
- package/dist/server/logMiddleware.js +3 -1
- package/dist/server/requestLoggerMiddleware.d.ts +7 -1
- package/dist/server/requestLoggerMiddleware.js +17 -4
- package/dist/server/startServer.js +12 -4
- package/package.json +1 -1
- package/src/sentry/sentry.shared.service.ts +3 -5
- package/src/server/genericErrorMiddleware.ts +16 -8
- package/src/server/logMiddleware.ts +5 -3
- package/src/server/requestLoggerMiddleware.ts +29 -7
- package/src/server/startServer.ts +10 -4
|
@@ -33,7 +33,7 @@ export declare class SentrySharedService {
|
|
|
33
33
|
* Does console.log(err)
|
|
34
34
|
* Returns "eventId" or undefined (if error was not reported).
|
|
35
35
|
*/
|
|
36
|
-
captureException(err_: any
|
|
36
|
+
captureException(err_: any): string | undefined;
|
|
37
37
|
/**
|
|
38
38
|
* Returns "eventId"
|
|
39
39
|
*/
|
|
@@ -54,15 +54,13 @@ export class SentrySharedService {
|
|
|
54
54
|
* Does console.log(err)
|
|
55
55
|
* Returns "eventId" or undefined (if error was not reported).
|
|
56
56
|
*/
|
|
57
|
-
captureException(err_
|
|
57
|
+
captureException(err_) {
|
|
58
58
|
// normalize the error
|
|
59
59
|
const err = _anyToError(err_);
|
|
60
60
|
const data = _isErrorObject(err) ? err.data : undefined;
|
|
61
61
|
// Using request-aware logger here
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
getRequestLogger().error('captureException:', ...[err_, data].filter(Boolean));
|
|
65
|
-
}
|
|
62
|
+
// Log both the error and attached ErrorData (if any)
|
|
63
|
+
getRequestLogger().error('captureException:', ...[err_, data].filter(Boolean));
|
|
66
64
|
if (data?.report === false) {
|
|
67
65
|
// Skip reporting the error
|
|
68
66
|
return;
|
|
@@ -30,16 +30,24 @@ export function genericErrorMiddleware(cfg = {}) {
|
|
|
30
30
|
}
|
|
31
31
|
export function respondWithError(req, res, err) {
|
|
32
32
|
const { headersSent } = res;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
33
|
+
const originalError = _anyToError(err);
|
|
34
|
+
let errorId;
|
|
35
|
+
if (errorService) {
|
|
36
|
+
// captureException logs the error,
|
|
37
|
+
// so we don't need to log it here
|
|
38
|
+
errorId = errorService.captureException(originalError);
|
|
37
39
|
}
|
|
38
40
|
else {
|
|
39
|
-
|
|
41
|
+
// because errorService was not provided - we are going to log the error here
|
|
42
|
+
if (headersSent) {
|
|
43
|
+
req.error(`error after headersSent:`, err);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
req.error(err);
|
|
47
|
+
}
|
|
48
|
+
// todo: add endpoint to the log
|
|
49
|
+
// todo: add userId from the "Context" (or, just req.userId?) to the log
|
|
40
50
|
}
|
|
41
|
-
const originalError = _anyToError(err);
|
|
42
|
-
const errorId = errorService?.captureException(originalError);
|
|
43
51
|
if (headersSent)
|
|
44
52
|
return;
|
|
45
53
|
const httpError = _errorLikeToErrorObject(originalError);
|
|
@@ -61,7 +61,9 @@ export function logMiddleware() {
|
|
|
61
61
|
const meta = {
|
|
62
62
|
// Experimental!
|
|
63
63
|
// Testing to include userId in metadata (not message payload) to see if it's searchable
|
|
64
|
-
|
|
64
|
+
labels: {
|
|
65
|
+
userId: req.userId,
|
|
66
|
+
},
|
|
65
67
|
};
|
|
66
68
|
// CloudRun does NOT have this env variable set,
|
|
67
69
|
// so you have to set it manually on deployment, like this:
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { BackendRequestHandler } from '../index.js';
|
|
2
|
+
export interface RequestLoggerMiddlewareCfg {
|
|
3
|
+
/**
|
|
4
|
+
* If set - this prefix will be removed from the request url before logging.
|
|
5
|
+
*/
|
|
6
|
+
removeUrlPrefix?: string;
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* Experimental request logger for Cloud Run.
|
|
4
10
|
*
|
|
5
11
|
* @experimental
|
|
6
12
|
*/
|
|
7
|
-
export declare function requestLoggerMiddleware(): BackendRequestHandler;
|
|
13
|
+
export declare function requestLoggerMiddleware(cfg?: RequestLoggerMiddlewareCfg): BackendRequestHandler;
|
|
@@ -5,14 +5,27 @@ import { onFinished } from '../index.js';
|
|
|
5
5
|
*
|
|
6
6
|
* @experimental
|
|
7
7
|
*/
|
|
8
|
-
export function requestLoggerMiddleware() {
|
|
8
|
+
export function requestLoggerMiddleware(cfg = {}) {
|
|
9
|
+
const { removeUrlPrefix } = cfg;
|
|
10
|
+
const removeUrlPrefixLength = removeUrlPrefix?.length;
|
|
9
11
|
return (req, res, next) => {
|
|
10
12
|
const started = Date.now();
|
|
11
|
-
|
|
13
|
+
let url = req.originalUrl.split('?')[0];
|
|
14
|
+
if (removeUrlPrefix && url.startsWith(removeUrlPrefix)) {
|
|
15
|
+
url = url.slice(removeUrlPrefixLength);
|
|
16
|
+
}
|
|
17
|
+
// todo: include requestId (3-character hash of it?)
|
|
18
|
+
req.log(['>>', req.method, url, req.userId].filter(Boolean).join(' '));
|
|
12
19
|
onFinished(res, () => {
|
|
13
|
-
|
|
20
|
+
const str = ['<<', res.statusCode, _since(started), req.method, url, req.userId]
|
|
14
21
|
.filter(Boolean)
|
|
15
|
-
.join(' ')
|
|
22
|
+
.join(' ');
|
|
23
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
24
|
+
req.error(str);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
req.log(str);
|
|
28
|
+
}
|
|
16
29
|
});
|
|
17
30
|
next();
|
|
18
31
|
};
|
|
@@ -16,12 +16,20 @@ export class BackendServer {
|
|
|
16
16
|
// 1. Register error handlers, etc.
|
|
17
17
|
if (registerUncaughtExceptionHandlers) {
|
|
18
18
|
process.on('uncaughtException', err => {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (this.cfg.sentryService) {
|
|
20
|
+
this.cfg.sentryService.captureException(err);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.error('BackendServer uncaughtException:', err);
|
|
24
|
+
}
|
|
21
25
|
});
|
|
22
26
|
process.on('unhandledRejection', err => {
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
if (this.cfg.sentryService) {
|
|
28
|
+
this.cfg.sentryService.captureException(err);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error('BackendServer unhandledRejection:', err);
|
|
32
|
+
}
|
|
25
33
|
});
|
|
26
34
|
}
|
|
27
35
|
process.once('SIGINT', () => this.stop('SIGINT'));
|
package/package.json
CHANGED
|
@@ -70,16 +70,14 @@ export class SentrySharedService {
|
|
|
70
70
|
* Does console.log(err)
|
|
71
71
|
* Returns "eventId" or undefined (if error was not reported).
|
|
72
72
|
*/
|
|
73
|
-
captureException(err_: any
|
|
73
|
+
captureException(err_: any): string | undefined {
|
|
74
74
|
// normalize the error
|
|
75
75
|
const err = _anyToError(err_)
|
|
76
76
|
const data = _isErrorObject(err) ? err.data : undefined
|
|
77
77
|
|
|
78
78
|
// Using request-aware logger here
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
getRequestLogger().error('captureException:', ...[err_, data].filter(Boolean))
|
|
82
|
-
}
|
|
79
|
+
// Log both the error and attached ErrorData (if any)
|
|
80
|
+
getRequestLogger().error('captureException:', ...[err_, data].filter(Boolean))
|
|
83
81
|
|
|
84
82
|
if (data?.report === false) {
|
|
85
83
|
// Skip reporting the error
|
|
@@ -67,17 +67,25 @@ export function genericErrorMiddleware(
|
|
|
67
67
|
export function respondWithError(req: BackendRequest, res: BackendResponse, err: any): void {
|
|
68
68
|
const { headersSent } = res
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
const originalError = _anyToError(err)
|
|
71
|
+
|
|
72
|
+
let errorId: string | undefined
|
|
73
|
+
if (errorService) {
|
|
74
|
+
// captureException logs the error,
|
|
75
|
+
// so we don't need to log it here
|
|
76
|
+
errorId = errorService.captureException(originalError)
|
|
74
77
|
} else {
|
|
75
|
-
|
|
76
|
-
}
|
|
78
|
+
// because errorService was not provided - we are going to log the error here
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
if (headersSent) {
|
|
81
|
+
req.error(`error after headersSent:`, err)
|
|
82
|
+
} else {
|
|
83
|
+
req.error(err)
|
|
84
|
+
}
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
// todo: add endpoint to the log
|
|
87
|
+
// todo: add userId from the "Context" (or, just req.userId?) to the log
|
|
88
|
+
}
|
|
81
89
|
|
|
82
90
|
if (headersSent) return
|
|
83
91
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inspect } from 'node:util'
|
|
2
|
-
import type { AnyObject, CommonLogger
|
|
2
|
+
import type { AnyObject, CommonLogger } from '@naturalcycles/js-lib'
|
|
3
3
|
import { _inspect, dimGrey } from '@naturalcycles/nodejs-lib'
|
|
4
4
|
import type { BackendRequestHandler } from './server.model.js'
|
|
5
5
|
|
|
@@ -73,10 +73,12 @@ function logToCI(args: any[]): void {
|
|
|
73
73
|
export function logMiddleware(): BackendRequestHandler {
|
|
74
74
|
if (isGAE || isCloudRun) {
|
|
75
75
|
return function gcpStructuredLogHandler(req, _res, next) {
|
|
76
|
-
const meta:
|
|
76
|
+
const meta: AnyObject = {
|
|
77
77
|
// Experimental!
|
|
78
78
|
// Testing to include userId in metadata (not message payload) to see if it's searchable
|
|
79
|
-
|
|
79
|
+
labels: {
|
|
80
|
+
userId: req.userId,
|
|
81
|
+
},
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
// CloudRun does NOT have this env variable set,
|
|
@@ -3,23 +3,45 @@ import { _since } from '@naturalcycles/js-lib'
|
|
|
3
3
|
import type { BackendRequestHandler } from '../index.js'
|
|
4
4
|
import { onFinished } from '../index.js'
|
|
5
5
|
|
|
6
|
+
export interface RequestLoggerMiddlewareCfg {
|
|
7
|
+
/**
|
|
8
|
+
* If set - this prefix will be removed from the request url before logging.
|
|
9
|
+
*/
|
|
10
|
+
removeUrlPrefix?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
/**
|
|
7
14
|
* Experimental request logger for Cloud Run.
|
|
8
15
|
*
|
|
9
16
|
* @experimental
|
|
10
17
|
*/
|
|
11
|
-
export function requestLoggerMiddleware(
|
|
18
|
+
export function requestLoggerMiddleware(
|
|
19
|
+
cfg: RequestLoggerMiddlewareCfg = {},
|
|
20
|
+
): BackendRequestHandler {
|
|
21
|
+
const { removeUrlPrefix } = cfg
|
|
22
|
+
const removeUrlPrefixLength = removeUrlPrefix?.length
|
|
23
|
+
|
|
12
24
|
return (req, res, next) => {
|
|
13
25
|
const started = Date.now() as UnixTimestampMillis
|
|
14
26
|
|
|
15
|
-
|
|
27
|
+
let url = req.originalUrl.split('?')[0]!
|
|
28
|
+
if (removeUrlPrefix && url.startsWith(removeUrlPrefix)) {
|
|
29
|
+
url = url.slice(removeUrlPrefixLength)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// todo: include requestId (3-character hash of it?)
|
|
33
|
+
req.log(['>>', req.method, url, req.userId].filter(Boolean).join(' '))
|
|
16
34
|
|
|
17
35
|
onFinished(res, () => {
|
|
18
|
-
req.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
36
|
+
const str = ['<<', res.statusCode, _since(started), req.method, url, req.userId]
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join(' ')
|
|
39
|
+
|
|
40
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
41
|
+
req.error(str)
|
|
42
|
+
} else {
|
|
43
|
+
req.log(str)
|
|
44
|
+
}
|
|
23
45
|
})
|
|
24
46
|
|
|
25
47
|
next()
|
|
@@ -19,13 +19,19 @@ export class BackendServer {
|
|
|
19
19
|
// 1. Register error handlers, etc.
|
|
20
20
|
if (registerUncaughtExceptionHandlers) {
|
|
21
21
|
process.on('uncaughtException', err => {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if (this.cfg.sentryService) {
|
|
23
|
+
this.cfg.sentryService.captureException(err)
|
|
24
|
+
} else {
|
|
25
|
+
console.error('BackendServer uncaughtException:', err)
|
|
26
|
+
}
|
|
24
27
|
})
|
|
25
28
|
|
|
26
29
|
process.on('unhandledRejection', err => {
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
if (this.cfg.sentryService) {
|
|
31
|
+
this.cfg.sentryService.captureException(err)
|
|
32
|
+
} else {
|
|
33
|
+
console.error('BackendServer unhandledRejection:', err)
|
|
34
|
+
}
|
|
29
35
|
})
|
|
30
36
|
}
|
|
31
37
|
|