Skip to content

Commit fac59c7

Browse files
committed
fix: TS with node types
1 parent f01b0b7 commit fac59c7

File tree

2 files changed

+156
-1
lines changed

2 files changed

+156
-1
lines changed

.evergreen/run-typescript.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ function get_ts_version() {
3131
export TSC="./node_modules/typescript/bin/tsc"
3232
export TS_VERSION=$(get_ts_version)
3333

34-
npm install --no-save --force typescript@"$TS_VERSION"
34+
# On old versions of TS we need to put the node types back to 18.11.19
35+
npm install --no-save --force typescript@"$TS_VERSION" "$(if [[ $TS_VERSION == '4.1.6' ]]; then echo "@types/node@18.11.19"; else echo ""; fi)"
3536

3637
echo "Typescript $($TSC -v)"
3738

etc/crawfish.mjs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#! /usr/bin/env node --experimental-modules
2+
3+
import { createReadStream, existsSync, promises } from 'fs';
4+
const { readFile } = promises;
5+
import { createInterface } from 'readline';
6+
import xml2js from 'xml2js';
7+
const { parseStringPromise } = xml2js;
8+
import yargs from 'yargs';
9+
import chalk from 'chalk';
10+
11+
let warnings = false;
12+
13+
/**
14+
* @param args - program arguments
15+
*/
16+
async function main(args) {
17+
args = yargs(args)
18+
.option('l', {
19+
alias: 'log',
20+
demandOption: true,
21+
default: './data/mongod.log', // cluster_setup.sh default
22+
describe: 'The log you wish to filter',
23+
type: 'string'
24+
})
25+
.option('f', {
26+
alias: 'filter',
27+
demandOption: true,
28+
default: '', // No filter is still useful if you want to look at all tests
29+
describe: 'The test name filter, if none provided all test logs will be shown',
30+
type: 'string'
31+
})
32+
.option('v', {
33+
alias: 'verbose',
34+
demandOption: false,
35+
describe: 'Enable warnings about processing',
36+
type: 'boolean'
37+
})
38+
.help('h')
39+
.alias('h', 'help').epilog(`
40+
- Some log processing is done:
41+
- better date time format
42+
- string interpolation
43+
- 'testName' property added
44+
- Depends on an xunit file, should be left over from every test run
45+
46+
Examples:
47+
${chalk.green('crawfish.mjs | jq -SC | less -R')}
48+
- jq -SC will sort the keys and force color output
49+
- less lets you page through and search logs
50+
51+
${chalk.green('crawfish.mjs | jq -Sc | code -')}
52+
- jq -Sc will sort the keys and keep the logs one line (compact)
53+
- Opens the output in vscode, good for searching!
54+
`).argv;
55+
56+
warnings = !!args.verbose;
57+
const logFile = args.log;
58+
const testNameRegex = args.filter;
59+
60+
if (!existsSync('xunit.xml')) {
61+
console.error('xunit.xml file not found, required for db log test filtering.');
62+
process.exit(1);
63+
}
64+
65+
const content = await readFile('xunit.xml', { encoding: 'utf8' });
66+
const xunit = await parseStringPromise(content);
67+
68+
const tests = collectTests(xunit, testNameRegex);
69+
if (warnings) console.error(`filtering log file ${logFile}`);
70+
71+
const logStream =
72+
logFile === '-' ? process.stdin : createReadStream(logFile, { encoding: 'utf8' });
73+
const lineStream = createInterface({
74+
input: logStream,
75+
crlfDelay: Infinity
76+
});
77+
78+
const testToLogs = new Map(tests.map(({ name }) => [name, []]));
79+
for await (const line of lineStream) {
80+
const structuredLog = JSON.parse(line);
81+
for (const test of tests) {
82+
const logTime = Date.parse(structuredLog.t.$date);
83+
if (logTime <= test.end && logTime >= test.start) {
84+
testToLogs.get(test.name).push(structuredLog);
85+
}
86+
}
87+
}
88+
89+
for (const [name, logs] of testToLogs.entries()) {
90+
for (const log of logs) {
91+
log.testName = name;
92+
interpolateMsg(log);
93+
friendlyDate(log);
94+
console.log(JSON.stringify(log));
95+
}
96+
}
97+
}
98+
99+
function interpolateMsg(log) {
100+
if (!log.msg) return;
101+
102+
if (!log.attr) return;
103+
104+
for (const key in log.attr) {
105+
if (Reflect.has(log.attr, key)) {
106+
log.msg = log.msg.split(`{${key}}`).join(`${JSON.stringify(log.attr[key])}`);
107+
delete log.attr[key];
108+
}
109+
}
110+
111+
if (Object.keys(log.attr).length === 0) delete log.attr;
112+
log.msg = log.msg.split(`"`).join(`'`);
113+
}
114+
115+
function friendlyDate(log) {
116+
const dateString = typeof log.t === 'string' ? log.t : log.t.$date;
117+
try {
118+
log.t = new Date(Date.parse(dateString)).toISOString();
119+
} catch (e) {
120+
if (warnings) console.error(`Cannot translate date time of ${JSON.stringify(log)}`);
121+
}
122+
}
123+
124+
function collectTests(xuint, testFilter) {
125+
const suites = xuint.testsuites.testsuite;
126+
127+
const tests = [];
128+
129+
for (const suite of suites) {
130+
if (suite.testcase) {
131+
for (const test of suite.testcase) {
132+
const fullName = `${suite.$.name} ${test.$.name}`;
133+
if (fullName.toLowerCase().includes(testFilter.toLowerCase())) {
134+
if (test.$.start === '0') {
135+
if (warnings) console.error(`Warning: ${fullName} was skipped, theres no logs`);
136+
continue;
137+
}
138+
tests.push({
139+
name: fullName,
140+
start: Date.parse(test.$.start),
141+
end: Date.parse(test.$.end)
142+
});
143+
}
144+
}
145+
}
146+
}
147+
148+
return tests;
149+
}
150+
151+
main(process.argv).catch(e => {
152+
console.error(e);
153+
process.exit(1);
154+
});

0 commit comments

Comments
 (0)