@pryv/boiler 1.0.8
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/.licenser.yml +72 -0
- package/LICENSE +28 -0
- package/README.md +168 -0
- package/examples/configs/default-config.yml +7 -0
- package/examples/configs/extra-config.js +8 -0
- package/examples/configs/extra-config.json +3 -0
- package/examples/configs/extra-config.yml +2 -0
- package/examples/configs/extra-js-async.js +11 -0
- package/examples/configs/test-config.yml +16 -0
- package/examples/doubleinit.js +9 -0
- package/examples/extraConfigs +0 -0
- package/examples/index.js +100 -0
- package/examples/plugins/plugin-async.js +10 -0
- package/examples/plugins/plugin-sync.js +10 -0
- package/examples/remotes/ondisk.json +3 -0
- package/package.json +37 -0
- package/src/airbrake.js +52 -0
- package/src/compute-learn.js +109 -0
- package/src/config.js +441 -0
- package/src/index.js +115 -0
- package/src/lib/nconf-yaml.js +13 -0
- package/src/logging.js +286 -0
package/src/logging.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
const util = require('util');
|
|
6
|
+
const winston = require('winston');
|
|
7
|
+
require('winston-daily-rotate-file');
|
|
8
|
+
const debugModule = require('debug');
|
|
9
|
+
let winstonInstance = null;
|
|
10
|
+
let rootLogger = null;
|
|
11
|
+
|
|
12
|
+
// ------ winston formating
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} options
|
|
17
|
+
* @param {boolean} options.color - set to true to have colors
|
|
18
|
+
* @param {boolean} options.time - set to true to for timestamp
|
|
19
|
+
* @param {boolean} options.align - set to true to allign logs items
|
|
20
|
+
*/
|
|
21
|
+
function generateFormat(options) {
|
|
22
|
+
const formats = [];
|
|
23
|
+
if (options.color) {
|
|
24
|
+
formats.push(winston.format.colorize());
|
|
25
|
+
}
|
|
26
|
+
if (options.time) {
|
|
27
|
+
formats.push(winston.format.timestamp());
|
|
28
|
+
}
|
|
29
|
+
if (options.align) {
|
|
30
|
+
formats.push(winston.format.align());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function printf(info) {
|
|
34
|
+
const {
|
|
35
|
+
timestamp, level, message, ...args
|
|
36
|
+
} = info;
|
|
37
|
+
|
|
38
|
+
let items = info[Symbol.for('splat')] || {};
|
|
39
|
+
|
|
40
|
+
let itemStr = '';
|
|
41
|
+
if (items.length > 0) {
|
|
42
|
+
let skip = false;
|
|
43
|
+
if (items.length === 1) {
|
|
44
|
+
if (typeof items[0] === 'undefined') {
|
|
45
|
+
skip = true;
|
|
46
|
+
} else {
|
|
47
|
+
if (items[0] && items[0].context) {
|
|
48
|
+
items = items[0].context;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (! skip)
|
|
53
|
+
itemStr = util.inspect(items, {depth: 10, colors: true});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const line = `[${level}]: ${message} ${itemStr}`;
|
|
59
|
+
|
|
60
|
+
if (options.time) {
|
|
61
|
+
const ts = timestamp.slice(0, 19).replace('T', ' ');
|
|
62
|
+
return ts + ' ' + line;
|
|
63
|
+
} else {
|
|
64
|
+
return line;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
formats.push(winston.format.printf(printf));
|
|
68
|
+
return winston.format.combine(...formats);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Helper to pass log instructions to winston
|
|
75
|
+
*/
|
|
76
|
+
function globalLog(level, text, context) {
|
|
77
|
+
if (winstonInstance) {
|
|
78
|
+
winstonInstance[level](text, context);
|
|
79
|
+
} else {
|
|
80
|
+
console.log('Logger not initialized: ', ...arguments);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Config initialize Logger right after beeing loaded
|
|
87
|
+
* This is done by config Only
|
|
88
|
+
*/
|
|
89
|
+
async function initLoggerWithConfig(config) {
|
|
90
|
+
if (winstonInstance) {
|
|
91
|
+
throw new Error("Logger was already initialized");
|
|
92
|
+
}
|
|
93
|
+
// console
|
|
94
|
+
winstonInstance = winston.createLogger({ });
|
|
95
|
+
const logConsole = config.get('logs:console');
|
|
96
|
+
let isSilent = ! config.get('logs:console:active');
|
|
97
|
+
|
|
98
|
+
// LOGS env var can override settings
|
|
99
|
+
if (process.env.LOGS) {
|
|
100
|
+
logConsole.level = process.env.LOGS;
|
|
101
|
+
isSilent = false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
const format = generateFormat(logConsole.format)
|
|
106
|
+
const myconsole = new winston.transports.Console({ format: format , level: logConsole.level, silent: isSilent});
|
|
107
|
+
winstonInstance.add(myconsole);
|
|
108
|
+
|
|
109
|
+
rootLogger.debug((isSilent ? '** silent ** ' : '') + 'Console with level: ', logConsole.level);
|
|
110
|
+
|
|
111
|
+
// file
|
|
112
|
+
const logFile = config.get('logs:file');
|
|
113
|
+
if (config.get('logs:file:active')) {
|
|
114
|
+
rootLogger.debug('File active: ' + logFile.path);
|
|
115
|
+
if (logFile.rotation.isActive) {
|
|
116
|
+
const transport = new winston.transports.DailyRotateFile({
|
|
117
|
+
filename: logFile.path + '.%DATE%',
|
|
118
|
+
datePattern: 'YYYY-MM-DD',
|
|
119
|
+
zippedArchive: true,
|
|
120
|
+
maxFiles: logFile.rotation.days ? logFile.rotation.days + 'd' : null,
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
const files = new winston.transports.File({
|
|
124
|
+
filename: logFile.path,
|
|
125
|
+
level: logFile.level,
|
|
126
|
+
maxsize: logFile.maxFileBytes,
|
|
127
|
+
maxFiles: logFile.maxNbFiles,
|
|
128
|
+
timestamp: true,
|
|
129
|
+
json: false
|
|
130
|
+
});
|
|
131
|
+
winstonInstance.add(files);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
rootLogger.debug('Logger Initialized');
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
// --------------- debug utils
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Dump objects with file and line
|
|
144
|
+
*/
|
|
145
|
+
function inspect() {
|
|
146
|
+
let line = '';
|
|
147
|
+
try {
|
|
148
|
+
throw new Error();
|
|
149
|
+
} catch (e) {
|
|
150
|
+
line = e.stack.split(' at ')[2].trim();
|
|
151
|
+
}
|
|
152
|
+
let res = '\n * dump at: ' + line;
|
|
153
|
+
for (var i = 0; i < arguments.length; i++) {
|
|
154
|
+
res += '\n' + i + ' ' + util.inspect(arguments[i], true, 10, true) + '\n';
|
|
155
|
+
}
|
|
156
|
+
return res;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
function setGlobalName(name) {
|
|
161
|
+
// create root logger
|
|
162
|
+
rootLogger = new Logger(name, null);
|
|
163
|
+
rootLogger.debug('setGlobalName: ' + name);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Logger {
|
|
168
|
+
parent; // eventual parent
|
|
169
|
+
debugInstance; // debug instance
|
|
170
|
+
|
|
171
|
+
constructor(name, parent) {
|
|
172
|
+
this.name = name;
|
|
173
|
+
this.parent = parent;
|
|
174
|
+
this.debugInstance = debugModule('pryv:' + this._name());
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Private
|
|
178
|
+
*/
|
|
179
|
+
_name() {
|
|
180
|
+
if (this.parent) return this.parent._name() + ':' + this.name;
|
|
181
|
+
return this.name;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
log() {
|
|
185
|
+
const level = arguments[0];
|
|
186
|
+
const text = '[' + this._name() + ']: ' + hideSensitiveValues(arguments[1]);
|
|
187
|
+
const context = [];
|
|
188
|
+
|
|
189
|
+
let meta;
|
|
190
|
+
// Security measure: We do not want any sensitive value to appear in logs
|
|
191
|
+
for (let i = 2; i < arguments.length; i++) {
|
|
192
|
+
context.push(inspectAndHide(arguments[i]));
|
|
193
|
+
}
|
|
194
|
+
if (context.length === 1) {
|
|
195
|
+
meta = {context: context[0]};
|
|
196
|
+
} else if (context.length > 1) {
|
|
197
|
+
meta = {context: context};
|
|
198
|
+
}
|
|
199
|
+
globalLog(level, text, meta);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
info () { this.log('info', ...arguments); }
|
|
203
|
+
warn () { this.log('warn', ...arguments); }
|
|
204
|
+
error () { this.log('error', ...arguments); }
|
|
205
|
+
debug () {
|
|
206
|
+
if (winstonInstance) {
|
|
207
|
+
this.log('debug', ...arguments);
|
|
208
|
+
}
|
|
209
|
+
this.debugInstance(...arguments);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* get a "sub" Logger
|
|
214
|
+
* @param {Logger} name
|
|
215
|
+
*/
|
|
216
|
+
getLogger (name) {
|
|
217
|
+
return new Logger(name, this);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
inspect() { inspect(...arguments); }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getLogger(name) {
|
|
224
|
+
if (! rootLogger) {
|
|
225
|
+
throw new Error('Initalize boiler before using logger')
|
|
226
|
+
}
|
|
227
|
+
if(! name) {
|
|
228
|
+
return rootLogger;
|
|
229
|
+
}
|
|
230
|
+
return rootLogger.getLogger(name);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = {
|
|
234
|
+
getLogger: getLogger,
|
|
235
|
+
setGlobalName: setGlobalName,
|
|
236
|
+
initLoggerWithConfig: initLoggerWithConfig
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ----------------- Hide sensite data -------------------- //
|
|
240
|
+
|
|
241
|
+
function inspectAndHide(o) {
|
|
242
|
+
if (typeof o === 'undefined') return o;
|
|
243
|
+
if (o instanceof Error) return o;
|
|
244
|
+
return _inspectAndHide(JSON.parse(JSON.stringify(o))); // clone and remove circular
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function _inspectAndHide(o) {
|
|
248
|
+
if (typeof o === 'string') {
|
|
249
|
+
return hideSensitiveValues(o);
|
|
250
|
+
}
|
|
251
|
+
if (o !== null && typeof o === 'object') {
|
|
252
|
+
if (Array.isArray(o)) {
|
|
253
|
+
const res = [];
|
|
254
|
+
for (let item of o) {
|
|
255
|
+
res.push(inspectAndHide(item));
|
|
256
|
+
}
|
|
257
|
+
return res;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const res = {};
|
|
261
|
+
for (let key of Object.keys(o)) {
|
|
262
|
+
if (['password', 'passwordHash', 'newPassword'].includes(key)) {
|
|
263
|
+
res[key] = '(hidden password)';
|
|
264
|
+
} else {
|
|
265
|
+
res[key] = inspectAndHide(o[key]);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return res;
|
|
269
|
+
}
|
|
270
|
+
return o;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
// Hides sensitive values (auth tokens and passwords) in log messages
|
|
275
|
+
function hideSensitiveValues (msg) {
|
|
276
|
+
if (typeof msg !== 'string') return msg;
|
|
277
|
+
const tokenRegexp = /auth\=c([a-z0-9-]*)/g;
|
|
278
|
+
const passwordRegexp = /"(password|passwordHash|newPassword)"[:=]"([^"]*)"/g;
|
|
279
|
+
const mask = '(hidden)';
|
|
280
|
+
|
|
281
|
+
const res = msg
|
|
282
|
+
.replace(tokenRegexp, 'auth='+mask)
|
|
283
|
+
.replace(passwordRegexp, '$1='+mask);
|
|
284
|
+
|
|
285
|
+
return res;
|
|
286
|
+
}
|