kayvee 3.16.0 → 3.18.0
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/.eslintrc.js +124 -0
- package/.github/workflows/notify-ci-status.yml +20 -0
- package/.nvmrc +1 -1
- package/.prettierrc.json +1 -0
- package/Makefile +20 -4
- package/build/lib/kayvee.js +13 -17
- package/build/lib/logger/logger.js +84 -76
- package/build/lib/middleware.js +62 -89
- package/build/lib/router/index.js +61 -63
- package/build/package.json +16 -8
- package/build/test/context_logger.js +36 -44
- package/build/test/kayvee.js +16 -16
- package/build/test/logger_test.js +113 -102
- package/build/test/middleware.js +90 -235
- package/build/test/router.js +238 -94
- package/lib/kayvee.ts +19 -7
- package/lib/logger/logger.ts +101 -47
- package/lib/middleware.ts +31 -31
- package/lib/router/index.ts +18 -13
- package/package.json +16 -8
- package/test/context_logger.ts +7 -7
- package/test/kayvee.ts +16 -7
- package/test/logger_test.ts +24 -27
- package/test/middleware.ts +88 -222
- package/test/router.ts +247 -176
- package/tsconfig.json +1 -1
- package/.eslintrc.yml +0 -47
- package/tsd.json +0 -15
- package/tslint.json +0 -134
- package/typings/globals/es6-shim/index.d.ts +0 -666
- package/typings/globals/es6-shim/typings.json +0 -8
- package/typings/index.d.ts +0 -1
- package/typings/mocha/mocha.d.ts +0 -236
- package/typings/node/node.d.ts +0 -2340
- package/typings/tsd.d.ts +0 -2
- package/typings.json +0 -5
package/build/lib/middleware.js
CHANGED
|
@@ -11,11 +11,10 @@ var _ = require("underscore");
|
|
|
11
11
|
/**
|
|
12
12
|
* all relative files path in a directory
|
|
13
13
|
*/
|
|
14
|
-
function walkDirSync(dir, files) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
var f = path.join(dir, file);
|
|
14
|
+
function walkDirSync(dir, files = []) {
|
|
15
|
+
const list = fs.readdirSync(dir);
|
|
16
|
+
list.forEach((file) => {
|
|
17
|
+
const f = path.join(dir, file);
|
|
19
18
|
if (fs.statSync(path.join(dir, file)).isDirectory()) {
|
|
20
19
|
walkDirSync(f, files);
|
|
21
20
|
}
|
|
@@ -23,7 +22,7 @@ function walkDirSync(dir, files) {
|
|
|
23
22
|
files.push(f);
|
|
24
23
|
}
|
|
25
24
|
});
|
|
26
|
-
return files.map(
|
|
25
|
+
return files.map((f) => path.relative(dir, f));
|
|
27
26
|
}
|
|
28
27
|
/**
|
|
29
28
|
* returns a middleware function that checks if path exists in dir.
|
|
@@ -31,12 +30,11 @@ function walkDirSync(dir, files) {
|
|
|
31
30
|
* Files in the directory are prefixed by base_path and compared to
|
|
32
31
|
* req.path
|
|
33
32
|
*/
|
|
34
|
-
function skip_path(dir, base_path) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return function (req, res) { return _(files).contains(req.path) && res.statusCode < 400; };
|
|
33
|
+
function skip_path(dir, base_path = "/") {
|
|
34
|
+
let files = walkDirSync(dir);
|
|
35
|
+
files = files.map((file) => path.join(base_path, file));
|
|
36
|
+
console.error(`KayveeMiddleware: Skipping successful requests for files in ${dir} at ${base_path}`);
|
|
37
|
+
return (req, res) => _(files).contains(req.path) && res.statusCode < 400;
|
|
40
38
|
}
|
|
41
39
|
/**
|
|
42
40
|
* request path
|
|
@@ -52,8 +50,11 @@ function getBaseUrl(req) {
|
|
|
52
50
|
function getQueryParams(req) {
|
|
53
51
|
var url = req.originalUrl || req.url;
|
|
54
52
|
var parsed = require("url").parse(url, true);
|
|
55
|
-
var parsedQueryString = require("qs").parse(parsed.search, {
|
|
56
|
-
|
|
53
|
+
var parsedQueryString = require("qs").parse(parsed.search, {
|
|
54
|
+
allowPrototypes: false,
|
|
55
|
+
ignoreQueryPrefix: true,
|
|
56
|
+
});
|
|
57
|
+
return `?${require("qs").stringify(parsedQueryString)}`;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
60
|
* response size
|
|
@@ -78,8 +79,7 @@ function getResponseTimeNs(req, res) {
|
|
|
78
79
|
return undefined;
|
|
79
80
|
}
|
|
80
81
|
// calculate diff
|
|
81
|
-
var ns = (res._startAt[0] - req._startAt[0]) * 1e9
|
|
82
|
-
+ (res._startAt[1] - req._startAt[1]);
|
|
82
|
+
var ns = (res._startAt[0] - req._startAt[0]) * 1e9 + (res._startAt[1] - req._startAt[1]);
|
|
83
83
|
return ns;
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
@@ -95,8 +95,8 @@ function getIp(req) {
|
|
|
95
95
|
* Log level
|
|
96
96
|
*/
|
|
97
97
|
function getLogLevel(req, res) {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
const statusCode = res.statusCode;
|
|
99
|
+
let result;
|
|
100
100
|
if (statusCode >= 499) {
|
|
101
101
|
result = KayveeLogger.Error;
|
|
102
102
|
}
|
|
@@ -105,66 +105,50 @@ function getLogLevel(req, res) {
|
|
|
105
105
|
}
|
|
106
106
|
return result;
|
|
107
107
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Get canary status
|
|
110
|
-
*/
|
|
111
|
-
function isCanary() {
|
|
112
|
-
return (process.env._CANARY === "1") || ("_POD_SHORTNAME" in process.env && process.env._POD_SHORTNAME.includes("-canary"));
|
|
113
|
-
}
|
|
114
108
|
/*
|
|
115
109
|
* Default handlers
|
|
116
110
|
*/
|
|
117
111
|
var defaultHandlers = [
|
|
118
112
|
// Request method
|
|
119
|
-
|
|
113
|
+
(req) => ({ method: req.method }),
|
|
120
114
|
// Path (URL without query params)
|
|
121
|
-
|
|
115
|
+
(req) => ({ path: getBaseUrl(req) }),
|
|
122
116
|
// Query params
|
|
123
|
-
|
|
117
|
+
(req) => ({ params: getQueryParams(req) }),
|
|
124
118
|
// Response size
|
|
125
|
-
|
|
119
|
+
(req, res) => ({ "response-size": getResponseSize(res) }),
|
|
126
120
|
// Response time (ns)
|
|
127
|
-
|
|
121
|
+
(req, res) => ({ "response-time": getResponseTimeNs(req, res) }),
|
|
128
122
|
// Status code
|
|
129
|
-
|
|
123
|
+
(req, res) => ({ "status-code": res.statusCode }),
|
|
130
124
|
// Ip address
|
|
131
|
-
|
|
125
|
+
(req) => ({ ip: getIp(req) }),
|
|
132
126
|
// Via -- what library/code produced this log?
|
|
133
|
-
|
|
127
|
+
() => ({ via: "kayvee-middleware" }),
|
|
134
128
|
// Kayvee's reserved fields
|
|
135
129
|
// Log level
|
|
136
|
-
|
|
130
|
+
(req, res) => ({ level: getLogLevel(req, res) }),
|
|
137
131
|
// Source -- which app emitted this log?
|
|
138
132
|
// -> Gets passed in among `options` during library initialization
|
|
139
133
|
// Title
|
|
140
|
-
|
|
141
|
-
// During the transition to pods, let's keep the canary field accurate
|
|
142
|
-
// whether it's in the canary pod or a canary container in homepod
|
|
143
|
-
function () { return ({ canary: isCanary() }); },
|
|
134
|
+
() => ({ title: "request-finished" }),
|
|
144
135
|
];
|
|
145
|
-
|
|
146
|
-
function handlerData(handlers) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
args[_i - 1] = arguments[_i];
|
|
150
|
-
}
|
|
151
|
-
var data = {};
|
|
152
|
-
handlers.forEach(function (h) {
|
|
136
|
+
const defaultContextHandlers = [];
|
|
137
|
+
function handlerData(handlers, ...args) {
|
|
138
|
+
const data = {};
|
|
139
|
+
handlers.forEach((h) => {
|
|
153
140
|
try {
|
|
154
|
-
|
|
141
|
+
const handler_data = h(...args);
|
|
155
142
|
_.extend(data, handler_data);
|
|
156
143
|
}
|
|
157
144
|
catch (e) {
|
|
145
|
+
// swallow invalid handler
|
|
158
146
|
}
|
|
159
147
|
});
|
|
160
148
|
return data;
|
|
161
149
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
var args = [];
|
|
165
|
-
for (var _i = 2; _i < arguments.length; _i++) {
|
|
166
|
-
args[_i - 2] = arguments[_i];
|
|
167
|
-
}
|
|
150
|
+
class ContextLogger {
|
|
151
|
+
constructor(logger, handlers, ...args) {
|
|
168
152
|
this.logger = null;
|
|
169
153
|
this.handlers = [];
|
|
170
154
|
this.args = [];
|
|
@@ -172,34 +156,25 @@ var ContextLogger = (function () {
|
|
|
172
156
|
this.handlers = handlers;
|
|
173
157
|
this.args = args;
|
|
174
158
|
}
|
|
175
|
-
|
|
176
|
-
return _.extend(handlerData
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
var _loop_1 = function(func) {
|
|
159
|
+
_contextualData(data) {
|
|
160
|
+
return _.extend(handlerData(this.handlers, ...this.args), data);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const func of KayveeLogger.LEVELS) {
|
|
181
164
|
ContextLogger.prototype[func] = function (title) {
|
|
182
|
-
this[
|
|
165
|
+
this[`${func}D`](title, {});
|
|
183
166
|
};
|
|
184
|
-
ContextLogger.prototype[
|
|
185
|
-
this.logger[
|
|
167
|
+
ContextLogger.prototype[`${func}D`] = function (title, data) {
|
|
168
|
+
this.logger[`${func}D`](title, this._contextualData(data));
|
|
186
169
|
};
|
|
187
|
-
};
|
|
188
|
-
for (var _i = 0, _a = KayveeLogger.LEVELS; _i < _a.length; _i++) {
|
|
189
|
-
var func = _a[_i];
|
|
190
|
-
_loop_1(func);
|
|
191
170
|
}
|
|
192
|
-
|
|
171
|
+
for (const func of KayveeLogger.METRICS) {
|
|
193
172
|
ContextLogger.prototype[func] = function (title, value) {
|
|
194
|
-
this[
|
|
173
|
+
this[`${func}D`](title, value, {});
|
|
195
174
|
};
|
|
196
|
-
ContextLogger.prototype[
|
|
197
|
-
this.logger[
|
|
175
|
+
ContextLogger.prototype[`${func}D`] = function (title, value, data) {
|
|
176
|
+
this.logger[`${func}D`](title, value, this._contextualData(data));
|
|
198
177
|
};
|
|
199
|
-
};
|
|
200
|
-
for (var _b = 0, _c = KayveeLogger.METRICS; _b < _c.length; _b++) {
|
|
201
|
-
var func = _c[_b];
|
|
202
|
-
_loop_2(func);
|
|
203
178
|
}
|
|
204
179
|
/*
|
|
205
180
|
* User configuration is passed via an `options` object.
|
|
@@ -224,20 +199,20 @@ for (var _b = 0, _c = KayveeLogger.METRICS; _b < _c.length; _b++) {
|
|
|
224
199
|
* base_handlers: e.g. [function(request, response) { return {"key":"val"}]
|
|
225
200
|
*
|
|
226
201
|
*/
|
|
227
|
-
var formatLine =
|
|
202
|
+
var formatLine = (options_arg) => {
|
|
228
203
|
var options = options_arg || {};
|
|
229
204
|
// `source` is the one required field
|
|
230
205
|
if (!options.source) {
|
|
231
|
-
throw
|
|
206
|
+
throw Error("Missing required config for 'source' in Kayvee middleware 'options'");
|
|
232
207
|
}
|
|
233
|
-
|
|
234
|
-
return
|
|
208
|
+
const router = KayveeLogger.getGlobalRouter();
|
|
209
|
+
return (tokens, req, res) => {
|
|
235
210
|
// Build a dict of data to log
|
|
236
211
|
var data = { _kvmeta: undefined }; // Adding _kvmeta here to make typescript compile happy
|
|
237
212
|
// Add user-configured request headers
|
|
238
213
|
var custom_headers = options.headers || [];
|
|
239
214
|
var header_data = {};
|
|
240
|
-
custom_headers.forEach(
|
|
215
|
+
custom_headers.forEach((h) => {
|
|
241
216
|
// Header field names are case insensitive, so let's be consistent
|
|
242
217
|
var lc = h.toLowerCase();
|
|
243
218
|
header_data[lc] = req.headers[lc];
|
|
@@ -247,9 +222,9 @@ var formatLine = function (options_arg) {
|
|
|
247
222
|
var custom_handlers = options.handlers || [];
|
|
248
223
|
// Allow user to override `base_handlers`; provide sane default set of handlers
|
|
249
224
|
var base_handlers = options.base_handlers || defaultHandlers;
|
|
250
|
-
base_handlers = base_handlers.concat([
|
|
225
|
+
base_handlers = base_handlers.concat([() => ({ source: options.source })]);
|
|
251
226
|
// Execute custom-handlers THEN base-handlers
|
|
252
|
-
|
|
227
|
+
const all_handlers = custom_handlers.concat(base_handlers);
|
|
253
228
|
_.extend(data, handlerData(all_handlers, req, res));
|
|
254
229
|
if (router) {
|
|
255
230
|
data._kvmeta = router.route(data);
|
|
@@ -257,7 +232,7 @@ var formatLine = function (options_arg) {
|
|
|
257
232
|
return kayvee.format(data);
|
|
258
233
|
};
|
|
259
234
|
};
|
|
260
|
-
|
|
235
|
+
const defaultContextLoggerOpts = {
|
|
261
236
|
enabled: true,
|
|
262
237
|
handlers: defaultContextHandlers,
|
|
263
238
|
};
|
|
@@ -266,8 +241,7 @@ var defaultContextLoggerOpts = {
|
|
|
266
241
|
* @public
|
|
267
242
|
*/
|
|
268
243
|
if (process.env.NODE_ENV === "test") {
|
|
269
|
-
module.exports =
|
|
270
|
-
if (morgan_options === void 0) { morgan_options = { skip: null }; }
|
|
244
|
+
module.exports = (clever_options, morgan_options = { skip: null }) => {
|
|
271
245
|
if (clever_options.ignore_dir) {
|
|
272
246
|
morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
|
|
273
247
|
}
|
|
@@ -276,22 +250,21 @@ if (process.env.NODE_ENV === "test") {
|
|
|
276
250
|
module.exports.ContextLogger = ContextLogger;
|
|
277
251
|
}
|
|
278
252
|
else {
|
|
279
|
-
module.exports =
|
|
280
|
-
if (context_logger_options === void 0) { context_logger_options = defaultContextLoggerOpts; }
|
|
253
|
+
module.exports = (clever_options, context_logger_options = defaultContextLoggerOpts) => {
|
|
281
254
|
// `source` is the one required field
|
|
282
255
|
if (!clever_options.source) {
|
|
283
256
|
throw new Error("Missing required config for 'source' in Kayvee middleware 'options'");
|
|
284
257
|
}
|
|
285
|
-
|
|
286
|
-
|
|
258
|
+
const logger = new KayveeLogger(clever_options.source);
|
|
259
|
+
const morgan_options = {
|
|
287
260
|
stream: process.stderr,
|
|
288
261
|
skip: null,
|
|
289
262
|
};
|
|
290
263
|
if (clever_options.ignore_dir) {
|
|
291
264
|
morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
|
|
292
265
|
}
|
|
293
|
-
|
|
294
|
-
return
|
|
266
|
+
const morgan_logger = morgan(formatLine(clever_options), morgan_options);
|
|
267
|
+
return (req, res, next) => {
|
|
295
268
|
if (context_logger_options.enabled) {
|
|
296
269
|
req.log = new ContextLogger(logger, context_logger_options.handlers, req);
|
|
297
270
|
}
|
|
@@ -4,20 +4,20 @@ var schema = require("./schema_definitions");
|
|
|
4
4
|
var yaml = require("js-yaml");
|
|
5
5
|
var _ = require("underscore");
|
|
6
6
|
var packageJson = require("../../package.json");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const kvVersion = packageJson.version;
|
|
8
|
+
const teamName = process.env._TEAM_OWNER || "UNSET";
|
|
9
|
+
const reEnvvarTokens = new RegExp("\\$\\{(.+?)\\}", "g");
|
|
10
|
+
const reFieldTokens = new RegExp("%\\{(.+?)\\}", "g");
|
|
11
11
|
// For performance reason this code is intentionally redundant and not-inlined.
|
|
12
12
|
// Removing redundancy and inlining this function some how makes performance worst.
|
|
13
13
|
function substituteEnvVars(obj, subber) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
for (
|
|
17
|
-
|
|
14
|
+
const rtn = {};
|
|
15
|
+
const replacer = (s) => s.replace(reEnvvarTokens, (__, p1) => subber(p1));
|
|
16
|
+
for (const key in obj) {
|
|
17
|
+
const val = obj[key];
|
|
18
18
|
if (Array.isArray(val)) {
|
|
19
|
-
|
|
20
|
-
for (
|
|
19
|
+
const updatedVals = Array(val.length);
|
|
20
|
+
for (let i = 0; i < val.length; i++) {
|
|
21
21
|
updatedVals[i] = replacer(val[i]);
|
|
22
22
|
}
|
|
23
23
|
rtn[key] = updatedVals;
|
|
@@ -29,51 +29,51 @@ function substituteEnvVars(obj, subber) {
|
|
|
29
29
|
return rtn;
|
|
30
30
|
}
|
|
31
31
|
function deepKey(obj, key) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const path = key.split(".");
|
|
33
|
+
let idx = 0;
|
|
34
|
+
let val = obj;
|
|
35
35
|
do {
|
|
36
36
|
val = val[path[idx++]];
|
|
37
37
|
} while (val && idx < path.length);
|
|
38
38
|
return val;
|
|
39
39
|
}
|
|
40
40
|
function fieldMatches(obj, field, values) {
|
|
41
|
-
|
|
41
|
+
const val = obj[field] || deepKey(obj, field);
|
|
42
42
|
if (val == null || val === "") {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
if (values[0] === "*") {
|
|
46
46
|
return true;
|
|
47
47
|
}
|
|
48
|
-
for (
|
|
48
|
+
for (let i = 0; i < values.length; i++) {
|
|
49
49
|
if (values[i] === val) {
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
class Rule {
|
|
56
|
+
constructor(name, matchers, output) {
|
|
57
57
|
this.name = null;
|
|
58
58
|
this.matchers = null;
|
|
59
59
|
this.output = null;
|
|
60
60
|
this.name = name;
|
|
61
61
|
this.matchers = matchers;
|
|
62
|
-
|
|
63
|
-
this.output = substituteEnvVars(output,
|
|
64
|
-
|
|
62
|
+
const envMissing = [];
|
|
63
|
+
this.output = substituteEnvVars(output, (k) => {
|
|
64
|
+
const val = process.env[k];
|
|
65
65
|
if (val == null) {
|
|
66
66
|
envMissing.push(k);
|
|
67
67
|
}
|
|
68
68
|
return val;
|
|
69
69
|
});
|
|
70
70
|
if (envMissing.length > 0) {
|
|
71
|
-
throw new Error(
|
|
71
|
+
throw new Error(`Missing env var(s): ${envMissing.join(", ")}`);
|
|
72
72
|
}
|
|
73
|
-
Object.keys(matchers).forEach(
|
|
74
|
-
|
|
73
|
+
Object.keys(matchers).forEach((field) => {
|
|
74
|
+
const fieldVals = matchers[field];
|
|
75
75
|
if (fieldVals.indexOf("*") !== -1 && fieldVals.length > 1) {
|
|
76
|
-
throw new Error(
|
|
76
|
+
throw new Error(`Invalid matcher values in ${name}.${field}.\n` +
|
|
77
77
|
"Wildcard matcher can't co-exist with other matchers.");
|
|
78
78
|
}
|
|
79
79
|
});
|
|
@@ -83,24 +83,24 @@ var Rule = (function () {
|
|
|
83
83
|
this.output.rule = this.name;
|
|
84
84
|
}
|
|
85
85
|
// matches returns true if `msg` matches against this rule
|
|
86
|
-
|
|
87
|
-
for (
|
|
86
|
+
matches(msg) {
|
|
87
|
+
for (const field in this.matchers) {
|
|
88
88
|
if (!fieldMatches(msg, field, this.matchers[field])) {
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
return true;
|
|
93
|
-
}
|
|
93
|
+
}
|
|
94
94
|
// returns the output with kv substitutions performed
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (
|
|
100
|
-
|
|
95
|
+
outputFor(msg) {
|
|
96
|
+
const rtn = {};
|
|
97
|
+
const subst = (__, k) => msg[k] || deepKey(msg, k) || "KEY_NOT_FOUND";
|
|
98
|
+
const replacer = (s) => s.replace(reFieldTokens, subst);
|
|
99
|
+
for (const key in this.output) {
|
|
100
|
+
const val = this.output[key];
|
|
101
101
|
if (Array.isArray(val)) {
|
|
102
|
-
|
|
103
|
-
for (
|
|
102
|
+
const updatedVals = Array(val.length);
|
|
103
|
+
for (let i = 0; i < val.length; i++) {
|
|
104
104
|
updatedVals[i] = replacer(val[i]);
|
|
105
105
|
}
|
|
106
106
|
rtn[key] = updatedVals;
|
|
@@ -110,9 +110,8 @@ var Rule = (function () {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
return rtn;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
}());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
116
115
|
// validateKVConfig ensures that `routes` matches the config schema. We have this
|
|
117
116
|
// function instead of just doing a plain jsonschema.validate in order to get
|
|
118
117
|
// better error messages for the "output" object (by default jsonschema would
|
|
@@ -120,11 +119,11 @@ var Rule = (function () {
|
|
|
120
119
|
// formats, but won't tell you what's wrong because it doesn't let you
|
|
121
120
|
// condition on the output.type property).
|
|
122
121
|
function validateKVConfig(config) {
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const validator = new jsonschema.Validator();
|
|
123
|
+
const results = validator.validate(config, schema);
|
|
125
124
|
return {
|
|
126
125
|
valid: results.valid,
|
|
127
|
-
errors: results.errors.map(
|
|
126
|
+
errors: results.errors.map((err) => err.stack),
|
|
128
127
|
};
|
|
129
128
|
}
|
|
130
129
|
// parseConfig parses and validates the configuration passed as a string. It
|
|
@@ -132,53 +131,53 @@ function validateKVConfig(config) {
|
|
|
132
131
|
// it was successfully parsed, rules is an array of rules, and errors is an
|
|
133
132
|
// array of errors.
|
|
134
133
|
function parseConfig(fileString) {
|
|
135
|
-
|
|
134
|
+
let config;
|
|
136
135
|
try {
|
|
137
136
|
config = yaml.safeLoad(fileString);
|
|
138
137
|
}
|
|
139
138
|
catch (e) {
|
|
140
139
|
return { valid: false, rules: [], errors: [e] };
|
|
141
140
|
}
|
|
142
|
-
|
|
141
|
+
const validateRes = validateKVConfig(config);
|
|
143
142
|
if (!validateRes.valid) {
|
|
144
143
|
return _.assign(validateRes, { rules: [] });
|
|
145
144
|
}
|
|
146
145
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return { valid: true, rules
|
|
146
|
+
const rulesObj = _.mapObject(config.routes, (elem, name) => new Rule(name, elem.matchers, elem.output));
|
|
147
|
+
const rules = _.values(rulesObj);
|
|
148
|
+
return { valid: true, rules, errors: [] };
|
|
150
149
|
}
|
|
151
150
|
catch (e) {
|
|
152
151
|
return { valid: false, rules: [], errors: [e] };
|
|
153
152
|
}
|
|
154
153
|
}
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
class Router {
|
|
155
|
+
constructor(rules) {
|
|
157
156
|
this.rules = null;
|
|
158
157
|
this.rules = rules || [];
|
|
159
158
|
}
|
|
160
159
|
// loadConfig reads in the config located at `filename` and sets the routing
|
|
161
160
|
// rules to what it finds there. It should be a YAML-formatted file with
|
|
162
161
|
// routing rules placed under the `routes` key on the root object.
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
loadConfig(filename) {
|
|
163
|
+
const data = fs.readFileSync(filename, "utf8");
|
|
165
164
|
this._loadConfigString(data);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
}
|
|
166
|
+
_loadConfigString(configStr) {
|
|
167
|
+
const parsedRules = parseConfig(configStr);
|
|
169
168
|
if (!parsedRules.valid) {
|
|
170
169
|
throw new Error(parsedRules.errors);
|
|
171
170
|
}
|
|
172
171
|
this.rules = parsedRules.rules;
|
|
173
|
-
}
|
|
172
|
+
}
|
|
174
173
|
// route matches the log line `msg` against all loaded rules and returns a
|
|
175
174
|
// metadata object describing the outputs it should be sent to based on that
|
|
176
175
|
// matching. logger.ts will attach this to log lines under the `_kvmeta`
|
|
177
176
|
// property.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
for (
|
|
181
|
-
|
|
177
|
+
route(msg) {
|
|
178
|
+
const outputs = [];
|
|
179
|
+
for (let i = 0; i < this.rules.length; i++) {
|
|
180
|
+
const rule = this.rules[i];
|
|
182
181
|
if (rule.matches(msg)) {
|
|
183
182
|
outputs.push(rule.outputFor(msg));
|
|
184
183
|
}
|
|
@@ -189,10 +188,9 @@ var Router = (function () {
|
|
|
189
188
|
kv_language: "js",
|
|
190
189
|
routes: outputs,
|
|
191
190
|
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
}());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
195
193
|
module.exports = {
|
|
196
|
-
Router
|
|
197
|
-
Rule
|
|
194
|
+
Router,
|
|
195
|
+
Rule,
|
|
198
196
|
};
|
package/build/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kayvee",
|
|
3
3
|
"description": "Write data to key=val pairs, for human and machine readability",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.18.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -16,19 +16,27 @@
|
|
|
16
16
|
"underscore": "^1.8.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
+
"@clever/prettier-config": "1.0.0",
|
|
20
|
+
"@types/mocha": "^8.0.3",
|
|
21
|
+
"@types/node": "^12.12.68",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
|
23
|
+
"@typescript-eslint/parser": "^5.2.0",
|
|
19
24
|
"babel-eslint": "^6.0.2",
|
|
20
25
|
"benchmark": "^2.1.1",
|
|
21
|
-
"eslint": "^
|
|
26
|
+
"eslint": "^7.11.0",
|
|
22
27
|
"eslint-config-airbnb": "^7.0.0",
|
|
23
|
-
"eslint-
|
|
24
|
-
"eslint-
|
|
28
|
+
"eslint-config-prettier": "^6.12.0",
|
|
29
|
+
"eslint-formatter-summary": "^1.1.0",
|
|
30
|
+
"eslint-plugin-jsx-a11y": "^6.3.1",
|
|
31
|
+
"eslint-plugin-react": "^7.21.5",
|
|
32
|
+
"eslint-plugin-react-hooks": "^4.1.2",
|
|
25
33
|
"express": "^4.13.4",
|
|
26
|
-
"mocha": "^
|
|
34
|
+
"mocha": "^3.5.3",
|
|
35
|
+
"prettier": "2.1.2",
|
|
27
36
|
"sinon": "^1.17.4",
|
|
28
37
|
"supertest": "^1.2.0",
|
|
29
|
-
"ts-node": "^0.
|
|
30
|
-
"
|
|
31
|
-
"typescript": "^1.8.9"
|
|
38
|
+
"ts-node": "^9.0.0",
|
|
39
|
+
"typescript": "^3.9.10"
|
|
32
40
|
},
|
|
33
41
|
"scripts": {
|
|
34
42
|
"test": "make -Br test",
|