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