import readline from "readline"; import fs from "fs"; import yargs from "yargs"; import packageJson from "./package.json"; import * as Rx from "rxjs"; import { hideBin } from "yargs/helpers"; import { map, pluck, takeUntil, filter, window, mergeAll, buffer } from "rxjs/operators"; const args = yargs(hideBin(process.argv)) .scriptName(Object.keys(packageJson.bin)[0]) .usage(`Usage: $0 logfile`) .option("files", { boolean: true, describe: "Extract and save the embedded files from the log file" }) .argv; const argsParam0 = args._[0]?.toString(); type LogLine = { log: string; stream: string; time: string; }; const rl = readline.createInterface({ input: fs.createReadStream(argsParam0) }); const readlineObservable = Rx.fromEvent(rl, "line") .pipe( takeUntil(Rx.fromEvent(rl, "close")) ); const fileWindowStartObservable = readlineObservable .pipe( filter(rawLine => rawLine.includes("Dumping image")) ); const fileWindowStopObservable = readlineObservable .pipe( filter(rawLine => rawLine.includes("\\u003c\\u003c\\u003c\\u003c")) ); const logObservable = readlineObservable .pipe( window(Rx.merge(fileWindowStartObservable, fileWindowStopObservable)), filter((_, i) => i % 2 === (args.files ? 1 : 0)), //keep every second observable for the files stream mergeAll(), filter(rawLine => !rawLine.includes("\\u003c\\u003c\\u003c\\u003c")), map(line => JSON.parse(line)), pluck("log"), map(log => log.replace(/\n$/, "")) ); if (args.files) { logObservable .pipe( buffer(fileWindowStopObservable), map(fileLines => ({ name: fileLines.shift()?.substring(10), content: fileLines.join() })) ) .subscribe({ next: file => { const filename = `${argsParam0}.${file.name}`; fs.writeFile(filename, Buffer.from(file.content, "base64"), () => { console.log(`File ${filename} is saved.`) }); }, error: console.error, complete: () => rl.close() }); } else { logObservable.subscribe({ next: console.log, error: console.error, complete: () => rl.close() }); }