kayvee 3.18.0 → 4.0.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.
Files changed (58) hide show
  1. package/README.md +147 -202
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +29 -0
  5. package/dist/kayvee.d.ts +12 -0
  6. package/dist/kayvee.d.ts.map +1 -0
  7. package/{build/lib → dist}/kayvee.js +17 -30
  8. package/dist/logger/logger.d.ts +49 -0
  9. package/dist/logger/logger.d.ts.map +1 -0
  10. package/{build/lib → dist}/logger/logger.js +91 -83
  11. package/dist/middleware.d.ts +23 -0
  12. package/dist/middleware.d.ts.map +1 -0
  13. package/dist/middleware.js +196 -0
  14. package/dist/package.json +89 -0
  15. package/dist/router/index.d.ts +23 -0
  16. package/dist/router/index.d.ts.map +1 -0
  17. package/{build/lib → dist}/router/index.js +33 -45
  18. package/package.json +63 -27
  19. package/.circleci/config.yml +0 -25
  20. package/.eslintrc.js +0 -124
  21. package/.github/workflows/notify-ci-status.yml +0 -20
  22. package/.nvmrc +0 -1
  23. package/.prettierrc.json +0 -1
  24. package/Makefile +0 -55
  25. package/benchmarks/data/.keep +0 -1
  26. package/benchmarks/data/corpus-basic.json +0 -22
  27. package/benchmarks/data/corpus-pathological.json +0 -22
  28. package/benchmarks/data/corpus-realistic.json +0 -22
  29. package/benchmarks/data/kvconfig-basic.yml +0 -7
  30. package/benchmarks/data/kvconfig-pathological.yml +0 -222
  31. package/benchmarks/data/kvconfig-realistic.yml +0 -39
  32. package/benchmarks/routing.js +0 -116
  33. package/build/lib/logger/helpers.js +0 -0
  34. package/build/lib/middleware.js +0 -274
  35. package/build/package.json +0 -53
  36. package/build/test/context_logger.js +0 -69
  37. package/build/test/kayvee.js +0 -36
  38. package/build/test/logger_test.js +0 -345
  39. package/build/test/middleware.js +0 -556
  40. package/build/test/router.js +0 -451
  41. package/index.js +0 -7
  42. package/lib/kayvee.ts +0 -73
  43. package/lib/logger/helpers.ts +0 -0
  44. package/lib/logger/logger.ts +0 -312
  45. package/lib/middleware.ts +0 -317
  46. package/lib/router/index.ts +0 -234
  47. package/lib/router/schema_definitions.json +0 -158
  48. package/test/context_logger.ts +0 -76
  49. package/test/kayvee.ts +0 -50
  50. package/test/kvconfig.yml +0 -14
  51. package/test/logger_test.ts +0 -378
  52. package/test/middleware.ts +0 -632
  53. package/test/router.ts +0 -558
  54. package/test/static/empty.css +0 -0
  55. package/test/static/js/empty.js +0 -0
  56. package/test/tests.json +0 -100
  57. package/tsconfig.json +0 -10
  58. /package/{build/lib → dist}/router/schema_definitions.json +0 -0
