logtunnel 0.4.0 → 1.1.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/README.md +259 -0
- package/package.json +2 -2
- package/src/definition.js +33 -30
- package/src/log-source.js +1 -1
- package/src/main.js +3 -1
- package/src/pipeline.js +55 -21
- package/src/transformers/filters/field.js +18 -0
- package/src/transformers/filters/find.js +11 -0
- package/src/transformers/filters/ignore.js +11 -0
- package/src/transformers/outputs/bigodon.js +17 -0
- package/src/transformers/outputs/factory.js +21 -0
- package/src/transformers/outputs/inspect.js +22 -0
- package/src/transformers/outputs/json.js +11 -0
- package/src/transformers/outputs/logfmt.js +13 -0
- package/src/transformers/outputs/original.js +7 -0
- package/src/transformers/outputs/table.js +67 -0
- package/src/transformers/parsers/factory.js +16 -0
- package/src/transformers/parsers/json.js +16 -0
- package/src/transformers/parsers/logfmt.js +13 -0
- package/src/transformers/parsers/regex.js +11 -0
- package/src/transformers/parsers/table.js +31 -0
- package/test/filters.spec.js +40 -40
- package/test/output.spec.js +61 -22
- package/test/parse.spec.js +20 -22
- package/test/pipeline.spec.js +107 -21
- package/test/utils.js +8 -2
- package/src/transformers/field.js +0 -11
- package/src/transformers/filter.js +0 -4
- package/src/transformers/ignore.js +0 -4
- package/src/transformers/output-json.js +0 -7
- package/src/transformers/output-logfmt.js +0 -9
- package/src/transformers/output-mustache.js +0 -13
- package/src/transformers/output-original.js +0 -3
- package/src/transformers/output-unset.js +0 -12
- package/src/transformers/output.js +0 -15
- package/src/transformers/parse-json.js +0 -12
- package/src/transformers/parse-logfmt.js +0 -9
- package/src/transformers/parse-regex.js +0 -4
- package/src/transformers/parse-table.js +0 -26
- package/src/transformers/parse.js +0 -14
package/test/pipeline.spec.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
const Lab = require('@hapi/lab');
|
|
2
2
|
const Code = require('@hapi/code');
|
|
3
|
-
const { runPipeline, _, f, i, F, p, o, H } = require('./utils');
|
|
3
|
+
const { runPipeline, _, f, i, F, p, o, H, k } = require('./utils');
|
|
4
4
|
|
|
5
5
|
const { describe, it } = exports.lab = Lab.script();
|
|
6
6
|
const { expect } = Code;
|
|
7
7
|
|
|
8
8
|
describe('pipeline', () => {
|
|
9
|
-
it('should remove rejected lines and keep accepted ones', () => {
|
|
9
|
+
it('should remove rejected lines and keep accepted ones', async () => {
|
|
10
10
|
const args = [
|
|
11
11
|
f('potato')
|
|
12
12
|
];
|
|
13
|
-
const actual = runPipeline([
|
|
13
|
+
const actual = await runPipeline([
|
|
14
14
|
'yada yada yada',
|
|
15
15
|
'lorem ipsum POTATO dolor',
|
|
16
16
|
'ablueblue POTATO',
|
|
@@ -24,13 +24,13 @@ describe('pipeline', () => {
|
|
|
24
24
|
expect(actual).to.equal(expected);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it('should apply multiple filters', () => {
|
|
27
|
+
it('should apply multiple filters', async () => {
|
|
28
28
|
const args = [
|
|
29
29
|
f('foo'),
|
|
30
30
|
f('bar'),
|
|
31
31
|
i('nope'),
|
|
32
32
|
];
|
|
33
|
-
const actual = runPipeline([
|
|
33
|
+
const actual = await runPipeline([
|
|
34
34
|
'1. foo',
|
|
35
35
|
'2. foo bar nope',
|
|
36
36
|
'3. foo bar',
|
|
@@ -43,12 +43,12 @@ describe('pipeline', () => {
|
|
|
43
43
|
expect(actual).to.equal(expected);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
it('should apply default argument as filter', () => {
|
|
46
|
+
it('should apply default argument as filter', async () => {
|
|
47
47
|
const args = [
|
|
48
48
|
_('foo'),
|
|
49
49
|
i('bar'),
|
|
50
50
|
];
|
|
51
|
-
const actual = runPipeline([
|
|
51
|
+
const actual = await runPipeline([
|
|
52
52
|
'1. foo',
|
|
53
53
|
'2. foo bar',
|
|
54
54
|
'3. baz',
|
|
@@ -60,12 +60,12 @@ describe('pipeline', () => {
|
|
|
60
60
|
expect(actual).to.equal(expected);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it('should parse and format correctly', () => {
|
|
63
|
+
it('should parse and format correctly', async () => {
|
|
64
64
|
const args = [
|
|
65
65
|
p('logfmt'),
|
|
66
66
|
o('json'),
|
|
67
67
|
];
|
|
68
|
-
const actual = runPipeline([
|
|
68
|
+
const actual = await runPipeline([
|
|
69
69
|
'foo=bar baz=qux',
|
|
70
70
|
], args);
|
|
71
71
|
const expected = [
|
|
@@ -75,13 +75,31 @@ describe('pipeline', () => {
|
|
|
75
75
|
expect(actual).to.equal(expected);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it('should
|
|
78
|
+
it('should buffer and format tables when requested', async () => {
|
|
79
|
+
const args = [
|
|
80
|
+
p('logfmt'),
|
|
81
|
+
o('table'),
|
|
82
|
+
];
|
|
83
|
+
const actual = await runPipeline([
|
|
84
|
+
'foo=bar baz=qux',
|
|
85
|
+
'foo=longer',
|
|
86
|
+
], args);
|
|
87
|
+
const expected = [
|
|
88
|
+
'foo baz',
|
|
89
|
+
'bar qux',
|
|
90
|
+
'longer',
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
expect(actual).to.equal(expected);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should filter by the original line even when parsers and outputs are provided', async () => {
|
|
79
97
|
const args = [
|
|
80
98
|
p('logfmt'),
|
|
81
99
|
o('json'),
|
|
82
100
|
i('o=n'),
|
|
83
101
|
];
|
|
84
|
-
const actual = runPipeline([
|
|
102
|
+
const actual = await runPipeline([
|
|
85
103
|
'foo=bar baz=qux',
|
|
86
104
|
'foo=nope baz=qux',
|
|
87
105
|
], args);
|
|
@@ -92,13 +110,13 @@ describe('pipeline', () => {
|
|
|
92
110
|
expect(actual).to.equal(expected);
|
|
93
111
|
});
|
|
94
112
|
|
|
95
|
-
it('should apply field filters after parse', () => {
|
|
113
|
+
it('should apply field filters after parse', async () => {
|
|
96
114
|
const args = [
|
|
97
115
|
p('logfmt'),
|
|
98
116
|
o('json'),
|
|
99
|
-
F('foo
|
|
117
|
+
F('startsWith foo "b"'),
|
|
100
118
|
];
|
|
101
|
-
const actual = runPipeline([
|
|
119
|
+
const actual = await runPipeline([
|
|
102
120
|
'foo=bar baz=qux',
|
|
103
121
|
'foo=nope baz=qux',
|
|
104
122
|
], args);
|
|
@@ -109,14 +127,14 @@ describe('pipeline', () => {
|
|
|
109
127
|
expect(actual).to.equal(expected);
|
|
110
128
|
});
|
|
111
129
|
|
|
112
|
-
it('should apply multiple field filters', () => {
|
|
130
|
+
it('should apply multiple field filters', async () => {
|
|
113
131
|
const args = [
|
|
114
132
|
p('logfmt'),
|
|
115
133
|
o('json'),
|
|
116
|
-
F('foo
|
|
117
|
-
F('foo
|
|
134
|
+
F('startsWith foo "b"'),
|
|
135
|
+
F('endsWith foo "r"'),
|
|
118
136
|
];
|
|
119
|
-
const actual = runPipeline([
|
|
137
|
+
const actual = await runPipeline([
|
|
120
138
|
'foo=baz baz=qux',
|
|
121
139
|
'foo=bar baz=qux',
|
|
122
140
|
'foo=tar baz=qux',
|
|
@@ -128,14 +146,34 @@ describe('pipeline', () => {
|
|
|
128
146
|
expect(actual).to.equal(expected);
|
|
129
147
|
});
|
|
130
148
|
|
|
131
|
-
it('should log headers when -H is provided', () => {
|
|
149
|
+
it('should log headers when -H is provided', async () => {
|
|
132
150
|
const args = [
|
|
133
151
|
p('table'),
|
|
134
152
|
o('original'),
|
|
135
|
-
F('NAME
|
|
153
|
+
F('eq NAME "foo"'),
|
|
154
|
+
H(),
|
|
155
|
+
];
|
|
156
|
+
const actual = await runPipeline([
|
|
157
|
+
'NAME',
|
|
158
|
+
'foo',
|
|
159
|
+
'bar',
|
|
160
|
+
], args);
|
|
161
|
+
const expected = [
|
|
162
|
+
'NAME',
|
|
163
|
+
'foo',
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
expect(actual).to.equal(expected);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should log headers only once when both -H and -o table is provided', async () => {
|
|
170
|
+
const args = [
|
|
171
|
+
p('table'),
|
|
172
|
+
o('table'),
|
|
173
|
+
F('eq NAME "foo"'),
|
|
136
174
|
H(),
|
|
137
175
|
];
|
|
138
|
-
const actual = runPipeline([
|
|
176
|
+
const actual = await runPipeline([
|
|
139
177
|
'NAME',
|
|
140
178
|
'foo',
|
|
141
179
|
'bar',
|
|
@@ -147,4 +185,52 @@ describe('pipeline', () => {
|
|
|
147
185
|
|
|
148
186
|
expect(actual).to.equal(expected);
|
|
149
187
|
});
|
|
188
|
+
|
|
189
|
+
it('should not duplicate headers when parsing and reformatting tables with ignore filters', async () => {
|
|
190
|
+
const args = [
|
|
191
|
+
p('table'),
|
|
192
|
+
o('table'),
|
|
193
|
+
i('kube'),
|
|
194
|
+
];
|
|
195
|
+
const actual = await runPipeline([
|
|
196
|
+
'NAMESPACE NAME',
|
|
197
|
+
'kube-system coredns',
|
|
198
|
+
'default api',
|
|
199
|
+
], args);
|
|
200
|
+
const expected = [
|
|
201
|
+
'NAMESPACE NAME',
|
|
202
|
+
'default api',
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
expect(actual).to.equal(expected);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should not specify null prototype of regex parser', async () => {
|
|
209
|
+
const args = [
|
|
210
|
+
p('(?<num>\\d*)'),
|
|
211
|
+
];
|
|
212
|
+
const [actual] = await runPipeline([
|
|
213
|
+
'12',
|
|
214
|
+
], args);
|
|
215
|
+
|
|
216
|
+
expect(actual).not.to.include('null prototype');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should parse and format kubectl tables with -k', async () => {
|
|
220
|
+
const args = [
|
|
221
|
+
k(),
|
|
222
|
+
];
|
|
223
|
+
const actual = await runPipeline([
|
|
224
|
+
'NAME AGE',
|
|
225
|
+
'foo 1',
|
|
226
|
+
'bar 22',
|
|
227
|
+
], args);
|
|
228
|
+
const expected = [
|
|
229
|
+
'NAME AGE',
|
|
230
|
+
'foo 1',
|
|
231
|
+
'bar 22',
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
expect(actual).to.equal(expected);
|
|
235
|
+
});
|
|
150
236
|
});
|
package/test/utils.js
CHANGED
|
@@ -31,7 +31,7 @@ function slowPod(...strs) {
|
|
|
31
31
|
return emitter;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function runPipeline(inputLines, args) {
|
|
34
|
+
async function runPipeline(inputLines, args) {
|
|
35
35
|
const lines = [];
|
|
36
36
|
|
|
37
37
|
const stdout = { write: (line) => lines.push(line) };
|
|
@@ -39,6 +39,11 @@ function runPipeline(inputLines, args) {
|
|
|
39
39
|
|
|
40
40
|
inputLines.forEach(line => pipeline.onLogLine(line));
|
|
41
41
|
|
|
42
|
+
// Waiting for nextTick promises to resolve
|
|
43
|
+
await Hoek.wait(0);
|
|
44
|
+
|
|
45
|
+
pipeline.onEnd();
|
|
46
|
+
|
|
42
47
|
// Removing \n from the end of each line
|
|
43
48
|
return lines.map(line => line.slice(0, -1));
|
|
44
49
|
}
|
|
@@ -62,5 +67,6 @@ const F = str => ({ field: [str] });
|
|
|
62
67
|
const p = str => ({ parser: str });
|
|
63
68
|
const o = str => ({ output: str });
|
|
64
69
|
const H = () => ({ headers: true });
|
|
70
|
+
const k = () => ({ kubectl: true });
|
|
65
71
|
|
|
66
|
-
module.exports = { pod, slowPod, runPipeline, _, f, i, F, p, o, H };
|
|
72
|
+
module.exports = { pod, slowPod, runPipeline, _, f, i, F, p, o, H, k };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const vm = require('vm');
|
|
2
|
-
|
|
3
|
-
module.exports = filter => line => {
|
|
4
|
-
if(typeof line !== 'object') {
|
|
5
|
-
throw new Error("To use a field filter, you need to specify a parser like '-p json' ");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
vm.createContext(line);
|
|
9
|
-
const result = vm.runInContext(filter, line);
|
|
10
|
-
return Boolean(result);
|
|
11
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const { compile } = require('jstache');
|
|
2
|
-
|
|
3
|
-
module.exports = format => {
|
|
4
|
-
const template = compile(format);
|
|
5
|
-
|
|
6
|
-
return line => {
|
|
7
|
-
if(typeof line !== 'object') {
|
|
8
|
-
throw new Error("To use an output transformer, you need to specify a parser like '-p json' ");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return template(line);
|
|
12
|
-
};
|
|
13
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const json = require('./output-json');
|
|
2
|
-
const logfmt = require('./output-logfmt');
|
|
3
|
-
const mustache = require('./output-mustache');
|
|
4
|
-
const original = require('./output-original');
|
|
5
|
-
const unset = require('./output-unset');
|
|
6
|
-
|
|
7
|
-
module.exports = format => {
|
|
8
|
-
switch(format?.toLowerCase()) {
|
|
9
|
-
case void 0: return unset();
|
|
10
|
-
case 'json': return json();
|
|
11
|
-
case 'logfmt': return logfmt();
|
|
12
|
-
case 'original': return original();
|
|
13
|
-
default: return mustache(format);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
function splitColumns(line) {
|
|
2
|
-
return line.replace(/\s+/g, ' ').split(' ');
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
module.exports = () => {
|
|
6
|
-
let headers;
|
|
7
|
-
|
|
8
|
-
return (line, _original, pipeline) => {
|
|
9
|
-
if(!pipeline.firstLine) {
|
|
10
|
-
// Ignore first line, it's the headers
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
if(!headers) {
|
|
14
|
-
headers = splitColumns(pipeline.firstLine);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const columns = splitColumns(line);
|
|
18
|
-
const obj = {};
|
|
19
|
-
|
|
20
|
-
headers.forEach((header, i) => {
|
|
21
|
-
obj[header] = columns[i];
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
return obj;
|
|
25
|
-
};
|
|
26
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
const json = require('./parse-json');
|
|
2
|
-
const logfmt = require('./parse-logfmt');
|
|
3
|
-
const regex = require('./parse-regex');
|
|
4
|
-
const table = require('./parse-table');
|
|
5
|
-
|
|
6
|
-
module.exports = format => {
|
|
7
|
-
switch(format?.toLowerCase()) {
|
|
8
|
-
case void 0: return null;
|
|
9
|
-
case 'json': return json();
|
|
10
|
-
case 'logfmt': return logfmt();
|
|
11
|
-
case 'table': return table();
|
|
12
|
-
default: return regex(format);
|
|
13
|
-
}
|
|
14
|
-
};
|