@pryv/boiler 1.2.3 → 1.2.5

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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020–2023 Pryv S.A https://pryv.com
1
+ Copyright (c) 2020–2024 Pryv S.A https://pryv.com
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions are met:
@@ -1,20 +1,24 @@
1
+ /**
2
+ * @license
3
+ * [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
4
+ */
1
5
 
2
- module.exports = { 
3
- init,
6
+ module.exports = {
7
+ init,
4
8
  log
5
- }
9
+ };
6
10
 
7
- async function init(settings) {
11
+ async function init (settings) {
8
12
  console.log('CUSTOM LOGGER initilaized with', settings);
9
13
  }
10
14
 
11
15
  /**
12
- *
16
+ *
13
17
  * @param {string} level one of 'debug', 'info', 'warn', 'error'
14
18
  * @param {string} key ':' namespaced keys
15
- * @param {message} message
16
- * @param {object} meta
19
+ * @param {message} message
20
+ * @param {object} meta
17
21
  */
18
- function log(level, key, text, meta) {
19
- console.log('Custom: ' + JSON.stringify({level, key, text, meta}));
20
- }
22
+ function log (level, key, text, meta) {
23
+ console.log('Custom: ' + JSON.stringify({ level, key, text, meta }));
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pryv/boiler",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "private": false,
5
5
  "description": "Logging and config boilerplate library for Node.js apps and services at Pryv",
6
6
  "keywords": [
@@ -19,6 +19,7 @@
19
19
  "license": "BSD-3-Clause",
20
20
  "author": "Pryv S.A <info@pryv.com> (https://pryv.com)",
21
21
  "main": "src/index",
22
+ "types": "src/index.d.ts",
22
23
  "scripts": {
23
24
  "license": "source-licenser -c .licenser.yml .",
24
25
  "lint": "semistandard"
@@ -27,7 +28,6 @@
27
28
  "debug": "^4.3.4",
28
29
  "js-yaml": "^4.1.0",
29
30
  "nconf": "^0.12.0",
30
- "superagent": "^8.0.9",
31
31
  "winston": "^3.9.0",
32
32
  "winston-daily-rotate-file": "^4.7.1"
33
33
  },
package/src/config.js CHANGED
@@ -24,8 +24,6 @@ const path = require('path');
24
24
  const nconf = require('nconf');
25
25
  nconf.formats.yaml = require('./lib/nconf-yaml');
26
26
 
27
- const superagent = require('superagent');
28
-
29
27
  /**
30
28
  * Default values for Logger
31
29
  */
@@ -50,8 +48,6 @@ const defaults = {
50
48
  }
51
49
  };
52
50
 
53
-
54
-
55
51
  /**
56
52
  * Config manager
57
53
  */
@@ -62,7 +58,7 @@ class Config {
62
58
  baseConfigDir;
63
59
  learnDirectoryAndFilename;
64
60
 
65
- constructor() {
61
+ constructor () {
66
62
  this.extraAsync = [];
67
63
  }
68
64
 
@@ -78,7 +74,7 @@ class Config {
78
74
  * @param {Object} logging
79
75
  * @returns {Config} this
80
76
  */
81
- initSync(options, logging) {
77
+ initSync (options, logging) {
82
78
  this.appName = options.appName;
83
79
  this.learnDirectoryAndFilename = getLearnFilename(options.appName, options.learnDirectory);
84
80
  this.baseFilesDir = options.baseFilesDir || process.cwd();
@@ -103,12 +99,12 @@ class Config {
103
99
  // memory must come first for config.set() to work without loading config files
104
100
  // 3. `process.env`
105
101
  // 4. `process.argv`
106
- store.argv({parseValues: true}).env({parseValues: true, separator: '__'});
102
+ store.argv({ parseValues: true }).env({ parseValues: true, separator: '__' });
107
103
 
108
104
  // 5. Values in `${NODE_ENV}-config.yml` or from --config parameter
109
105
  let configFile;
110
106
  if (store.get('config')) {
111
- configFile = store.get('config')
107
+ configFile = store.get('config');
112
108
  } else if (store.get('NODE_ENV')) {
113
109
  configFile = path.resolve(baseConfigDir, store.get('NODE_ENV') + '-config.yml');
114
110
  }
@@ -122,7 +118,7 @@ class Config {
122
118
 
123
119
  // load extra config files & plugins
124
120
  if (options.extras) {
125
- for (let extra of options.extras) {
121
+ for (const extra of options.extras) {
126
122
  if (extra.file) {
127
123
  loadFile(extra.scope, extra.file);
128
124
  continue;
@@ -133,7 +129,7 @@ class Config {
133
129
  continue;
134
130
  }
135
131
  if (extra.data) {
136
- const conf = extra.key ? {[extra.key]: extra.data} : extra.data;
132
+ const conf = extra.key ? { [extra.key]: extra.data } : extra.data;
137
133
  store.use(extra.scope, { type: 'literal', store: conf });
138
134
  logger.debug('Loaded [' + extra.scope + '] from DATA: ' + (extra.key ? ' under [' + extra.key + ']' : ''));
139
135
  continue;
@@ -141,7 +137,7 @@ class Config {
141
137
  if (extra.url || extra.urlFromKey || extra.fileAsync) {
142
138
  // register scope in the chain to keep order of configs
143
139
  store.use(extra.scope, { type: 'literal', store: {} });
144
- logger.debug('Booked [' + extra.scope +'] for async Loading ');
140
+ logger.debug('Booked [' + extra.scope + '] for async Loading ');
145
141
  this.extraAsync.push(extra);
146
142
  continue;
147
143
  }
@@ -154,7 +150,6 @@ class Config {
154
150
  }
155
151
  }
156
152
 
157
-
158
153
  // .end-1 load default and custom config from configs/default-config.json
159
154
  loadFile('default-file', path.resolve(baseConfigDir, 'default-config.yml'));
160
155
 
@@ -167,36 +162,34 @@ class Config {
167
162
 
168
163
  // --- helpers --/
169
164
 
170
- function loadFile(scope, filePath) {
171
-
165
+ function loadFile (scope, filePath) {
172
166
  if (fs.existsSync(filePath)) {
173
-
174
- if (filePath.endsWith('.js')) { // JS file
167
+ if (filePath.endsWith('.js')) { // JS file
175
168
  const conf = require(filePath);
176
169
  store.use(scope, { type: 'literal', store: conf });
177
- } else {  // JSON or YAML
178
- const options = { file: filePath }
179
- if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) { options.format = nconf.formats.yaml }
170
+ } else { // JSON or YAML
171
+ const options = { file: filePath };
172
+ if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) { options.format = nconf.formats.yaml; }
180
173
  store.file(scope, options);
181
174
  }
182
175
 
183
- logger.debug('Loaded [' + scope + '] from file: ' + filePath)
176
+ logger.debug('Loaded [' + scope + '] from file: ' + filePath);
184
177
  } else {
185
178
  logger.debug('Cannot find file: ' + filePath + ' for scope [' + scope + ']');
186
179
  }
187
180
  }
188
181
  }
189
182
 
190
- async initASync() {
183
+ async initASync () {
191
184
  const store = this.store;
192
185
  const logger = this.logger;
193
186
  const baseConfigDir = this.baseConfigDir;
194
187
  const baseFilesDir = this.baseFilesDir;
195
188
 
196
- async function loadUrl(scope, key, url) {
189
+ async function loadUrl (scope, key, url) {
197
190
  if (typeof url === 'undefined' || url === null) {
198
- logger.warn('Null or Undefined Url for [' + scope +']');
199
- return;
191
+ logger.warn('Null or Undefined Url for [' + scope + ']');
192
+ return;
200
193
  }
201
194
 
202
195
  let res = null;
@@ -205,13 +198,13 @@ class Config {
205
198
  } else {
206
199
  res = await loadFromUrl(url);
207
200
  }
208
- const conf = key ? {[key]: res} : res;
201
+ const conf = key ? { [key]: res } : res;
209
202
  store.add(scope, { type: 'literal', store: conf });
210
203
  logger.debug('Loaded [' + scope + '] from URL: ' + url + (key ? ' under [' + key + ']' : ''));
211
204
  }
212
205
 
213
206
  // load remote config files
214
- for (let extra of this.extraAsync) {
207
+ for (const extra of this.extraAsync) {
215
208
  if (extra.url) {
216
209
  await loadUrl(extra.scope, extra.key, extra.url);
217
210
  continue;
@@ -231,11 +224,11 @@ class Config {
231
224
  if (extra.fileAsync) {
232
225
  const filePath = path.resolve(baseConfigDir, extra.fileAsync);
233
226
 
234
- if (! fs.existsSync(filePath)) {
227
+ if (!fs.existsSync(filePath)) {
235
228
  logger.warn('Cannot find file: ' + filePath + ' for scope [' + extra.scope + ']');
236
229
  continue;
237
230
  }
238
- if (! filePath.endsWith('.js')) {
231
+ if (!filePath.endsWith('.js')) {
239
232
  logger.warn('Cannot only load .js file: ' + filePath + ' for scope [' + extra.scope + ']');
240
233
  continue;
241
234
  }
@@ -257,8 +250,8 @@ class Config {
257
250
  * @param {string} key
258
251
  * @returns {boolean}
259
252
  */
260
- has(key) {
261
- if (! this.store) { throw(new Error('Config not yet initialized'))}
253
+ has (key) {
254
+ if (!this.store) { throw (new Error('Config not yet initialized')); }
262
255
  const value = this.store.get(key);
263
256
  return (typeof value !== 'undefined');
264
257
  }
@@ -267,10 +260,10 @@ class Config {
267
260
  * Retreive value
268
261
  * @param {string} [key] if no key is provided all the config is returned
269
262
  */
270
- get(key) {
271
- if (! this.store) { throw(new Error('Config not yet initialized'))}
263
+ get (key) {
264
+ if (!this.store) { throw (new Error('Config not yet initialized')); }
272
265
  const value = this.store.get(key);
273
- if (typeof value === 'undefined') this.logger.debug('get: [' + key +'] is undefined');
266
+ if (typeof value === 'undefined') this.logger.debug('get: [' + key + '] is undefined');
274
267
  learn(this.learnDirectoryAndFilename, key);
275
268
  return value;
276
269
  }
@@ -279,20 +272,20 @@ class Config {
279
272
  * Retreive value and store info that applies
280
273
  * @param {string} key
281
274
  */
282
- getScopeAndValue(key) {
283
- if (! this.store) { throw(new Error('Config not yet initialized'))};
284
- for (let scopeName of Object.keys(this.store.stores)) {
275
+ getScopeAndValue (key) {
276
+ if (!this.store) { throw (new Error('Config not yet initialized')); }
277
+ for (const scopeName of Object.keys(this.store.stores)) {
285
278
  const store = this.store.stores[scopeName];
286
279
  const value = store.get(key);
287
280
  if (typeof value !== 'undefined') {
288
281
  const res = {
289
- value: value,
282
+ value,
290
283
  scope: scopeName
291
- }
284
+ };
292
285
  if (store.type === 'file') {
293
- res.info = 'From file: ' + store.file
286
+ res.info = 'From file: ' + store.file;
294
287
  } else {
295
- info = 'Type: ' + store.type
288
+ res.info = 'Type: ' + store.type;
296
289
  }
297
290
  return res;
298
291
  }
@@ -305,8 +298,8 @@ class Config {
305
298
  * @param {string} key
306
299
  * @param {Object} value
307
300
  */
308
- set(key, value) {
309
- if (! this.store) { throw(new Error('Config not yet initialized'))}
301
+ set (key, value) {
302
+ if (!this.store) { throw (new Error('Config not yet initialized')); }
310
303
  this.store.set(key, value);
311
304
  }
312
305
 
@@ -314,7 +307,7 @@ class Config {
314
307
  * Inject Test Config and override any other option
315
308
  * @param {Object} configObject;
316
309
  */
317
- injectTestConfig(configObject) {
310
+ injectTestConfig (configObject) {
318
311
  this.replaceScopeConfig('test', configObject);
319
312
  }
320
313
 
@@ -323,12 +316,11 @@ class Config {
323
316
  * @param {string} scope;
324
317
  * @param {Object} configObject;
325
318
  */
326
- replaceScopeConfig(scope, configObject) {
327
- if (! this.store) { throw(new Error('Config not yet initialized'))}
328
- this.logger.debug('Replace ['+ scope + '] with: ', configObject);
329
- this.store.add(scope, {type: 'literal', store: configObject});
319
+ replaceScopeConfig (scope, configObject) {
320
+ if (!this.store) { throw (new Error('Config not yet initialized')); }
321
+ this.logger.debug('Replace [' + scope + '] with: ', configObject);
322
+ this.store.add(scope, { type: 'literal', store: configObject });
330
323
  }
331
-
332
324
  }
333
325
 
334
326
  module.exports = Config;
@@ -338,12 +330,12 @@ module.exports = Config;
338
330
  const FILE_PROTOCOL = 'file://';
339
331
  const FILE_PROTOCOL_LENGTH = FILE_PROTOCOL.length;
340
332
 
341
- async function loadFromUrl(url) {
342
- const res = await superagent.get(url);
343
- return res.body;
333
+ async function loadFromUrl (url) {
334
+ const res = await fetch(url);
335
+ return res.json();
344
336
  }
345
337
 
346
- function loadFromFile(fileUrl, baseFilesDir) {
338
+ function loadFromFile (fileUrl, baseFilesDir) {
347
339
  const filePath = stripFileProtocol(fileUrl);
348
340
 
349
341
  if (isRelativePath(filePath)) {
@@ -358,69 +350,65 @@ function loadFromFile(fileUrl, baseFilesDir) {
358
350
  return res;
359
351
  }
360
352
 
361
-
362
- function isFileUrl(filePath) {
353
+ function isFileUrl (filePath) {
363
354
  return filePath.startsWith(FILE_PROTOCOL);
364
355
  }
365
356
 
366
- function isRelativePath(filePath) {
357
+ function isRelativePath (filePath) {
367
358
  return !path.isAbsolute(filePath);
368
359
  }
369
360
 
370
- function stripFileProtocol(filePath) {
361
+ function stripFileProtocol (filePath) {
371
362
  return filePath.substring(FILE_PROTOCOL_LENGTH);
372
363
  }
373
364
 
374
-
375
365
  // -------- learning mode ------- //
376
366
 
377
- function getLearnFilename(appName, learnDirectory) {
378
- if (! learnDirectory) return;
367
+ function getLearnFilename (appName, learnDirectory) {
368
+ if (!learnDirectory) return;
379
369
  let i = 0;
380
370
  let res;
381
371
  do {
382
- res = path.join(learnDirectory, appName + i );
372
+ res = path.join(learnDirectory, appName + i);
383
373
  i++;
384
- } while(fs.existsSync(res + '-config.json'));
374
+ } while (fs.existsSync(res + '-config.json'));
385
375
  return res;
386
376
  }
387
377
 
388
- function learn(learnDirectoryAndFilename, key) {
378
+ function learn (learnDirectoryAndFilename, key) {
389
379
  if (learnDirectoryAndFilename) {
390
380
  const caller_line = (new Error()).stack.split('\n')[3]; // get callee name and line
391
- const index = caller_line.indexOf("at ");
392
- const str = key + ';' + caller_line.slice(index+3, caller_line.length) + '\n';
381
+ const index = caller_line.indexOf('at ');
382
+ const str = key + ';' + caller_line.slice(index + 3, caller_line.length) + '\n';
393
383
  fs.appendFileSync(learnDirectoryAndFilename + '-calls.csv', str);
394
384
  }
395
385
  }
396
386
 
397
- function saveConfig(learnDirectoryAndFilename, store) {
387
+ function saveConfig (learnDirectoryAndFilename, store) {
398
388
  if (learnDirectoryAndFilename) {
399
389
  const filename = learnDirectoryAndFilename + '-config.json';
400
- fs.writeFileSync(filename, JSON.stringify({stores: store.stores, config: store.get()}, null, 2));
390
+ fs.writeFileSync(filename, JSON.stringify({ stores: store.stores, config: store.get() }, null, 2));
401
391
  }
402
392
  }
403
393
 
404
-
405
394
  /**
406
395
  * @typedef ConfigFile
407
396
  * @property {string} scope - scope for nconf hierachical load
408
397
  * @property {string} file - the config file (.yml, .json, .js)
409
398
  */
410
399
 
411
- /**
400
+ /**
412
401
  * @typedef ConfigPlugin
413
402
  * @property {Object} plugin
414
403
  * @property {Function} plugin.load - a function that takes the "nconf store" as argument and returns the "name" of the plugin
415
404
  */
416
405
 
417
- /**
406
+ /**
418
407
  * @typedef ConfigData
419
408
  * @property {string} scope - scope for nconf hierachical load
420
409
  * @property {string} [key] - (optional) key to load result of url. If null loaded at root of the config
421
410
  * @property {object} data - the data to load
422
411
 
423
-
424
412
  /**
425
413
  * @typedef ConfigRemoteURL
426
414
  * @property {string} scope - scope for nconf hierachical load
package/src/index.d.ts ADDED
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @license
3
+ * [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
4
+ */
5
+
6
+ export = boiler;
7
+
8
+ declare const boiler: {
9
+ /**
10
+ * Get a Logger
11
+ * @param name - The name of the logger
12
+ */
13
+ getLogger: (name?: string) => boiler.Logger;
14
+
15
+ /**
16
+ * Preferred way to get the configuration.
17
+ * Waits until the configuration is fully initialized.
18
+ */
19
+ getConfig: () => Promise<boiler.Config>;
20
+
21
+ /**
22
+ * Get the configuration.
23
+ * If the configuration is not fully initialized, throws an error (or warns if warnOnly is true).
24
+ * @param warnOnly - Only warns about potential misuse of config instead of throwing
25
+ */
26
+ getConfigUnsafe: (warnOnly?: boolean) => boiler.Config;
27
+
28
+ /**
29
+ * Initialize Boiler. Should be called just once when starting an app.
30
+ * @param options - Initialization options
31
+ * @param fullyLoadedCallback - Called when the config is fully loaded
32
+ */
33
+ init: (options: boiler.InitOptions, fullyLoadedCallback?: (config: boiler.Config) => void) => typeof boiler;
34
+ };
35
+
36
+ declare namespace boiler {
37
+ interface InitOptions {
38
+ /** The name of the application, used by Logger and debug */
39
+ appName: string;
40
+ /** Directory to use to look for configs (default, env) */
41
+ baseConfigDir?: string;
42
+ /** Directory to use for `file://` relative paths */
43
+ baseFilesDir?: string;
44
+ /** Array of extra config sources to load */
45
+ extraConfigs?: Array<ConfigFile | ConfigPlugin | ConfigPluginAsync | ConfigData | ConfigRemoteURL | ConfigRemoteURLFromKey | ConfigFileAsync>;
46
+ }
47
+
48
+ interface ConfigFile {
49
+ /** Scope for nconf hierarchical load */
50
+ scope: string;
51
+ /** The config file path (.yml, .json, .js) */
52
+ file: string;
53
+ }
54
+
55
+ interface ConfigFileAsync {
56
+ /** Scope for nconf hierarchical load */
57
+ scope: string;
58
+ /** The async config file path (.js that exports an async function) */
59
+ fileAsync: string;
60
+ }
61
+
62
+ interface ConfigPlugin {
63
+ plugin: {
64
+ /** Function that takes the Config instance and returns the plugin name */
65
+ load: (config: Config) => string;
66
+ };
67
+ }
68
+
69
+ interface ConfigPluginAsync {
70
+ pluginAsync: {
71
+ /** Async function that takes the Config instance and returns the plugin name */
72
+ load: (config: Config) => Promise<string>;
73
+ };
74
+ }
75
+
76
+ interface ConfigData {
77
+ /** Scope for nconf hierarchical load */
78
+ scope: string;
79
+ /** Key to load data under. If null, loaded at root of the config */
80
+ key?: string;
81
+ /** The data to load */
82
+ data: object;
83
+ }
84
+
85
+ interface ConfigRemoteURL {
86
+ /** Scope for nconf hierarchical load */
87
+ scope: string;
88
+ /** Key to load result of URL under. If null, loaded at root of the config */
89
+ key?: string;
90
+ /** The URL to the config definition */
91
+ url: string;
92
+ }
93
+
94
+ interface ConfigRemoteURLFromKey {
95
+ /** Scope for nconf hierarchical load */
96
+ scope: string;
97
+ /** Key to load result of URL under. If null, override */
98
+ key?: string;
99
+ /** Retrieve URL from config matching this key */
100
+ urlFromKey: string;
101
+ }
102
+
103
+ interface ScopeAndValue {
104
+ value: unknown;
105
+ scope: string;
106
+ info: string;
107
+ }
108
+
109
+ class Config {
110
+ /** The nconf store instance */
111
+ store: unknown;
112
+ /** The logger instance for config */
113
+ logger: Logger;
114
+ /** Base directory for config files */
115
+ baseConfigDir: string;
116
+
117
+ /**
118
+ * Return true if key has a value
119
+ * @param key - The config key to check
120
+ */
121
+ has(key: string): boolean;
122
+
123
+ /**
124
+ * Retrieve value
125
+ * @param key - The config key. If no key is provided, all the config is returned
126
+ */
127
+ get(key?: string): unknown;
128
+ get<T>(key: string): T | undefined;
129
+
130
+ /**
131
+ * Retrieve value and store info that applies
132
+ * @param key - The config key
133
+ */
134
+ getScopeAndValue(key: string): ScopeAndValue | null;
135
+
136
+ /**
137
+ * Set value
138
+ * @param key - The config key
139
+ * @param value - The value to set
140
+ */
141
+ set(key: string, value: unknown): void;
142
+
143
+ /**
144
+ * Inject test config and override any other option
145
+ * @param configObject - The config object to inject for testing
146
+ */
147
+ injectTestConfig(configObject: object): void;
148
+
149
+ /**
150
+ * Replace a scope config set
151
+ * @param scope - The scope to replace
152
+ * @param configObject - The new config object for the scope
153
+ */
154
+ replaceScopeConfig(scope: string, configObject: object): void;
155
+ }
156
+
157
+ class Logger {
158
+ /** The name of this logger */
159
+ name: string;
160
+ /** The parent logger, if any */
161
+ parent: Logger | null;
162
+
163
+ /**
164
+ * Log a message at the specified level
165
+ * @param level - The log level
166
+ * @param message - The log message
167
+ * @param context - Additional context
168
+ */
169
+ log(level: string, message: string, ...context: unknown[]): void;
170
+
171
+ /**
172
+ * Log an info message
173
+ * @param message - The log message
174
+ * @param context - Additional context
175
+ */
176
+ info(message: string, ...context: unknown[]): void;
177
+
178
+ /**
179
+ * Log a warning message
180
+ * @param message - The log message
181
+ * @param context - Additional context
182
+ */
183
+ warn(message: string, ...context: unknown[]): void;
184
+
185
+ /**
186
+ * Log an error message
187
+ * @param message - The log message
188
+ * @param context - Additional context
189
+ */
190
+ error(message: string, ...context: unknown[]): void;
191
+
192
+ /**
193
+ * Log a debug message
194
+ * @param message - The log message
195
+ * @param context - Additional context
196
+ */
197
+ debug(message: string, ...context: unknown[]): void;
198
+
199
+ /**
200
+ * Get a "sub" Logger
201
+ * @param name - The name of the child logger
202
+ */
203
+ getLogger(name: string): Logger;
204
+
205
+ /**
206
+ * Dump objects with file and line for debugging
207
+ */
208
+ inspect(...args: unknown[]): void;
209
+ }
210
+ }
package/src/index.js CHANGED
@@ -25,14 +25,14 @@ const boiler = {
25
25
  * Prefered way to get the configuration
26
26
  * @returns {Promise<Config>}
27
27
  */
28
- getConfig: getConfig,
28
+ getConfig,
29
29
  /**
30
30
  * get the configuration.
31
31
  * If the configuration is not fully initialized throw an error
32
32
  * @param {boolean} warnOnly - Only warns about potential misuse of config
33
33
  * @returns {Config}
34
34
  */
35
- getConfigUnsafe: getConfigUnsafe,
35
+ getConfigUnsafe,
36
36
 
37
37
  /**
38
38
  * Init Boiler, should be called just once when starting an APP
@@ -42,7 +42,7 @@ const boiler = {
42
42
  * @param {Array<ConfigFile|ConfigRemoteURL|ConfigRemoteURLFromKey|ConfigPlugin>} [options.extraConfigs] - (optional) and array of extra files to load
43
43
  * @param {Function} [fullyLoadedCallback] - (optional) called when the config is fully loaded
44
44
  */
45
- init: init
45
+ init
46
46
  };
47
47
 
48
48
  let logger;
package/src/logging.js CHANGED
@@ -13,17 +13,17 @@ let customLoggerInstance = null;
13
13
  // ------ winston formating
14
14
 
15
15
  /**
16
- *
17
- * @param {Object} options
16
+ *
17
+ * @param {Object} options
18
18
  * @param {boolean} options.color - set to true to have colors
19
19
  * @param {boolean} options.time - set to true to for timestamp
20
20
  * @param {boolean} options.align - set to true to allign logs items
21
21
  */
22
- function generateFormat(options) {
22
+ function generateFormat (options) {
23
23
  const formats = [];
24
24
  if (options.color) {
25
25
  formats.push(winston.format.colorize());
26
- }
26
+ }
27
27
  if (options.time) {
28
28
  formats.push(winston.format.timestamp());
29
29
  }
@@ -31,13 +31,13 @@ function generateFormat(options) {
31
31
  formats.push(winston.format.align());
32
32
  }
33
33
 
34
- function printf(info) {
34
+ function printf (info) {
35
35
  const {
36
36
  timestamp, level, message, ...args
37
37
  } = info;
38
-
39
- let items = info[Symbol.for('splat')] || {};
40
-
38
+
39
+ let items = info[Symbol.for('splat')] || {};
40
+
41
41
  let itemStr = '';
42
42
  if (items.length > 0) {
43
43
  let skip = false;
@@ -45,17 +45,14 @@ function generateFormat(options) {
45
45
  if (typeof items[0] === 'undefined') {
46
46
  skip = true;
47
47
  } else {
48
- if (items[0] && items[0].context) {
48
+ if (items[0] && items[0].context) {
49
49
  items = items[0].context;
50
- }
50
+ }
51
51
  }
52
52
  }
53
- if (! skip)
54
- itemStr = util.inspect(items, {depth: 10, colors: true});
53
+ if (!skip) { itemStr = util.inspect(items, { depth: 10, colors: true }); }
55
54
  }
56
55
 
57
-
58
-
59
56
  const line = `[${level}]: ${message} ${itemStr}`;
60
57
 
61
58
  if (options.time) {
@@ -69,12 +66,10 @@ function generateFormat(options) {
69
66
  return winston.format.combine(...formats);
70
67
  }
71
68
 
72
-
73
-
74
69
  /**
75
70
  * Helper to pass log instructions to winston
76
71
  */
77
- function globalLog(level, key, message, context) {
72
+ function globalLog (level, key, message, context) {
78
73
  const text = `[${key}] ${message}`;
79
74
  if (winstonInstance) {
80
75
  winstonInstance[level](text, context);
@@ -86,56 +81,60 @@ function globalLog(level, key, message, context) {
86
81
  }
87
82
  }
88
83
 
89
-
90
84
  /**
91
85
  * Config initialize Logger right after beeing loaded
92
86
  * This is done by config Only
93
- */
94
- async function initLoggerWithConfig(config) { 
87
+ */
88
+ async function initLoggerWithConfig (config) {
95
89
  if (winstonInstance) {
96
- throw new Error("Logger was already initialized");
90
+ throw new Error('Logger was already initialized');
97
91
  }
98
92
  // console
99
93
  winstonInstance = winston.createLogger({ });
100
94
  const logConsole = config.get('logs:console');
101
- let isSilent = ! config.get('logs:console:active');
95
+ let isSilent = !config.get('logs:console:active');
102
96
 
103
97
  // LOGS env var can override settings
104
98
  if (process.env.LOGS) {
105
99
  logConsole.level = process.env.LOGS;
106
100
  isSilent = false;
107
- }
108
-
101
+ }
109
102
 
110
- const format = generateFormat(logConsole.format)
111
- const myconsole = new winston.transports.Console({ format: format , level: logConsole.level, silent: isSilent});
103
+ const consoleFormat = generateFormat(logConsole.format);
104
+ const myconsole = new winston.transports.Console({ format: consoleFormat, level: logConsole.level, silent: isSilent });
112
105
  winstonInstance.add(myconsole);
113
-
114
- rootLogger.debug((isSilent ? '** silent ** ' : '') + 'Console with level: ', logConsole.level);
106
+
107
+ rootLogger.debug((isSilent ? '** silent ** ' : '') + 'Console with level: ', logConsole.level);
115
108
 
116
109
  // file
117
110
  const logFile = config.get('logs:file');
118
111
  if (config.get('logs:file:active')) {
112
+ const fileFormat = winston.format.combine(
113
+ winston.format.timestamp(),
114
+ winston.format.json()
115
+ );
116
+
119
117
  rootLogger.debug('File active: ' + logFile.path);
120
118
  if (logFile.rotation.isActive) {
121
- const transport = new winston.transports.DailyRotateFile({
119
+ const rotatedFiles = new winston.transports.DailyRotateFile({
122
120
  filename: logFile.path + '.%DATE%',
123
121
  datePattern: 'YYYY-MM-DD',
124
122
  zippedArchive: true,
123
+ level: logFile.level,
125
124
  maxFiles: logFile.rotation.days ? logFile.rotation.days + 'd' : null,
125
+ format: fileFormat
126
126
  });
127
+ winstonInstance.add(rotatedFiles);
127
128
  } else {
128
- const files = new winston.transports.File({
129
+ const files = new winston.transports.File({
129
130
  filename: logFile.path,
130
131
  level: logFile.level,
131
- maxsize: logFile.maxFileBytes,
132
- maxFiles: logFile.maxNbFiles,
133
- timestamp: true,
134
- json: false
132
+ maxSize: logFile.maxFileBytes || '10m',
133
+ maxFiles: logFile.maxNbFiles || '14d',
134
+ format: fileFormat
135
135
  });
136
136
  winstonInstance.add(files);
137
137
  }
138
-
139
138
  }
140
139
 
141
140
  // custom
@@ -144,17 +143,23 @@ async function initLoggerWithConfig(config) { 
144
143
  await customLoggerInstance.init(config.get('logs:custom:settings'));
145
144
  }
146
145
 
147
- rootLogger.debug('Logger Initialized');
148
- };
149
-
146
+ // catch all errors.
147
+ if (!config.get('logs:skipUncaughtException')) {
148
+ process.on('uncaughtException', function (err) {
149
+ rootLogger.error('UncaughtException', { message: err.message, name: err.name, stack: err.stack });
150
+ throw err;
151
+ });
152
+ }
150
153
 
154
+ rootLogger.debug('Logger Initialized');
155
+ }
151
156
 
152
- // --------------- debug utils
157
+ // --------------- debug utils
153
158
 
154
159
  /**
155
160
  * Dump objects with file and line
156
161
  */
157
- function inspect() {
162
+ function inspect () {
158
163
  let line = '';
159
164
  try {
160
165
  throw new Error();
@@ -162,38 +167,37 @@ function inspect() {
162
167
  line = e.stack.split(' at ')[2].trim();
163
168
  }
164
169
  let res = '\n * dump at: ' + line;
165
- for (var i = 0; i < arguments.length; i++) {
170
+ for (let i = 0; i < arguments.length; i++) {
166
171
  res += '\n' + i + ' ' + util.inspect(arguments[i], true, 10, true) + '\n';
167
172
  }
168
173
  return res;
169
- };
170
-
174
+ }
171
175
 
172
- function setGlobalName(name) {
176
+ function setGlobalName (name) {
173
177
  // create root logger
174
178
  rootLogger = new Logger(name, null);
175
179
  rootLogger.debug('setGlobalName: ' + name);
176
180
  }
177
181
 
178
-
179
182
  class Logger {
180
183
  parent; // eventual parent
181
184
  debugInstance; // debug instance
182
185
 
183
- constructor(name, parent) {
186
+ constructor (name, parent) {
184
187
  this.name = name;
185
188
  this.parent = parent;
186
- this.debugInstance = debugModule('pryv:' + this._name());
189
+ this.debugInstance = debugModule('pryv:' + this._name());
187
190
  }
191
+
188
192
  /**
189
193
  * Private
190
194
  */
191
- _name() {
195
+ _name () {
192
196
  if (this.parent) return this.parent._name() + ':' + this.name;
193
197
  return this.name;
194
198
  }
195
199
 
196
- log() {
200
+ log () {
197
201
  const level = arguments[0];
198
202
  const message = hideSensitiveValues(arguments[1]);
199
203
  const context = [];
@@ -204,9 +208,9 @@ class Logger {
204
208
  context.push(inspectAndHide(arguments[i]));
205
209
  }
206
210
  if (context.length === 1) {
207
- meta = {context: context[0]};
211
+ meta = { context: context[0] };
208
212
  } else if (context.length > 1) {
209
- meta = {context: context};
213
+ meta = { context };
210
214
  }
211
215
  globalLog(level, this._name(), message, meta);
212
216
  }
@@ -214,69 +218,69 @@ class Logger {
214
218
  info () { this.log('info', ...arguments); }
215
219
  warn () { this.log('warn', ...arguments); }
216
220
  error () { this.log('error', ...arguments); }
217
- debug () {
221
+ debug () {
218
222
  if (winstonInstance) {
219
- this.log('debug', ...arguments);
223
+ this.log('debug', ...arguments);
220
224
  }
221
- this.debugInstance(...arguments);
222
- }
225
+ this.debugInstance(...arguments);
226
+ }
223
227
 
224
228
  /**
225
229
  * get a "sub" Logger
226
- * @param {Logger} name
230
+ * @param {Logger} name
227
231
  * @returns {Logger}
228
232
  */
229
233
  getLogger (name) {
230
234
  return new Logger(name, this);
231
235
  }
232
236
 
233
- inspect() { inspect(...arguments); }
237
+ inspect () { inspect(...arguments); }
234
238
  }
235
239
 
236
240
  /**
237
241
  * Get a new logger, or root loggger if no name is provided
238
- * @param {string} [name]
242
+ * @param {string} [name]
239
243
  * @returns {Logger}
240
244
  */
241
- function getLogger(name) {
242
- if (! rootLogger) {
243
- throw new Error('Initalize boiler before using logger')
245
+ function getLogger (name) {
246
+ if (!rootLogger) {
247
+ throw new Error('Initalize boiler before using logger');
244
248
  }
245
- if(! name) {
249
+ if (!name) {
246
250
  return rootLogger;
247
251
  }
248
252
  return rootLogger.getLogger(name);
249
253
  }
250
254
 
251
255
  module.exports = {
252
- getLogger: getLogger,
253
- setGlobalName: setGlobalName,
254
- initLoggerWithConfig: initLoggerWithConfig
255
- }
256
+ getLogger,
257
+ setGlobalName,
258
+ initLoggerWithConfig
259
+ };
256
260
 
257
261
  // ----------------- Hide sensite data -------------------- //
258
262
 
259
- function inspectAndHide(o) {
263
+ function inspectAndHide (o) {
260
264
  if (typeof o === 'undefined') return o;
261
265
  if (o instanceof Error) return o;
262
266
  return _inspectAndHide(JSON.parse(JSON.stringify(o))); // clone and remove circular
263
267
  }
264
268
 
265
- function _inspectAndHide(o) {
269
+ function _inspectAndHide (o) {
266
270
  if (typeof o === 'string') {
267
271
  return hideSensitiveValues(o);
268
272
  }
269
273
  if (o !== null && typeof o === 'object') {
270
274
  if (Array.isArray(o)) {
271
275
  const res = [];
272
- for (let item of o) {
276
+ for (const item of o) {
273
277
  res.push(inspectAndHide(item));
274
278
  }
275
279
  return res;
276
280
  }
277
281
 
278
282
  const res = {};
279
- for (let key of Object.keys(o)) {
283
+ for (const key of Object.keys(o)) {
280
284
  if (['password', 'passwordHash', 'newPassword'].includes(key)) {
281
285
  res[key] = '(hidden password)';
282
286
  } else {
@@ -288,7 +292,6 @@ function _inspectAndHide(o) {
288
292
  return o;
289
293
  }
290
294
 
291
-
292
295
  // Hides sensitive values (auth tokens and passwords) in log messages
293
296
  function hideSensitiveValues (msg) {
294
297
  if (typeof msg !== 'string') return msg;
@@ -297,8 +300,8 @@ function hideSensitiveValues (msg) {
297
300
  const mask = '(hidden)';
298
301
 
299
302
  const res = msg
300
- .replace(tokenRegexp, 'auth='+mask)
301
- .replace(passwordRegexp, '$1='+mask);
302
-
303
+ .replace(tokenRegexp, 'auth=' + mask)
304
+ .replace(passwordRegexp, '$1=' + mask);
305
+
303
306
  return res;
304
- }
307
+ }