Skip to content

Commit 109b4a4

Browse files
danorlandoocontant
andauthored
Feature/logging system with pino and sanitization (danny-avila#214) (danny-avila#227)
* feat: Create structured data logging system with Pino This commit creates a new feature that enables structured data logging using the Pino logging library. The structured data logging feature allows for more granular and customizable logging, making it easier to analyze and debug issues in the application. The changes made in this commit include: - Adding support for structured data logging using the Pino API - Adding support to redact sensible data from logging output using pino redact. - Pino integrate natively with fluentd, logstash, Docker Logging Drivers and other JSON based system. * Add pino package to project * Logging-System: Add support for an array of regex to redact * Logging-Systems: Add Redact Patterns and Pino Redact Paths + Boolean logics wasn't right. Co-authored-by: Olivier Contant <[email protected]>
1 parent 943a137 commit 109b4a4

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

package-lock.json

Lines changed: 7 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"passport-google-oauth20": "^2.0.0",
5050
"passport-jwt": "^4.0.1",
5151
"passport-local": "^1.0.0",
52+
"pino": "^8.12.1",
5253
"sanitize": "^2.1.2"
5354
},
5455
"devDependencies": {

utils/LoggingSystem.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const pino = require('pino');
2+
3+
const logger = pino({
4+
level: 'info',
5+
redact: {
6+
paths: [ // List of Paths to redact from the logs (https://getpino.io/#/docs/redaction)
7+
'env.OPENAI_KEY',
8+
'env.BINGAI_TOKEN',
9+
'env.CHATGPT_TOKEN',
10+
'env.MEILI_MASTER_KEY',
11+
'env.GOOGLE_CLIENT_SECRET',
12+
'env.JWT_SECRET_DEV',
13+
'env.JWT_SECRET_PROD',
14+
'newUser.password'], // See example to filter object class instances
15+
censor: '***', // Redaction character
16+
},
17+
});
18+
19+
// Sanitize outside the logger paths. This is useful for sanitizing variables directly with Regex and patterns.
20+
const redactPatterns = [ // Array of regular expressions for redacting patterns
21+
/api[-_]?key/i,
22+
/password/i,
23+
/token/i,
24+
/secret/i,
25+
/key/i,
26+
/certificate/i,
27+
/client[-_]?id/i,
28+
/authorization[-_]?code/i,
29+
/authorization[-_]?login[-_]?hint/i,
30+
/authorization[-_]?acr[-_]?values/i,
31+
/authorization[-_]?response[-_]?mode/i,
32+
/authorization[-_]?nonce/i
33+
];
34+
35+
/*
36+
// Example of redacting sensitive data from object class instances
37+
function redactSensitiveData(obj) {
38+
if (obj instanceof User) {
39+
return {
40+
...obj.toObject(),
41+
password: '***', // Redact the password field
42+
};
43+
}
44+
return obj;
45+
}
46+
47+
// Example of redacting sensitive data from object class instances
48+
logger.info({ newUser: redactSensitiveData(newUser) }, 'newUser');
49+
*/
50+
51+
const levels = {
52+
TRACE: 10,
53+
DEBUG: 20,
54+
INFO: 30,
55+
WARN: 40,
56+
ERROR: 50,
57+
FATAL: 60
58+
};
59+
60+
61+
62+
let level = levels.INFO;
63+
64+
module.exports = {
65+
levels,
66+
setLevel: (l) => (level = l),
67+
log: {
68+
trace: (msg) => {
69+
if (level <= levels.TRACE) return;
70+
logger.trace(msg);
71+
},
72+
debug: (msg) => {
73+
if (level <= levels.DEBUG) return;
74+
logger.debug(msg);
75+
},
76+
info: (msg) => {
77+
if (level <= levels.INFO) return;
78+
logger.info(msg);
79+
},
80+
warn: (msg) => {
81+
if (level <= levels.WARN) return;
82+
logger.warn(msg);
83+
},
84+
error: (msg) => {
85+
if (level <= levels.ERROR) return;
86+
logger.error(msg);
87+
},
88+
fatal: (msg) => {
89+
if (level <= levels.FATAL) return;
90+
logger.fatal(msg);
91+
},
92+
93+
// Custom loggers
94+
parameters: (parameters) => {
95+
if (level <= levels.TRACE) return;
96+
logger.debug({ parameters }, 'Function Parameters');
97+
},
98+
functionName: (name) => {
99+
if (level <= levels.TRACE) return;
100+
logger.debug(`EXECUTING: ${name}`);
101+
},
102+
flow: (flow) => {
103+
if (level <= levels.INFO) return;
104+
logger.debug(`BEGIN FLOW: ${flow}`);
105+
},
106+
variable: ({ name, value }) => {
107+
if (level <= levels.DEBUG) return;
108+
// Check if the variable name matches any of the redact patterns and redact the value
109+
let sanitizedValue = value;
110+
for (const pattern of redactPatterns) {
111+
if (pattern.test(name)) {
112+
sanitizedValue = '***';
113+
break;
114+
}
115+
}
116+
logger.debug({ variable: { name, value: sanitizedValue } }, `VARIABLE ${name}`);
117+
},
118+
request: () => (req, res, next) => {
119+
if (level < levels.DEBUG) return next();
120+
logger.debug({ query: req.query, body: req.body }, `Hit URL ${req.url} with following`);
121+
return next();
122+
}
123+
}
124+
};
125+

0 commit comments

Comments
 (0)