@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/.licenser.yml
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
files:
|
|
2
|
+
"**/*.js":
|
|
3
|
+
header:
|
|
4
|
+
startBlock: |
|
|
5
|
+
/**
|
|
6
|
+
* @license
|
|
7
|
+
linePrefix: " * "
|
|
8
|
+
endBlock: " */"
|
|
9
|
+
license: |
|
|
10
|
+
[{SPDX}]({LICENSE_URL})
|
|
11
|
+
|
|
12
|
+
package.json:
|
|
13
|
+
json:
|
|
14
|
+
force:
|
|
15
|
+
author: "{AUTHOR_NAME} <{AUTHOR_EMAIL}> ({AUTHOR_WEB})"
|
|
16
|
+
license: "{SPDX}"
|
|
17
|
+
sortPackage: true
|
|
18
|
+
|
|
19
|
+
siblingLicenseFile:
|
|
20
|
+
name: "LICENSE"
|
|
21
|
+
|
|
22
|
+
README.md:
|
|
23
|
+
footer:
|
|
24
|
+
startBlock: "\n\n## License\n\n"
|
|
25
|
+
linePrefix: ""
|
|
26
|
+
endBlock: ""
|
|
27
|
+
license: "[{SPDX}]({LICENSE_URL})"
|
|
28
|
+
|
|
29
|
+
ignore:
|
|
30
|
+
- .git
|
|
31
|
+
- .vscode
|
|
32
|
+
- node_modules
|
|
33
|
+
|
|
34
|
+
license: |
|
|
35
|
+
Copyright (c) {YEARS} {AUTHOR_NAME} {AUTHOR_WEB}
|
|
36
|
+
|
|
37
|
+
Redistribution and use in source and binary forms, with or without
|
|
38
|
+
modification, are permitted provided that the following conditions are met:
|
|
39
|
+
|
|
40
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
41
|
+
this list of conditions and the following disclaimer.
|
|
42
|
+
|
|
43
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
44
|
+
this list of conditions and the following disclaimer in the documentation
|
|
45
|
+
and/or other materials provided with the distribution.
|
|
46
|
+
|
|
47
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
48
|
+
may be used to endorse or promote products derived from this software
|
|
49
|
+
without specific prior written permission.
|
|
50
|
+
|
|
51
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
52
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
53
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
54
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
55
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
56
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
57
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
58
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
59
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
60
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
61
|
+
|
|
62
|
+
SPDX-License-Identifier: {SPDX}
|
|
63
|
+
|
|
64
|
+
substitutions:
|
|
65
|
+
YEARS:
|
|
66
|
+
start: 2020
|
|
67
|
+
end: CURRENT_YEAR
|
|
68
|
+
AUTHOR_NAME: "Pryv S.A"
|
|
69
|
+
AUTHOR_EMAIL: info@pryv.com
|
|
70
|
+
AUTHOR_WEB: https://pryv.com
|
|
71
|
+
LICENSE_URL: https://github.com/pryv/pryv-boiler/blob/master/LICENSE
|
|
72
|
+
SPDX: BSD-3-Clause
|
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Copyright (c) 2020–2022 Pryv S.A https://pryv.com
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
7
|
+
this list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
|
|
13
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
14
|
+
may be used to endorse or promote products derived from this software
|
|
15
|
+
without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
18
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
19
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
24
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
25
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
|
|
28
|
+
SPDX-License-Identifier: BSD-3-Clause
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Pryv config and logging boilerplate for Node.js
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### Initialization
|
|
8
|
+
|
|
9
|
+
The "boiler" must be initialized with the application name and configuration files settings, before invoking `getConfig()` or `getLogger()`:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
require('@pryv/boiler').init({
|
|
13
|
+
appName: 'my-app', // This will will be prefixed to any log messages
|
|
14
|
+
baseConfigDir: path.resolve(__dirname, '../config'),
|
|
15
|
+
extraConfigs: [{
|
|
16
|
+
scope: 'extra-config',
|
|
17
|
+
file: path.resolve(__dirname, '../config/extras.js')
|
|
18
|
+
}]
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Configuration
|
|
24
|
+
|
|
25
|
+
We use [nconf](https://www.npmjs.com/package/nconf).
|
|
26
|
+
|
|
27
|
+
#### Base config directory structure
|
|
28
|
+
|
|
29
|
+
During initialization `default-config.yml` and `${NODE_ENV}-config.yml` will be loaded if present in `baseConfigDir`.
|
|
30
|
+
|
|
31
|
+
#### Loading order and precedence
|
|
32
|
+
|
|
33
|
+
The order configuration sources are loaded is important. Each source is loaded with a "scope" name, which can be used to replace the source's contents at any point of time.
|
|
34
|
+
|
|
35
|
+
The configuration sources are loaded in the following order, the first taking precedence:
|
|
36
|
+
|
|
37
|
+
1. **'test'**: Empty slot reserved for tests to override any other config parameter
|
|
38
|
+
2. **'argv'**: Command line arguments
|
|
39
|
+
3. **'env'**: Environment variables
|
|
40
|
+
4. **'base'**: File `${NODE_ENV}-config.yml` (if present) or specified by the `--config` command line argument
|
|
41
|
+
5. **'extra-config'**: Additional source(s) as specified by the `extraConfigs` option (see below)
|
|
42
|
+
6. Defaults:
|
|
43
|
+
- **'default-file'**: `${baseConfigDir}/default-config.yml`
|
|
44
|
+
- **'defaults'**: Hard-coded defaults for logger
|
|
45
|
+
|
|
46
|
+
#### Extra configurations
|
|
47
|
+
|
|
48
|
+
Additional configuration sources can be set or reserved at initialization with the `extraConfigs` option, which expects an array of objects with one of the following structures:
|
|
49
|
+
|
|
50
|
+
- File: `{scope: <name>, file: <path to file> }`. Accepts `.yml`, `.json` and `.js` files. Note `.js` content is loaded with `require(<path to file>)`.
|
|
51
|
+
- Data: `{scope: <name>, key: <optional key>, data: <object> }`. If `key` is provided, the content of `data` will be accessible by this key, otherwise it is loaded at the root of the configuration.
|
|
52
|
+
- Remote URL: `{scope: <name>, key: <optional key>, url: <URL to json content> }`. The JSON contents of this URL will be loaded asynchronously.
|
|
53
|
+
- URL from key: `{scope: <name>, key: <optional key>, urlFromKey: <key> }`. Similar to remove URL, with the URL obtained from an existing configuration key.
|
|
54
|
+
|
|
55
|
+
#### Working with the configuration
|
|
56
|
+
|
|
57
|
+
Retrieving the configuration object:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// synchronous loading
|
|
61
|
+
const { getConfigUnsafe } = require('@pryv/boiler'); // Until all asynchronous sources such as URL are loaded, items might not be available
|
|
62
|
+
const config = await getConfigUnsafe();
|
|
63
|
+
|
|
64
|
+
// asynchronous loading
|
|
65
|
+
const { getConfig } = require('@pryv/boiler');
|
|
66
|
+
const config = await getConfig(); // Here we can be sure all items are fully loaded
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Retrieving settings:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// configuration content is {foo: { bar: 'hello'}};
|
|
73
|
+
const foo = config.get('foo'); // {bar: 'hello'}
|
|
74
|
+
const bar = config.get('foo:bar'); // 'hello'
|
|
75
|
+
|
|
76
|
+
const barExists = config.has('bar'); // true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Assigning settings:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// configuration content is {foo: { bar: 'hello'}};
|
|
83
|
+
config.set('foo', 'bye bye'); // {bar: 'hello'}
|
|
84
|
+
const foo = config.get('foo'); // 'bye bye'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Changing a scope's contents:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// configuration content is {foo: { bar: 'hello'}};
|
|
91
|
+
config.get('foo'); // {bar: 'hello'}
|
|
92
|
+
// replace 'test' existing content (test is always present as the topmost configuration source)
|
|
93
|
+
config.replaceScopeConfig('test', {foo: 'test'});
|
|
94
|
+
config.get('foo'); // 'test'
|
|
95
|
+
// reset content of 'test' scope
|
|
96
|
+
config.replaceScopeConfig('test', {});
|
|
97
|
+
config.get('foo'); // {bar: 'hello'}
|
|
98
|
+
|
|
99
|
+
// Note: for 'test' scope there is a "sugar" function with config.injectTestConfig(object)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### "Learn" mode
|
|
103
|
+
|
|
104
|
+
To help detect unused configuration settings, a "learn" mode can be activated to track all calls to `config.get()` in files.
|
|
105
|
+
|
|
106
|
+
Example when running tests:
|
|
107
|
+
```
|
|
108
|
+
export CONFIG_LEARN_DIR="{absolute path}/service-core/learn-config"
|
|
109
|
+
yarn test
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
### Logging
|
|
114
|
+
|
|
115
|
+
All messages are prefixed by the `appName` value provided at initialization (see above)). `appName` can be postfixed with a string by setting the environment variable `PRYV_BOILER_SUFFIX`, which is useful when spawning several concurrent processes of the same application.
|
|
116
|
+
|
|
117
|
+
#### Using the logger
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const {getLogger} = require('@pryv/boiler');
|
|
121
|
+
|
|
122
|
+
logger.info('Message', item); // standard log
|
|
123
|
+
logger.warn('Message', item); // warning
|
|
124
|
+
logger.error('Message', item); // warning
|
|
125
|
+
logger.debug('Message', item); // debug
|
|
126
|
+
|
|
127
|
+
logger.getLogger('sub'); // new logger name spaced with parent, here '{appName}:sub'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`logger.info()`, `logger.warn()` and `logger.error()` use [Winston](https://www.npmjs.com/package/winston) `logger.debug()` is based on [debug](https://www.npmjs.com/package/debug).
|
|
131
|
+
|
|
132
|
+
#### Outputting debug messages
|
|
133
|
+
|
|
134
|
+
Set the `DEBUG` environment variable. For example: `DEBUG="*" node app.js` will output all debug messages.
|
|
135
|
+
|
|
136
|
+
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
|
+
|
|
138
|
+
#### Log configuration sample
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
logs: {
|
|
142
|
+
console: {
|
|
143
|
+
active: true,
|
|
144
|
+
level: 'info',
|
|
145
|
+
format: {
|
|
146
|
+
color: true,
|
|
147
|
+
time: true,
|
|
148
|
+
aligned: true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
file: {
|
|
152
|
+
active: true,
|
|
153
|
+
path: 'application.log'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
## Contributing
|
|
160
|
+
|
|
161
|
+
`npm run lint` lints the code with [Semi-Standard](https://github.com/standard/semistandard).
|
|
162
|
+
|
|
163
|
+
`npm run license` updates license information.
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
[BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = async function () {
|
|
7
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
8
|
+
return {
|
|
9
|
+
'extra-js-async': 'extra-js-async loaded'
|
|
10
|
+
};
|
|
11
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
const boiler = require('../src');
|
|
7
|
+
const config = boiler.init({ appName: 'sample-double-init' });
|
|
8
|
+
|
|
9
|
+
boiler.init({ appName: 'sample-double-init2' });
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const boiler = require('../src');
|
|
7
|
+
const { getConfigUnsafe, getLogger, getConfig } = require('../src').init({
|
|
8
|
+
appName: 'sample',
|
|
9
|
+
baseConfigDir: path.resolve(__dirname, './configs'),
|
|
10
|
+
extraConfigs: [{
|
|
11
|
+
scope: 'airbrake',
|
|
12
|
+
key: 'logs',
|
|
13
|
+
data: {
|
|
14
|
+
airbrake: {
|
|
15
|
+
active: false,
|
|
16
|
+
projectId: 319858,
|
|
17
|
+
key: '44ca9a107f4546505c7e24c8c598b0c7'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}, {
|
|
21
|
+
scope: 'extra1',
|
|
22
|
+
file: path.resolve(__dirname, './configs/extra-config.yml')
|
|
23
|
+
}, {
|
|
24
|
+
scope: 'extra2',
|
|
25
|
+
file: path.resolve(__dirname, './configs/extra-config.json')
|
|
26
|
+
}, {
|
|
27
|
+
scope: 'extra3',
|
|
28
|
+
file: path.resolve(__dirname, './configs/extra-config.js')
|
|
29
|
+
}, {
|
|
30
|
+
scope: 'extra4',
|
|
31
|
+
data: {
|
|
32
|
+
'extra-4-data': 'extra 4 object loaded'
|
|
33
|
+
}
|
|
34
|
+
}, {
|
|
35
|
+
scope: 'extra5',
|
|
36
|
+
key: 'extra-5-data',
|
|
37
|
+
data: 'extra 5 object loaded'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
scope: 'extra-js-async',
|
|
41
|
+
fileAsync: path.resolve(__dirname, './configs/extra-js-async.js')
|
|
42
|
+
}, {
|
|
43
|
+
scope: 'pryv.li',
|
|
44
|
+
url: 'https://reg.pryv.li/service/info'
|
|
45
|
+
}, {
|
|
46
|
+
scope: 'pryv.me',
|
|
47
|
+
key: 'service',
|
|
48
|
+
url: 'https://reg.pryv.me/service/info'
|
|
49
|
+
}, {
|
|
50
|
+
scope: 'pryv.me-def',
|
|
51
|
+
key: 'definitions',
|
|
52
|
+
urlFromKey: 'service:assets:definitions'
|
|
53
|
+
}, {
|
|
54
|
+
scope: 'ondisk-scope',
|
|
55
|
+
key: 'ondisk',
|
|
56
|
+
url: 'file://' + path.resolve(__dirname, './remotes/ondisk.json')
|
|
57
|
+
}, {
|
|
58
|
+
plugin: require('./plugins/plugin-sync')
|
|
59
|
+
}, {
|
|
60
|
+
pluginAsync: require('./plugins/plugin-async')
|
|
61
|
+
}]
|
|
62
|
+
}, function () {
|
|
63
|
+
console.log('Ready');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const config = getConfigUnsafe(true);
|
|
67
|
+
|
|
68
|
+
const rootLogger = getLogger();
|
|
69
|
+
rootLogger.debug('hello root');
|
|
70
|
+
|
|
71
|
+
const indexLogger = getLogger('index');
|
|
72
|
+
indexLogger.debug('hello index');
|
|
73
|
+
indexLogger.info('extra Yaml', config.get('extra-yaml'));
|
|
74
|
+
indexLogger.info('extra Json', config.get('extra-json'));
|
|
75
|
+
indexLogger.info('extra Js', config.get('extra-js'));
|
|
76
|
+
indexLogger.info('extra 4 data', config.get('extra-4-data'));
|
|
77
|
+
indexLogger.info('extra 5 data', config.get('extra-5-data'));
|
|
78
|
+
indexLogger.info('default yaml', config.get('default-yaml'));
|
|
79
|
+
indexLogger.info('Default Service Name', config.get('service:name'));
|
|
80
|
+
|
|
81
|
+
config.replaceScopeConfig('extra5', { 'extra-5-data': 'new Extra 5 data' });
|
|
82
|
+
indexLogger.info('extra 5 data', config.get('extra-5-data'));
|
|
83
|
+
|
|
84
|
+
const subLogger = indexLogger.getLogger('sub');
|
|
85
|
+
subLogger.debug('hello sub');
|
|
86
|
+
indexLogger.info('plugin sync', config.get('plugin-sync'));
|
|
87
|
+
indexLogger.info('hide stuff auth=c08r0xs95xlb1xgssmp6tr7c0000gp', { password: 'toto' });
|
|
88
|
+
|
|
89
|
+
(async () => {
|
|
90
|
+
await getConfig();
|
|
91
|
+
await boiler.notifyAirbrake('Hello');
|
|
92
|
+
indexLogger.info('pryv.li serial: ', config.get('serial'));
|
|
93
|
+
indexLogger.info('pryv.me name: ', config.get('service:name'));
|
|
94
|
+
indexLogger.info('Favicon: ', config.get('definitions:favicon:default:url'));
|
|
95
|
+
indexLogger.info('OnDisk: ', config.get('ondisk'));
|
|
96
|
+
indexLogger.info('Plugin async: ', config.get('plugin-async'));
|
|
97
|
+
indexLogger.info('Service Name', config.get('service:name'));
|
|
98
|
+
|
|
99
|
+
indexLogger.info('Scope of foo', config.getScopeAndValue('foo'));
|
|
100
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pryv/boiler",
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Logging and config boilerplate library for Node.js apps and services at Pryv",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"Pryv",
|
|
8
|
+
"Config",
|
|
9
|
+
"Logging"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/pryv/pryv-boiler#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/pryv/pryv-boiler/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/pryv/pryv-boiler.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "BSD-3-Clause",
|
|
20
|
+
"author": "Pryv S.A <info@pryv.com> (https://pryv.com)",
|
|
21
|
+
"main": "src/index",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"license": "source-licenser -c .licenser.yml .",
|
|
24
|
+
"lint": "semistandard"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@airbrake/node": "^1.4.2",
|
|
28
|
+
"debug": "^4.3.4",
|
|
29
|
+
"js-yaml": "^4.0.0",
|
|
30
|
+
"nconf": "^0.12.0",
|
|
31
|
+
"semistandard": "^16.0.1",
|
|
32
|
+
"source-licenser": "^2.0.4",
|
|
33
|
+
"superagent": "^7.1.2",
|
|
34
|
+
"winston": "^3.7.2",
|
|
35
|
+
"winston-daily-rotate-file": "^4.6.1"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/airbrake.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/pryv-boiler/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* read all files in CONFIG_LEARN_DIR and output a readable result
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const learnDir = process.env.CONFIG_LEARN_DIR || path.resolve(__dirname, '../../../learn-config');
|
|
11
|
+
console.log('Looking for learning files in: ' + learnDir);
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
const apps = {};
|
|
15
|
+
|
|
16
|
+
// get all files
|
|
17
|
+
const files = fs.readdirSync(learnDir);
|
|
18
|
+
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
if (file.endsWith('-calls.csv')) {
|
|
21
|
+
handleCSV(path.join(learnDir, file));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
if (file.endsWith('-config.json')) {
|
|
27
|
+
handleConfig(path.join(learnDir, file));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handleConfig (file) {
|
|
32
|
+
/* eslint-disable-next-line no-useless-escape */
|
|
33
|
+
const appNameSearch = /.*\/([a-zA-Z\-]*)[0-9]{1,2}-config.json/;
|
|
34
|
+
const appName = file.match(appNameSearch)[1];
|
|
35
|
+
const config = require(file).config;
|
|
36
|
+
const calls = apps[appName].calls;
|
|
37
|
+
|
|
38
|
+
checkExistsAndFlag(config, calls);
|
|
39
|
+
|
|
40
|
+
function checkExistsAndFlag (configItem, callsItem) {
|
|
41
|
+
if (typeof configItem !== 'object' || Array.isArray(configItem)) return;
|
|
42
|
+
for (const key of Object.keys(configItem)) {
|
|
43
|
+
if (key !== 'calls') {
|
|
44
|
+
if (typeof callsItem[key] === 'undefined') {
|
|
45
|
+
callsItem[key] = 'UNUSED';
|
|
46
|
+
// console.log(callsItem)
|
|
47
|
+
} else {
|
|
48
|
+
checkExistsAndFlag(configItem[key], callsItem[key]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleCSV (file) {
|
|
56
|
+
/* eslint-disable-next-line no-useless-escape */
|
|
57
|
+
const appNameSearch = /.*\/([a-zA-Z\-]*)[0-9]{1,2}-calls.csv/;
|
|
58
|
+
const appName = file.match(appNameSearch)[1];
|
|
59
|
+
|
|
60
|
+
// initialize apps.appname if needed
|
|
61
|
+
if (!apps[appName]) {
|
|
62
|
+
apps[appName] = {
|
|
63
|
+
calls: {},
|
|
64
|
+
rank: {}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const filelines = fs.readFileSync(file, 'utf-8').split('\n');
|
|
69
|
+
for (const line of filelines) {
|
|
70
|
+
// -- calls count
|
|
71
|
+
const [path, call] = line.split(';');
|
|
72
|
+
const key = deepFind(apps[appName].calls, path + ':calls');
|
|
73
|
+
if (!key[call]) key[call] = 0;
|
|
74
|
+
key[call]++;
|
|
75
|
+
// -- ranking
|
|
76
|
+
apps[appName].rank[line] = key[call];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function deepFind (obj, path) {
|
|
81
|
+
const paths = path.split(':');
|
|
82
|
+
let current = obj;
|
|
83
|
+
let i;
|
|
84
|
+
|
|
85
|
+
for (i = 0; i < paths.length; ++i) {
|
|
86
|
+
if (current[paths[i]] === undefined) {
|
|
87
|
+
current[paths[i]] = {}; // initialize path while searching
|
|
88
|
+
}
|
|
89
|
+
current = current[paths[i]];
|
|
90
|
+
}
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// sort and filter ranking
|
|
95
|
+
const KEEP_HIGHER_N = 10;
|
|
96
|
+
|
|
97
|
+
for (const appName of Object.keys(apps)) {
|
|
98
|
+
const app = apps[appName];
|
|
99
|
+
const arrayOfCalls = [];
|
|
100
|
+
for (const callLine of Object.keys(app.rank)) {
|
|
101
|
+
arrayOfCalls.push({ count: app.rank[callLine], line: callLine });
|
|
102
|
+
}
|
|
103
|
+
const arrayOfCallsSorted = arrayOfCalls.sort((a, b) => { return b.count - a.count; });
|
|
104
|
+
// replace rank info
|
|
105
|
+
app.rank = arrayOfCallsSorted.slice(0, KEEP_HIGHER_N);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fs.writeFileSync(path.join(learnDir, 'compute.json'), JSON.stringify(apps, null, 2));
|
|
109
|
+
// console.log(apps);
|