@@ -1,312 +0,0 @@
1
- var _ = require("underscore");
2
- var kv = require("../kayvee");
3
- var router = require("../router");
4
-
5
- var LEVELS = {
6
- Trace: "trace",
7
- Debug: "debug",
8
- Info: "info",
9
- Warning: "warning",
10
- Error: "error",
11
- Critical: "critical",
12
- };
13
-
14
- var LOG_LEVEL_ENUM = {
15
- trace: 0,
16
- debug: 1,
17
- info: 2,
18
- warning: 3,
19
- error: 4,
20
- critical: 5,
21
- };
22
-
23
- const assign = Object.assign || _.assign; // Use the faster Object.assign if possible
24
-
25
- let globalRouter;
26
-
27
- function setGlobalRouting(filename) {
28
- globalRouter = new router.Router();
29
- globalRouter.loadConfig(filename);
30
- }
31
-
32
- function getGlobalRouter() {
33
- return globalRouter;
34
- }
35
-
36
- // This is a port from kayvee-go/logger/logger.go
37
- class Logger {
38
- formatter = null;
39
- logLvl = null;
40
- globals = null;
41
- logWriter = null;
42
- logRouter = null;
43
- asyncLocalStorage = null;
44
-
45
- constructor(
46
- source,
47
- logLvl = process.env.KAYVEE_LOG_LEVEL,
48
- formatter = kv.format,
49
- output = console.error,
50
- ) {
51
- this.formatter = formatter;
52
- this.logLvl = this._validateLogLvl(logLvl);
53
- this.globals = {};
54
- this.globals.source = source;
55
- this.logWriter = output;
56
- this.asyncLocalStorage = null;
57
-
58
- if (process.env._TEAM_OWNER) {
59
- this.globals.team = process.env._TEAM_OWNER;
60
- }
61
- if (process.env._DEPLOY_ENV) {
62
- this.globals.deploy_env = process.env._DEPLOY_ENV;
63
- }
64
- if (process.env._EXECUTION_NAME) {
65
- this.globals.wf_id = process.env._EXECUTION_NAME;
66
- }
67
- if (process.env._POD_ID) {
68
- this.globals["pod-id"] = process.env._POD_ID;
69
- }
70
- if (process.env._POD_SHORTNAME) {
71
- this.globals["pod-shortname"] = process.env._POD_SHORTNAME;
72
- }
73
- if (process.env._POD_REGION) {
74
- this.globals["pod-region"] = process.env._POD_REGION;
75
- }
76
- if (process.env._POD_ACCOUNT) {
77
- this.globals["pod-account"] = process.env._POD_ACCOUNT;
78
- }
79
- }
80
-
81
- setAsyncLocalStorage(asyncLocalStorage) {
82
- this.asyncLocalStorage = asyncLocalStorage;
83
- }
84
-
85
- setRouter(r) {
86
- this.logRouter = r;
87
- }
88
-
89
- setConfig(source, logLvl, formatter, output) {
90
- this.globals.source = source;
91
- this.logLvl = this._validateLogLvl(logLvl);
92
- this.formatter = formatter;
93
- this.logWriter = output;
94
- return this.logWriter;
95
- }
96
-
97
- _validateLogLvl(logLvl) {
98
- if (logLvl == null) {
99
- return LEVELS.Debug;
100
- }
101
- for (var key in LEVELS) {
102
- if (Object.prototype.hasOwnProperty.call(LEVELS, key)) {
103
- var value = LEVELS[key];
104
- if (logLvl.toLowerCase() === value) {
105
- return value;
106
- }
107
- }
108
- }
109
- return LEVELS.Debug;
110
- }
111
-
112
- setLogLevel(logLvl) {
113
- this.logLvl = this._validateLogLvl(logLvl);
114
- return this.logLvl;
115
- }
116
-
117
- setFormatter(formatter) {
118
- this.formatter = formatter;
119
- return this.formatter;
120
- }
121
-
122
- setOutput(output) {
123
- this.logWriter = output;
124
- return this.logWriter;
125
- }
126
-
127
- trace(title) {
128
- this.traceD(title, {});
129
- }
130
-
131
- debug(title) {
132
- this.debugD(title, {});
133
- }
134
-
135
- info(title) {
136
- this.infoD(title, {});
137
- }
138
-
139
- warn(title) {
140
- this.warnD(title, {});
141
- }
142
-
143
- error(title) {
144
- this.errorD(title, {});
145
- }
146
-
147
- critical(title) {
148
- this.criticalD(title, {});
149
- }
150
-
151
- counter(title) {
152
- this.counterD(title, 1, {});
153
- }
154
-
155
- gauge(title, value) {
156
- this.gaugeD(title, value, {});
157
- }
158
-
159
- traceD(title, data) {
160
- this._logWithLevel(
161
- LEVELS.Trace,
162
- {
163
- title,
164
- },
165
- data,
166
- );
167
- }
168
-
169
- debugD(title, data) {
170
- this._logWithLevel(
171
- LEVELS.Debug,
172
- {
173
- title,
174
- },
175
- data,
176
- );
177
- }
178
-
179
- infoD(title, data) {
180
- this._logWithLevel(
181
- LEVELS.Info,
182
- {
183
- title,
184
- },
185
- data,
186
- );
187
- }
188
-
189
- warnD(title, data) {
190
- this._logWithLevel(
191
- LEVELS.Warning,
192
- {
193
- title,
194
- },
195
- data,
196
- );
197
- }
198
-
199
- errorD(title, data) {
200
- this._logWithLevel(
201
- LEVELS.Error,
202
- {
203
- title,
204
- },
205
- data,
206
- );
207
- }
208
-
209
- criticalD(title, data) {
210
- this._logWithLevel(
211
- LEVELS.Critical,
212
- {
213
- title,
214
- },
215
- data,
216
- );
217
- }
218
-
219
- counterD(title, value, data) {
220
- this._logWithLevel(
221
- LEVELS.Info,
222
- {
223
- title,
224
- value,
225
- type: "counter",
226
- },
227
- data,
228
- );
229
- }
230
-
231
- gaugeD(title, value, data) {
232
- this._logWithLevel(
233
- LEVELS.Info,
234
- {
235
- title,
236
- value,
237
- type: "gauge",
238
- },
239
- data,
240
- );
241
- }
242
-
243
- _logWithLevel(logLvl, metadata, userdata) {
244
- if (LOG_LEVEL_ENUM[logLvl] < LOG_LEVEL_ENUM[this.logLvl]) {
245
- return;
246
- }
247
- // I'm not clever enough to want to do these in one line without extra vars.
248
- // We're on a REALLY old version of TS compiling to ES5. So I don't get a lot of the fancy tools
249
- // like ?. and ??.
250
- const store = this.asyncLocalStorage && this.asyncLocalStorage.getStore();
251
- const storeData = store || { get: () => ({}) };
252
- const contextData = storeData.get("context") ? storeData.get("context") : {};
253
- const plainContextData =
254
- contextData instanceof Map ? Object.fromEntries(contextData) : contextData;
255
-
256
- var data = assign({ level: logLvl }, this.globals, metadata, plainContextData, userdata);
257
-
258
- if (this.logRouter) {
259
- data._kvmeta = this.logRouter.route(data);
260
- } else if (globalRouter) {
261
- data._kvmeta = globalRouter.route(data);
262
- }
263
- this.logWriter(this.formatter(data));
264
- }
265
- }
266
-
267
- module.exports = Logger;
268
- module.exports.setGlobalRouting = setGlobalRouting;
269
- module.exports.getGlobalRouter = getGlobalRouter;
270
- module.exports.mockRouting = (cb) => {
271
- const _logWithLevel: any = Logger.prototype._logWithLevel;
272
-
273
- if (_logWithLevel.isMocked) {
274
- throw Error("Nested kv.mockRouting calls are not supported");
275
- }
276
-
277
- const ruleMatches = {};
278
-
279
- Logger.prototype._logWithLevel = function (logLvl, metadata, userdata) {
280
- const formatter = this.formatter;
281
- const logWriter = this.logWriter;
282
-
283
- this.formatter = (msg) => msg;
284
- this.logWriter = (msg) => {
285
- if (!msg._kvmeta) {
286
- return;
287
- }
288
-
289
- msg._kvmeta.routes.forEach((route) => {
290
- ruleMatches[route.rule] = (ruleMatches[route.rule] || []).concat(route);
291
- });
292
- };
293
-
294
- _logWithLevel.call(this, logLvl, metadata, userdata);
295
-
296
- this.formatter = formatter;
297
- this.logWriter = logWriter;
298
- };
299
-
300
- const stfuTypeScript: any = Logger.prototype._logWithLevel;
301
- stfuTypeScript.isMocked = true;
302
-
303
- const done = () => {
304
- Logger.prototype._logWithLevel = _logWithLevel;
305
- return ruleMatches;
306
- };
307
-
308
- cb(done);
309
- };
310
- _.extend(module.exports, LEVELS);
311
- module.exports.LEVELS = ["trace", "debug", "info", "warn", "error", "critical"];
312
- module.exports.METRICS = ["counter", "gauge"];
package/lib/middleware.ts DELETED
@@ -1,317 +0,0 @@
1
- /**
2
- * Module dependencies.
3
- * @private
4
- */
5
-
6
- var fs = require("fs");
7
- var path = require("path");
8
-
9
- var kayvee = require("../lib/kayvee");
10
- var KayveeLogger = require("../lib/logger/logger");
11
- var morgan = require("morgan");
12
- var _ = require("underscore");
13
-
14
- /**
15
- * all relative files path in a directory
16
- */
17
-
18
- function walkDirSync(dir, files = []) {
19
- const list = fs.readdirSync(dir);
20
- list.forEach((file) => {
21
- const f = path.join(dir, file);
22
- if (fs.statSync(path.join(dir, file)).isDirectory()) {
23
- walkDirSync(f, files);
24
- } else {
25
- files.push(f);
26
- }
27
- });
28
- return files.map((f) => path.relative(dir, f));
29
- }
30
-
31
- /**
32
- * returns a middleware function that checks if path exists in dir.
33
- *
34
- * Files in the directory are prefixed by base_path and compared to
35
- * req.path
36
- */
37
-
38
- function skip_path(dir, base_path = "/") {
39
- let files = walkDirSync(dir);
40
- files = files.map((file) => path.join(base_path, file));
41
- console.error(
42
- `KayveeMiddleware: Skipping successful requests for files in ${dir} at ${base_path}`,
43
- );
44
- return (req, res) => _(files).contains(req.path) && res.statusCode < 400;
45
- }
46
-
47
- /**
48
- * request path
49
- */
50
-
51
- function getBaseUrl(req) {
52
- var url = req.originalUrl || req.url;
53
- var parsed = require("url").parse(url, true);
54
- return parsed.pathname;
55
- }
56
-
57
- /**
58
- * request query params
59
- */
60
-
61
- function getQueryParams(req) {
62
- var url = req.originalUrl || req.url;
63
- var parsed = require("url").parse(url, true);
64
- var parsedQueryString = require("qs").parse(parsed.search, {
65
- allowPrototypes: false,
66
- ignoreQueryPrefix: true,
67
- });
68
- return `?${require("qs").stringify(parsedQueryString)}`;
69
- }
70
-
71
- /**
72
- * response size
73
- */
74
-
75
- function getResponseSize(res) {
76
- var result = undefined;
77
- var headers = res.headers || res._headers;
78
- if (headers && headers["content-length"]) {
79
- result = Number(headers["content-length"]);
80
- } else if (res.data) {
81
- result = res.data.length;
82
- }
83
- return result;
84
- }
85
-
86
- /**
87
- * response time in nanoseconds
88
- */
89
-
90
- function getResponseTimeNs(req, res) {
91
- if (!req._startAt || !res._startAt) {
92
- // missing request and/or response start time
93
- return undefined;
94
- }
95
-
96
- // calculate diff
97
- var ns = (res._startAt[0] - req._startAt[0]) * 1e9 + (res._startAt[1] - req._startAt[1]);
98
- return ns;
99
- }
100
-
101
- /**
102
- * IP address that sent the request.
103
- *
104
- * `req.ip` is defined in Express: http://expressjs.com/en/api.html#req.ip
105
- */
106
- function getIp(req) {
107
- var remoteAddress = req.connection ? req.connection.remoteAddress : undefined;
108
- return req.ip || remoteAddress;
109
- }
110
-
111
- /**
112
- * Log level
113
- */
114
- function getLogLevel(req, res) {
115
- const statusCode = res.statusCode;
116
- let result;
117
- if (statusCode >= 499) {
118
- result = KayveeLogger.Error;
119
- } else {
120
- result = KayveeLogger.Info;
121
- }
122
- return result;
123
- }
124
-
125
- /*
126
- * Default handlers
127
- */
128
- var defaultHandlers = [
129
- // Request method
130
- (req) => ({ method: req.method }),
131
- // Path (URL without query params)
132
- (req) => ({ path: getBaseUrl(req) }),
133
- // Query params
134
- (req) => ({ params: getQueryParams(req) }),
135
- // Response size
136
- (req, res) => ({ "response-size": getResponseSize(res) }),
137
- // Response time (ns)
138
- (req, res) => ({ "response-time": getResponseTimeNs(req, res) }),
139
- // Status code
140
- (req, res) => ({ "status-code": res.statusCode }),
141
- // Ip address
142
- (req) => ({ ip: getIp(req) }),
143
- // Via -- what library/code produced this log?
144
- () => ({ via: "kayvee-middleware" }),
145
-
146
- // Kayvee's reserved fields
147
- // Log level
148
- (req, res) => ({ level: getLogLevel(req, res) }),
149
- // Source -- which app emitted this log?
150
- // -> Gets passed in among `options` during library initialization
151
- // Title
152
- () => ({ title: "request-finished" }),
153
- ];
154
-
155
- const defaultContextHandlers = [];
156
-
157
- function handlerData(handlers, ...args) {
158
- const data = {};
159
- handlers.forEach((h) => {
160
- try {
161
- const handler_data = h(...args);
162
- _.extend(data, handler_data);
163
- } catch (e) {
164
- // swallow invalid handler
165
- }
166
- });
167
- return data;
168
- }
169
-
170
- class ContextLogger {
171
- logger = null;
172
- handlers = [];
173
- args = [];
174
-
175
- constructor(logger, handlers, ...args) {
176
- this.logger = logger;
177
- this.handlers = handlers;
178
- this.args = args;
179
- }
180
-
181
- _contextualData(data) {
182
- return _.extend(handlerData(this.handlers, ...this.args), data);
183
- }
184
- }
185
-
186
- for (const func of KayveeLogger.LEVELS) {
187
- ContextLogger.prototype[func] = function (title) {
188
- this[`${func}D`](title, {});
189
- };
190
- ContextLogger.prototype[`${func}D`] = function (title, data) {
191
- this.logger[`${func}D`](title, this._contextualData(data));
192
- };
193
- }
194
-
195
- for (const func of KayveeLogger.METRICS) {
196
- ContextLogger.prototype[func] = function (title, value) {
197
- this[`${func}D`](title, value, {});
198
- };
199
- ContextLogger.prototype[`${func}D`] = function (title, value, data) {
200
- this.logger[`${func}D`](title, value, this._contextualData(data));
201
- };
202
- }
203
-
204
- /*
205
- * User configuration is passed via an `options` object.
206
- * Results from configuration are prioritized such that (`base_handlers` > `handlers` > `headers`).
207
- *
208
- * // `headers` - logs these request headers, if they exist
209
- *
210
- * headers: e.g. ['X-Request-Id', 'Host']
211
- *
212
- * // `handlers` - an array of functions of that return dicts to be logged.
213
- *
214
- * handlers: e.g. [function(request, response) { return {"key":"val"}]
215
- *
216
- * // `base_handlers` - an array of functions of that return dicts to be logged.
217
- * // Barring exceptional circumstances, `base_handlers` should not be overriden by the user.
218
- * // `base_handlers` defaults to a core set of handlers to run... see `defaultHandlers`.
219
- * //
220
- * // Separating `base_handlers` from `handlers` is done to ensure that reserved keys
221
- * // don't accidentally get overriden by custom handlers. This can now only happen if
222
- * // the user explicitly overrides `base_handlers`.
223
- *
224
- * base_handlers: e.g. [function(request, response) { return {"key":"val"}]
225
- *
226
- */
227
-
228
- var formatLine = (options_arg) => {
229
- var options = options_arg || {};
230
-
231
- // `source` is the one required field
232
- if (!options.source) {
233
- throw Error("Missing required config for 'source' in Kayvee middleware 'options'");
234
- }
235
-
236
- const router = KayveeLogger.getGlobalRouter();
237
-
238
- return (tokens, req, res) => {
239
- // Build a dict of data to log
240
- var data = { _kvmeta: undefined }; // Adding _kvmeta here to make typescript compile happy
241
-
242
- // Add user-configured request headers
243
- var custom_headers = options.headers || [];
244
- var header_data = {};
245
- custom_headers.forEach((h) => {
246
- // Header field names are case insensitive, so let's be consistent
247
- var lc = h.toLowerCase();
248
- header_data[lc] = req.headers[lc];
249
- });
250
- _.extend(data, header_data);
251
-
252
- // Run user-configured handlers; add custom data
253
- var custom_handlers = options.handlers || [];
254
-
255
- // Allow user to override `base_handlers`; provide sane default set of handlers
256
- var base_handlers = options.base_handlers || defaultHandlers;
257
- base_handlers = base_handlers.concat([() => ({ source: options.source })]);
258
-
259
- // Execute custom-handlers THEN base-handlers
260
- const all_handlers = custom_handlers.concat(base_handlers);
261
- _.extend(data, handlerData(all_handlers, req, res));
262
-
263
- if (router) {
264
- data._kvmeta = router.route(data);
265
- }
266
-
267
- return kayvee.format(data);
268
- };
269
- };
270
-
271
- const defaultContextLoggerOpts = {
272
- enabled: true,
273
- handlers: defaultContextHandlers,
274
- };
275
-
276
- /**
277
- * Module exports.
278
- * @public
279
- */
280
-
281
- if (process.env.NODE_ENV === "test") {
282
- module.exports = (clever_options, morgan_options = { skip: null }) => {
283
- if (clever_options.ignore_dir) {
284
- morgan_options.skip = skip_path(
285
- clever_options.ignore_dir.directory,
286
- clever_options.ignore_dir.path,
287
- );
288
- }
289
- return morgan(formatLine(clever_options), morgan_options);
290
- };
291
- module.exports.ContextLogger = ContextLogger;
292
- } else {
293
- module.exports = (clever_options, context_logger_options = defaultContextLoggerOpts) => {
294
- // `source` is the one required field
295
- if (!clever_options.source) {
296
- throw new Error("Missing required config for 'source' in Kayvee middleware 'options'");
297
- }
298
- const logger = new KayveeLogger(clever_options.source);
299
- const morgan_options = {
300
- stream: process.stderr,
301
- skip: null,
302
- };
303
- if (clever_options.ignore_dir) {
304
- morgan_options.skip = skip_path(
305
- clever_options.ignore_dir.directory,
306
- clever_options.ignore_dir.path,
307
- );
308
- }
309
- const morgan_logger = morgan(formatLine(clever_options), morgan_options);
310
- return (req, res, next) => {
311
- if (context_logger_options.enabled) {
312
- req.log = new ContextLogger(logger, context_logger_options.handlers, req);
313
- }
314
- morgan_logger(req, res, next);
315
- };
316
- };
317
- }