@ogcio/fastify-logging-wrapper 4.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.
@@ -0,0 +1,168 @@
1
+ import { Level } from "pino";
2
+ import { getLoggerConfiguration } from "../src/logging-wrapper.js";
3
+ import { buildLogger } from "./helpers/build-logger.js";
4
+ import { hostname } from "os";
5
+ import { REDACTED_VALUE } from "../src/logging-wrapper-entities.js";
6
+ import assert from "node:assert";
7
+ import { test } from "node:test";
8
+
9
+ const getRandomFieldValue = () => Math.random().toString(36).slice(2);
10
+
11
+ const toRedactFields = {
12
+ input_value: {
13
+ request: {
14
+ "x-api-key": "non redacted because not under headers",
15
+ headers: {
16
+ "x-amz-security-token": getRandomFieldValue(),
17
+ "x-api-key": getRandomFieldValue(),
18
+ authorization: getRandomFieldValue(),
19
+ cookie: getRandomFieldValue(),
20
+ "set-cookie": getRandomFieldValue(),
21
+ "proxy-authorization": getRandomFieldValue(),
22
+ non_to_redact: "non_redacted_value",
23
+ },
24
+ },
25
+ response: {
26
+ headers: {
27
+ "x-amz-security-token": getRandomFieldValue(),
28
+ "x-api-key": getRandomFieldValue(),
29
+ authorization: getRandomFieldValue(),
30
+ cookie: getRandomFieldValue(),
31
+ "set-cookie": getRandomFieldValue(),
32
+ "proxy-authorization": getRandomFieldValue(),
33
+ non_to_redact: "non_redacted_value",
34
+ },
35
+ },
36
+ },
37
+ expected_output: {
38
+ request: {
39
+ "x-api-key": "non redacted because not under headers",
40
+ headers: {
41
+ "x-amz-security-token": REDACTED_VALUE,
42
+ "x-api-key": REDACTED_VALUE,
43
+ authorization: REDACTED_VALUE,
44
+ cookie: REDACTED_VALUE,
45
+ "set-cookie": REDACTED_VALUE,
46
+ "proxy-authorization": REDACTED_VALUE,
47
+ non_to_redact: "non_redacted_value",
48
+ },
49
+ },
50
+ response: {
51
+ headers: {
52
+ "x-amz-security-token": REDACTED_VALUE,
53
+ "x-api-key": REDACTED_VALUE,
54
+ authorization: REDACTED_VALUE,
55
+ cookie: REDACTED_VALUE,
56
+ "set-cookie": REDACTED_VALUE,
57
+ "proxy-authorization": REDACTED_VALUE,
58
+ non_to_redact: "non_redacted_value",
59
+ },
60
+ },
61
+ },
62
+ };
63
+
64
+ const methodsDataProvider = [
65
+ {
66
+ method: "trace",
67
+ expected: {
68
+ level: 10,
69
+ level_name: "TRACE",
70
+ },
71
+ },
72
+ {
73
+ method: "debug",
74
+ expected: {
75
+ level: 20,
76
+ level_name: "DEBUG",
77
+ },
78
+ },
79
+ {
80
+ method: "info",
81
+ expected: {
82
+ level: 30,
83
+ level_name: "INFO",
84
+ },
85
+ },
86
+ {
87
+ method: "warn",
88
+ expected: {
89
+ level: 40,
90
+ level_name: "WARN",
91
+ },
92
+ },
93
+ {
94
+ method: "error",
95
+ expected: {
96
+ level: 50,
97
+ level_name: "ERROR",
98
+ },
99
+ },
100
+ {
101
+ method: "fatal",
102
+ expected: {
103
+ level: 60,
104
+ level_name: "FATAL",
105
+ },
106
+ },
107
+ ];
108
+
109
+ test("Basic format is the expected one", async (t) => {
110
+ const { logger, loggedRecordsMethod } = buildLogger({
111
+ ...getLoggerConfiguration("debug"),
112
+ });
113
+ logger.debug("test message");
114
+ logger.info("another message");
115
+
116
+ const loggedRecords = loggedRecordsMethod();
117
+ assert.strictEqual(loggedRecords.length, 2);
118
+
119
+ const parsed = JSON.parse(loggedRecords[0]);
120
+ assert.strictEqual(typeof parsed.timestamp, "number");
121
+ assert.ok(
122
+ parsed.timestamp > Date.now() - 2000,
123
+ "the timestamp must be newer than 2 seconds ago",
124
+ );
125
+ delete parsed.timestamp;
126
+ assert.deepStrictEqual(parsed, {
127
+ level: 20,
128
+ level_name: "DEBUG",
129
+ hostname: hostname(),
130
+ message: "test message",
131
+ });
132
+ });
133
+
134
+ test("Fields are redacted as expected", async (t) => {
135
+ const { logger, loggedRecordsMethod } = buildLogger({
136
+ ...getLoggerConfiguration(),
137
+ });
138
+ logger.warn(toRedactFields.input_value);
139
+
140
+ const loggedRecords = loggedRecordsMethod();
141
+ const parsed = JSON.parse(loggedRecords[0]);
142
+ delete parsed.hostname;
143
+ delete parsed.level;
144
+ delete parsed.level_name;
145
+ delete parsed.timestamp;
146
+
147
+ assert.deepStrictEqual(parsed, toRedactFields.expected_output);
148
+ });
149
+
150
+ methodsDataProvider.forEach((methodDataProvider) =>
151
+ test(`Methods are writing correct levels - ${methodDataProvider.method}`, async (t) => {
152
+ const { logger, loggedRecordsMethod } = buildLogger({
153
+ ...getLoggerConfiguration("trace"),
154
+ });
155
+
156
+ logger[methodDataProvider.method]("test");
157
+
158
+ const loggedRecords = loggedRecordsMethod();
159
+ assert.strictEqual(loggedRecords.length, 1);
160
+ const parsed = JSON.parse(loggedRecords[0]);
161
+
162
+ assert.strictEqual(parsed.level, methodDataProvider.expected.level);
163
+ assert.strictEqual(
164
+ parsed.level_name,
165
+ methodDataProvider.expected.level_name,
166
+ );
167
+ }),
168
+ );
@@ -0,0 +1,5 @@
1
+ import { FastifyServerOptions, FastifyInstance } from "fastify";
2
+ import { DestinationStream } from "pino";
3
+ export declare const initializeLoggingHooks: (server: FastifyInstance) => void;
4
+ export declare const getLoggingConfiguration: (loggerDestination?: DestinationStream) => FastifyServerOptions;
5
+ //# sourceMappingURL=fastify-logging-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify-logging-wrapper.d.ts","sourceRoot":"","sources":["../src/fastify-logging-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAchE,OAAO,EAAQ,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAK/C,eAAO,MAAM,sBAAsB,WAAY,eAAe,KAAG,IA6BhE,CAAC;AAEF,eAAO,MAAM,uBAAuB,uBACd,iBAAiB,KACpC,oBAMD,CAAC"}
@@ -0,0 +1,34 @@
1
+ import hyperid from "hyperid";
2
+ import { LogMessages, REQUEST_ID_LOG_LABEL, } from "./logging-wrapper-entities.js";
3
+ import { getLoggerConfiguration, getLoggingContextError, getPartialLoggingContextError, parseFullLoggingRequest, resetLoggingContext, setLoggingContext, } from "./logging-wrapper.js";
4
+ import { pino } from "pino";
5
+ import { REQUEST_ID_HEADER } from "@ogcio/shared-errors";
6
+ const hyperidInstance = hyperid({ fixedLength: true, urlSafe: true });
7
+ export const initializeLoggingHooks = (server) => {
8
+ server.addHook("preHandler", (request, _reply, done) => {
9
+ setLoggingContext({ request });
10
+ request.log.info({ request: parseFullLoggingRequest(request) }, LogMessages.NewRequest);
11
+ done();
12
+ });
13
+ server.addHook("onResponse", (_req, reply, done) => {
14
+ setLoggingContext({ response: reply });
15
+ reply.log.info(LogMessages.Response);
16
+ // Include error in API Track if exists
17
+ reply.log.info({ error: getPartialLoggingContextError() }, LogMessages.ApiTrack);
18
+ resetLoggingContext();
19
+ done();
20
+ });
21
+ server.addHook("onError", (request, _reply, error, done) => {
22
+ setLoggingContext({ error });
23
+ request.log.error({ error: getLoggingContextError() }, LogMessages.Error);
24
+ done();
25
+ });
26
+ };
27
+ export const getLoggingConfiguration = (loggerDestination) => ({
28
+ logger: pino(getLoggerConfiguration(), loggerDestination),
29
+ disableRequestLogging: true,
30
+ genReqId: () => hyperidInstance(),
31
+ requestIdLogLabel: REQUEST_ID_LOG_LABEL,
32
+ requestIdHeader: REQUEST_ID_HEADER,
33
+ });
34
+ //# sourceMappingURL=fastify-logging-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify-logging-wrapper.js","sourceRoot":"","sources":["../src/fastify-logging-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,WAAW,EACX,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,eAAe,GAAG,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,MAAuB,EAAQ,EAAE;IACtE,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QACrD,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,OAAO,EAAE,uBAAuB,CAAC,OAAO,CAAC,EAAE,EAC7C,WAAW,CAAC,UAAU,CACvB,CAAC;QACF,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACjD,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACvC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACrC,uCAAuC;QACvC,KAAK,CAAC,GAAG,CAAC,IAAI,CACZ,EAAE,KAAK,EAAE,6BAA6B,EAAE,EAAE,EAC1C,WAAW,CAAC,QAAQ,CACrB,CAAC;QACF,mBAAmB,EAAE,CAAC;QACtB,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACzD,iBAAiB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAE7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;QAE1E,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,iBAAqC,EACf,EAAE,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,CAAC,sBAAsB,EAAE,EAAE,iBAAiB,CAAC;IACzD,qBAAqB,EAAE,IAAI;IAC3B,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,EAAE;IACjC,iBAAiB,EAAE,oBAAoB;IACvC,eAAe,EAAE,iBAAiB;CACnC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { getLoggingConfiguration, initializeLoggingHooks, } from "./fastify-logging-wrapper.js";
2
+ export { toLoggingError, LogMessages, LoggingError, } from "./logging-wrapper-entities.js";
3
+ export { getLoggingContextError, setLoggingContext } from "./logging-wrapper.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,cAAc,EACd,WAAW,EACX,YAAY,GACb,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { getLoggingConfiguration, initializeLoggingHooks, } from "./fastify-logging-wrapper.js";
2
+ export { toLoggingError, LogMessages, } from "./logging-wrapper-entities.js";
3
+ export { getLoggingContextError, setLoggingContext } from "./logging-wrapper.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,cAAc,EACd,WAAW,GAEZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { FastifyError } from "fastify";
2
+ import { HttpError } from "@fastify/sensible";
3
+ export interface LoggingRequest {
4
+ scheme: string;
5
+ method: string;
6
+ path: string | undefined;
7
+ hostname: string;
8
+ query_params: unknown;
9
+ [key: string]: unknown;
10
+ }
11
+ export interface FullLoggingRequest extends LoggingRequest {
12
+ headers: unknown;
13
+ user_agent: string | undefined;
14
+ client_ip: string;
15
+ }
16
+ export interface LoggingResponse {
17
+ headers: unknown;
18
+ status_code: number;
19
+ [key: string]: unknown;
20
+ }
21
+ export interface LoggingError {
22
+ class: LogErrorClasses;
23
+ message: string;
24
+ trace?: string;
25
+ parent?: {
26
+ name: string;
27
+ message: string;
28
+ stack?: string;
29
+ };
30
+ [key: string]: unknown;
31
+ }
32
+ export interface LoggingContext {
33
+ request?: LoggingRequest;
34
+ response?: LoggingResponse;
35
+ error?: LoggingError;
36
+ }
37
+ export declare enum LogMessages {
38
+ NewRequest = "NEW_REQUEST",
39
+ Response = "RESPONSE",
40
+ Error = "ERROR",
41
+ ApiTrack = "API_TRACK"
42
+ }
43
+ export declare enum LogErrorClasses {
44
+ ServerError = "SERVER_ERROR",
45
+ ValidationError = "VALIDATION_ERROR",
46
+ RequestError = "REQUEST_ERROR",
47
+ GatewayError = "GATEWAY_ERROR",
48
+ UnknownError = "UNKNOWN_ERROR"
49
+ }
50
+ export declare const REDACTED_VALUE = "[redacted]";
51
+ export declare const REDACTED_PATHS: string[];
52
+ export declare const MESSAGE_KEY = "message";
53
+ export declare const REQUEST_ID_LOG_LABEL = "request_id";
54
+ export declare const toLoggingError: (error: HttpError | FastifyError) => LoggingError;
55
+ //# sourceMappingURL=logging-wrapper-entities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-wrapper-entities.d.ts","sourceRoot":"","sources":["../src/logging-wrapper-entities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIvC,OAAO,EAAE,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,oBAAY,WAAW;IACrB,UAAU,gBAAgB;IAC1B,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,QAAQ,cAAc;CACvB;AAED,oBAAY,eAAe;IACzB,WAAW,iBAAiB;IAC5B,eAAe,qBAAqB;IACpC,YAAY,kBAAkB;IAC9B,YAAY,kBAAkB;IAC9B,YAAY,kBAAkB;CAC/B;AAED,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,eAAO,MAAM,cAAc,UAO1B,CAAC;AAEF,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAIjD,eAAO,MAAM,cAAc,UAClB,SAAS,GAAG,YAAY,KAC9B,YAyBF,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { parseErrorForLogging, } from "@ogcio/shared-errors";
2
+ import { isHttpError } from "http-errors";
3
+ export var LogMessages;
4
+ (function (LogMessages) {
5
+ LogMessages["NewRequest"] = "NEW_REQUEST";
6
+ LogMessages["Response"] = "RESPONSE";
7
+ LogMessages["Error"] = "ERROR";
8
+ LogMessages["ApiTrack"] = "API_TRACK";
9
+ })(LogMessages || (LogMessages = {}));
10
+ export var LogErrorClasses;
11
+ (function (LogErrorClasses) {
12
+ LogErrorClasses["ServerError"] = "SERVER_ERROR";
13
+ LogErrorClasses["ValidationError"] = "VALIDATION_ERROR";
14
+ LogErrorClasses["RequestError"] = "REQUEST_ERROR";
15
+ LogErrorClasses["GatewayError"] = "GATEWAY_ERROR";
16
+ LogErrorClasses["UnknownError"] = "UNKNOWN_ERROR";
17
+ })(LogErrorClasses || (LogErrorClasses = {}));
18
+ export const REDACTED_VALUE = "[redacted]";
19
+ export const REDACTED_PATHS = [
20
+ '*.headers["x-amz-security-token"]',
21
+ '*.headers["x-api-key"]',
22
+ '*.headers["authorization"]',
23
+ '*.headers["cookie"]',
24
+ '*.headers["set-cookie"]',
25
+ '*.headers["proxy-authorization"]',
26
+ ];
27
+ export const MESSAGE_KEY = "message";
28
+ export const REQUEST_ID_LOG_LABEL = "request_id";
29
+ const UNHANDLED_EXCEPTION_CODE = "UNHANDLED_EXCEPTION";
30
+ export const toLoggingError = (error) => {
31
+ const output = {
32
+ class: parseErrorClass(error),
33
+ message: error.message,
34
+ trace: error.stack,
35
+ };
36
+ if (isHttpError(error)) {
37
+ const parentInput = error.parentError ?? error.parent;
38
+ const parent = parentInput
39
+ ? { parent: parseErrorForLogging(parentInput) }
40
+ : {};
41
+ return {
42
+ ...output,
43
+ code: error.name,
44
+ process: error.errorProcess,
45
+ ...parent,
46
+ };
47
+ }
48
+ return {
49
+ ...output,
50
+ code: error.code ?? UNHANDLED_EXCEPTION_CODE,
51
+ };
52
+ };
53
+ const parseErrorClass = (error) => {
54
+ if (!error.statusCode) {
55
+ return LogErrorClasses.UnknownError;
56
+ }
57
+ const statusCode = Number(error.statusCode);
58
+ if (statusCode === 502) {
59
+ return LogErrorClasses.GatewayError;
60
+ }
61
+ if (statusCode >= 500) {
62
+ return LogErrorClasses.ServerError;
63
+ }
64
+ if (statusCode === 422) {
65
+ return LogErrorClasses.ValidationError;
66
+ }
67
+ if (statusCode >= 400) {
68
+ return LogErrorClasses.RequestError;
69
+ }
70
+ return LogErrorClasses.UnknownError;
71
+ };
72
+ //# sourceMappingURL=logging-wrapper-entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-wrapper-entities.js","sourceRoot":"","sources":["../src/logging-wrapper-entities.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAqCxC,MAAM,CAAN,IAAY,WAKX;AALD,WAAY,WAAW;IACrB,yCAA0B,CAAA;IAC1B,oCAAqB,CAAA;IACrB,8BAAe,CAAA;IACf,qCAAsB,CAAA;AACxB,CAAC,EALW,WAAW,KAAX,WAAW,QAKtB;AAED,MAAM,CAAN,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,+CAA4B,CAAA;IAC5B,uDAAoC,CAAA;IACpC,iDAA8B,CAAA;IAC9B,iDAA8B,CAAA;IAC9B,iDAA8B,CAAA;AAChC,CAAC,EANW,eAAe,KAAf,eAAe,QAM1B;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAE3C,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,mCAAmC;IACnC,wBAAwB;IACxB,4BAA4B;IAC5B,qBAAqB;IACrB,yBAAyB;IACzB,kCAAkC;CACnC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEjD,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAEvD,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,KAA+B,EACjB,EAAE;IAChB,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC;QAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC;IAEF,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC;QACtD,MAAM,MAAM,GAAG,WAAW;YACxB,CAAC,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,WAAW,CAAC,EAAE;YAC/C,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO;YACL,GAAG,MAAM;YACT,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,YAAY;YAC3B,GAAG,MAAM;SACV,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,MAAM;QACT,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,wBAAwB;KAC7C,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACtB,KAA+B,EACd,EAAE;IACnB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,eAAe,CAAC,YAAY,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,eAAe,CAAC,YAAY,CAAC;IACtC,CAAC;IAED,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;QACtB,OAAO,eAAe,CAAC,WAAW,CAAC;IACrC,CAAC;IAED,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,eAAe,CAAC,eAAe,CAAC;IACzC,CAAC;IAED,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;QACtB,OAAO,eAAe,CAAC,YAAY,CAAC;IACtC,CAAC;IAED,OAAO,eAAe,CAAC,YAAY,CAAC;AACtC,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { FastifyRequest, FastifyReply, FastifyError } from "fastify";
2
+ import { LoggingContext, FullLoggingRequest, LoggingError } from "./logging-wrapper-entities.js";
3
+ import { LogLevel, PinoLoggerOptions } from "fastify/types/logger.js";
4
+ import { HttpError } from "@fastify/sensible";
5
+ type INPUT_ERROR_TYPES = FastifyError | HttpError;
6
+ export declare const getLoggingContext: (params: {
7
+ includeError: boolean;
8
+ }) => LoggingContext;
9
+ export declare const setLoggingContext: (params: {
10
+ request?: FastifyRequest;
11
+ response?: FastifyReply;
12
+ error?: INPUT_ERROR_TYPES;
13
+ }) => void;
14
+ export declare const resetLoggingContext: () => void;
15
+ export declare const getLoggingContextError: () => LoggingError | undefined;
16
+ export declare const getPartialLoggingContextError: () => Omit<LoggingError, "trace"> | undefined;
17
+ export declare const parseFullLoggingRequest: (req: FastifyRequest) => FullLoggingRequest;
18
+ export declare const getLoggerConfiguration: (minimumLevel?: LogLevel) => PinoLoggerOptions;
19
+ export {};
20
+ //# sourceMappingURL=logging-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-wrapper.d.ts","sourceRoot":"","sources":["../src/logging-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAErE,OAAO,EACL,cAAc,EAEd,kBAAkB,EAElB,YAAY,EAKb,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAI9C,KAAK,iBAAiB,GAAG,YAAY,GAAG,SAAS,CAAC;AAElD,eAAO,MAAM,iBAAiB,WAAY;IACxC,YAAY,EAAE,OAAO,CAAC;CACvB,KAAG,cAGyC,CAAC;AAE9C,eAAO,MAAM,iBAAiB,WAAY;IACxC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B,KAAG,IAUH,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,IAItC,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAO,YAAY,GAAG,SACR,CAAC;AAElD,eAAO,MAAM,6BAA6B,QACtC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,GAC3B,SAGF,CAAC;AAaH,eAAO,MAAM,uBAAuB,QAC7B,cAAc,KAClB,kBAKD,CAAC;AAOH,eAAO,MAAM,sBAAsB,kBACnB,QAAQ,KACrB,iBAmBD,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { hostname } from "os";
2
+ import { REDACTED_VALUE, REDACTED_PATHS, MESSAGE_KEY, toLoggingError, } from "./logging-wrapper-entities.js";
3
+ const loggingContext = {};
4
+ export const getLoggingContext = (params) => params.includeError
5
+ ? loggingContext
6
+ : { ...loggingContext, error: undefined };
7
+ export const setLoggingContext = (params) => {
8
+ if (params.request !== undefined) {
9
+ loggingContext.request = parseLoggingRequest(params.request);
10
+ }
11
+ if (params.response !== undefined) {
12
+ loggingContext.response = parseLoggingResponse(params.response);
13
+ }
14
+ if (params.error !== undefined) {
15
+ loggingContext.error = toLoggingError(params.error);
16
+ }
17
+ };
18
+ export const resetLoggingContext = () => {
19
+ loggingContext.request = undefined;
20
+ loggingContext.response = undefined;
21
+ loggingContext.error = undefined;
22
+ };
23
+ export const getLoggingContextError = () => getLoggingContext({ includeError: true }).error;
24
+ export const getPartialLoggingContextError = () => ({
25
+ ...(getLoggingContext({ includeError: true }).error ?? {}),
26
+ trace: undefined,
27
+ });
28
+ const getPathWithoutParams = (req) => req.routeOptions?.url ?? req.url.split("?")[0];
29
+ const parseLoggingRequest = (req) => ({
30
+ scheme: req.protocol,
31
+ method: req.method,
32
+ path: getPathWithoutParams(req),
33
+ hostname: req.hostname,
34
+ query_params: req.query,
35
+ });
36
+ export const parseFullLoggingRequest = (req) => ({
37
+ ...parseLoggingRequest(req),
38
+ headers: req.headers,
39
+ client_ip: req.ip,
40
+ user_agent: req.headers["user-agent"] ?? undefined,
41
+ });
42
+ const parseLoggingResponse = (res) => ({
43
+ status_code: res.statusCode,
44
+ headers: res.getHeaders(),
45
+ });
46
+ export const getLoggerConfiguration = (minimumLevel = "debug") => ({
47
+ base: { hostname: hostname() },
48
+ messageKey: MESSAGE_KEY,
49
+ mixin: () => ({
50
+ timestamp: Date.now(),
51
+ ...getLoggingContext({ includeError: false }),
52
+ }),
53
+ redact: {
54
+ paths: REDACTED_PATHS,
55
+ censor: REDACTED_VALUE,
56
+ },
57
+ timestamp: false,
58
+ formatters: {
59
+ level: (name, levelVal) => ({
60
+ level: levelVal,
61
+ level_name: name.toUpperCase(),
62
+ }),
63
+ },
64
+ level: minimumLevel,
65
+ });
66
+ //# sourceMappingURL=logging-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-wrapper.js","sourceRoot":"","sources":["../src/logging-wrapper.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@ogcio/fastify-logging-wrapper",
3
+ "version": "4.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "rm -rf dist tsconfig.prod.tsbuildinfo tsconfig.tsbuildinfo && tsc -p tsconfig.prod.json",
9
+ "test": "tap --jobs=1 --allow-incomplete-coverage __tests__/**/*.test.ts"
10
+ },
11
+ "keywords": [],
12
+ "author": {
13
+ "name": "Samuele Salvatico",
14
+ "email": "samuele.salvatico@nearform.com"
15
+ },
16
+ "license": "ISC",
17
+ "description": "Enable standardized log entries for each request in fastify",
18
+ "dependencies": {
19
+ "@fastify/error": "^4.0.0",
20
+ "@fastify/sensible": "^5.6.0",
21
+ "fastify": "^4.28.1",
22
+ "hyperid": "^3.3.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/http-errors": "^2.0.4"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/ogcio/shared-node-utils.git",
30
+ "directory": "packages/fastify-logging-wrapper"
31
+ }
32
+ }
@@ -0,0 +1,59 @@
1
+ import { FastifyServerOptions, FastifyInstance } from "fastify";
2
+ import hyperid from "hyperid";
3
+ import {
4
+ LogMessages,
5
+ REQUEST_ID_LOG_LABEL,
6
+ } from "./logging-wrapper-entities.js";
7
+ import {
8
+ getLoggerConfiguration,
9
+ getLoggingContextError,
10
+ getPartialLoggingContextError,
11
+ parseFullLoggingRequest,
12
+ resetLoggingContext,
13
+ setLoggingContext,
14
+ } from "./logging-wrapper.js";
15
+ import { pino, DestinationStream } from "pino";
16
+ import { REQUEST_ID_HEADER } from "@ogcio/shared-errors";
17
+
18
+ const hyperidInstance = hyperid({ fixedLength: true, urlSafe: true });
19
+
20
+ export const initializeLoggingHooks = (server: FastifyInstance): void => {
21
+ server.addHook("preHandler", (request, _reply, done) => {
22
+ setLoggingContext({ request });
23
+ request.log.info(
24
+ { request: parseFullLoggingRequest(request) },
25
+ LogMessages.NewRequest,
26
+ );
27
+ done();
28
+ });
29
+
30
+ server.addHook("onResponse", (_req, reply, done) => {
31
+ setLoggingContext({ response: reply });
32
+ reply.log.info(LogMessages.Response);
33
+ // Include error in API Track if exists
34
+ reply.log.info(
35
+ { error: getPartialLoggingContextError() },
36
+ LogMessages.ApiTrack,
37
+ );
38
+ resetLoggingContext();
39
+ done();
40
+ });
41
+
42
+ server.addHook("onError", (request, _reply, error, done) => {
43
+ setLoggingContext({ error });
44
+
45
+ request.log.error({ error: getLoggingContextError() }, LogMessages.Error);
46
+
47
+ done();
48
+ });
49
+ };
50
+
51
+ export const getLoggingConfiguration = (
52
+ loggerDestination?: DestinationStream,
53
+ ): FastifyServerOptions => ({
54
+ logger: pino(getLoggerConfiguration(), loggerDestination),
55
+ disableRequestLogging: true,
56
+ genReqId: () => hyperidInstance(),
57
+ requestIdLogLabel: REQUEST_ID_LOG_LABEL,
58
+ requestIdHeader: REQUEST_ID_HEADER,
59
+ });
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export {
2
+ getLoggingConfiguration,
3
+ initializeLoggingHooks,
4
+ } from "./fastify-logging-wrapper.js";
5
+ export {
6
+ toLoggingError,
7
+ LogMessages,
8
+ LoggingError,
9
+ } from "./logging-wrapper-entities.js";
10
+
11
+ export { getLoggingContextError, setLoggingContext } from "./logging-wrapper.js";