@pryv/boiler 1.0.8 → 1.2.3
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 +20 -0
- package/examples/index.js +1 -11
- package/package.json +9 -8
- package/src/compute-learn.js +1 -1
- package/src/config.js +12 -18
- package/src/index.js +6 -14
- package/src/logging.js +21 -3
- 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,20 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
init,
|
|
4
|
+
log
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async function init(settings) {
|
|
8
|
+
console.log('CUSTOM LOGGER initilaized with', settings);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {string} level one of 'debug', 'info', 'warn', 'error'
|
|
14
|
+
* @param {string} key ':' namespaced keys
|
|
15
|
+
* @param {message} message
|
|
16
|
+
* @param {object} meta
|
|
17
|
+
*/
|
|
18
|
+
function log(level, key, text, meta) {
|
|
19
|
+
console.log('Custom: ' + JSON.stringify({level, key, text, meta}));
|
|
20
|
+
}
|
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.3",
|
|
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,9 +46,6 @@ const defaults = {
|
|
|
46
46
|
rotation: {
|
|
47
47
|
isActive: false
|
|
48
48
|
}
|
|
49
|
-
},
|
|
50
|
-
airbrake: {
|
|
51
|
-
active: false
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
};
|
|
@@ -76,6 +73,7 @@ class Config {
|
|
|
76
73
|
* @param {string} appName
|
|
77
74
|
* @param {string} [learnDirectory] - (optional) if set, all .get() calls will be tracked in this files in this directory
|
|
78
75
|
* @param {string} [options.baseConfigDir] - (optional) directory to use to look for configs (default, env)
|
|
76
|
+
* @param {string} [options.baseFilesDir] - (optional) directory to use for `file://` relative path
|
|
79
77
|
* @param {Array<ConfigFile|ConfigPlugin|ConfigData|ConfigRemoteURL|ConfigRemoteURLFromKey>} [options.extras] - (optional) and array of extra files or plugins to load (synchronously or async)
|
|
80
78
|
* @param {Object} logging
|
|
81
79
|
* @returns {Config} this
|
|
@@ -83,6 +81,7 @@ class Config {
|
|
|
83
81
|
initSync(options, logging) {
|
|
84
82
|
this.appName = options.appName;
|
|
85
83
|
this.learnDirectoryAndFilename = getLearnFilename(options.appName, options.learnDirectory);
|
|
84
|
+
this.baseFilesDir = options.baseFilesDir || process.cwd();
|
|
86
85
|
|
|
87
86
|
const logger = this.logger = logging.getLogger('config');
|
|
88
87
|
const store = this.store = new nconf.Provider();
|
|
@@ -192,6 +191,7 @@ class Config {
|
|
|
192
191
|
const store = this.store;
|
|
193
192
|
const logger = this.logger;
|
|
194
193
|
const baseConfigDir = this.baseConfigDir;
|
|
194
|
+
const baseFilesDir = this.baseFilesDir;
|
|
195
195
|
|
|
196
196
|
async function loadUrl(scope, key, url) {
|
|
197
197
|
if (typeof url === 'undefined' || url === null) {
|
|
@@ -201,7 +201,7 @@ class Config {
|
|
|
201
201
|
|
|
202
202
|
let res = null;
|
|
203
203
|
if (isFileUrl(url)) {
|
|
204
|
-
res = loadFromFile(url);
|
|
204
|
+
res = loadFromFile(url, baseFilesDir);
|
|
205
205
|
} else {
|
|
206
206
|
res = await loadFromUrl(url);
|
|
207
207
|
}
|
|
@@ -265,13 +265,13 @@ class Config {
|
|
|
265
265
|
|
|
266
266
|
/**
|
|
267
267
|
* Retreive value
|
|
268
|
-
* @param {string} key
|
|
268
|
+
* @param {string} [key] if no key is provided all the config is returned
|
|
269
269
|
*/
|
|
270
270
|
get(key) {
|
|
271
271
|
if (! this.store) { throw(new Error('Config not yet initialized'))}
|
|
272
|
-
learn(this.learnDirectoryAndFilename, key);
|
|
273
272
|
const value = this.store.get(key);
|
|
274
273
|
if (typeof value === 'undefined') this.logger.debug('get: [' + key +'] is undefined');
|
|
274
|
+
learn(this.learnDirectoryAndFilename, key);
|
|
275
275
|
return value;
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -338,17 +338,16 @@ module.exports = Config;
|
|
|
338
338
|
const FILE_PROTOCOL = 'file://';
|
|
339
339
|
const FILE_PROTOCOL_LENGTH = FILE_PROTOCOL.length;
|
|
340
340
|
|
|
341
|
-
async function loadFromUrl(
|
|
342
|
-
const res = await superagent.get(
|
|
341
|
+
async function loadFromUrl(url) {
|
|
342
|
+
const res = await superagent.get(url);
|
|
343
343
|
return res.body;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
function loadFromFile(fileUrl ) {
|
|
346
|
+
function loadFromFile(fileUrl, baseFilesDir) {
|
|
347
347
|
const filePath = stripFileProtocol(fileUrl);
|
|
348
348
|
|
|
349
349
|
if (isRelativePath(filePath)) {
|
|
350
|
-
|
|
351
|
-
fileUrl = path.resolve(serviceCorePath, filePath);
|
|
350
|
+
fileUrl = path.resolve(baseFilesDir, filePath);
|
|
352
351
|
fileUrl = 'file://' + fileUrl;
|
|
353
352
|
} else {
|
|
354
353
|
// absolute path, do nothing.
|
|
@@ -390,19 +389,14 @@ function learn(learnDirectoryAndFilename, key) {
|
|
|
390
389
|
if (learnDirectoryAndFilename) {
|
|
391
390
|
const caller_line = (new Error()).stack.split('\n')[3]; // get callee name and line
|
|
392
391
|
const index = caller_line.indexOf("at ");
|
|
393
|
-
str = key + ';' + caller_line.slice(index+3, caller_line.length) + '\n';
|
|
392
|
+
const str = key + ';' + caller_line.slice(index+3, caller_line.length) + '\n';
|
|
394
393
|
fs.appendFileSync(learnDirectoryAndFilename + '-calls.csv', str);
|
|
395
394
|
}
|
|
396
395
|
}
|
|
397
396
|
|
|
398
397
|
function saveConfig(learnDirectoryAndFilename, store) {
|
|
399
398
|
if (learnDirectoryAndFilename) {
|
|
400
|
-
|
|
401
|
-
let filename;
|
|
402
|
-
do {
|
|
403
|
-
filename =learnDirectoryAndFilename + '-config.json';
|
|
404
|
-
i++;
|
|
405
|
-
} while(fs.existsSync(filename));
|
|
399
|
+
const filename = learnDirectoryAndFilename + '-config.json';
|
|
406
400
|
fs.writeFileSync(filename, JSON.stringify({stores: store.stores, config: store.get()}, null, 2));
|
|
407
401
|
}
|
|
408
402
|
}
|
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,7 +23,7 @@ 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
28
|
getConfig: getConfig,
|
|
35
29
|
/**
|
|
@@ -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,6 +8,7 @@ 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
|
|
|
@@ -73,12 +74,16 @@ function generateFormat(options) {
|
|
|
73
74
|
/**
|
|
74
75
|
* Helper to pass log instructions to winston
|
|
75
76
|
*/
|
|
76
|
-
function globalLog(level,
|
|
77
|
+
function globalLog(level, key, message, context) {
|
|
78
|
+
const text = `[${key}] ${message}`;
|
|
77
79
|
if (winstonInstance) {
|
|
78
80
|
winstonInstance[level](text, context);
|
|
79
81
|
} else {
|
|
80
82
|
console.log('Logger not initialized: ', ...arguments);
|
|
81
83
|
}
|
|
84
|
+
if (customLoggerInstance) {
|
|
85
|
+
customLoggerInstance.log(level, key, message, context);
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
|
|
@@ -132,6 +137,13 @@ async function initLoggerWithConfig(config) {
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
}
|
|
140
|
+
|
|
141
|
+
// custom
|
|
142
|
+
if (config.get('logs:custom:active')) {
|
|
143
|
+
customLoggerInstance = require(config.get('logs:custom:path'));
|
|
144
|
+
await customLoggerInstance.init(config.get('logs:custom:settings'));
|
|
145
|
+
}
|
|
146
|
+
|
|
135
147
|
rootLogger.debug('Logger Initialized');
|
|
136
148
|
};
|
|
137
149
|
|
|
@@ -183,7 +195,7 @@ class Logger {
|
|
|
183
195
|
|
|
184
196
|
log() {
|
|
185
197
|
const level = arguments[0];
|
|
186
|
-
const
|
|
198
|
+
const message = hideSensitiveValues(arguments[1]);
|
|
187
199
|
const context = [];
|
|
188
200
|
|
|
189
201
|
let meta;
|
|
@@ -196,7 +208,7 @@ class Logger {
|
|
|
196
208
|
} else if (context.length > 1) {
|
|
197
209
|
meta = {context: context};
|
|
198
210
|
}
|
|
199
|
-
globalLog(level,
|
|
211
|
+
globalLog(level, this._name(), message, meta);
|
|
200
212
|
}
|
|
201
213
|
|
|
202
214
|
info () { this.log('info', ...arguments); }
|
|
@@ -212,6 +224,7 @@ class Logger {
|
|
|
212
224
|
/**
|
|
213
225
|
* get a "sub" Logger
|
|
214
226
|
* @param {Logger} name
|
|
227
|
+
* @returns {Logger}
|
|
215
228
|
*/
|
|
216
229
|
getLogger (name) {
|
|
217
230
|
return new Logger(name, this);
|
|
@@ -220,6 +233,11 @@ class Logger {
|
|
|
220
233
|
inspect() { inspect(...arguments); }
|
|
221
234
|
}
|
|
222
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Get a new logger, or root loggger if no name is provided
|
|
238
|
+
* @param {string} [name]
|
|
239
|
+
* @returns {Logger}
|
|
240
|
+
*/
|
|
223
241
|
function getLogger(name) {
|
|
224
242
|
if (! rootLogger) {
|
|
225
243
|
throw new Error('Initalize boiler before using logger')
|
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
|
-
};
|