Path: blob/main/extensions/copilot/script/cleanLog.ts
13383 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as fs from 'fs';6import * as path from 'path';78function showHelp(): void {9console.log(`10cleanLog.ts - A utility script to filter log files by topic.1112This script reads a log file, filters entries to only include those matching13a specified topic, strips timestamps from the output, and writes the filtered14content back to the file.1516Usage:17npx ts-node script/cleanLog.ts --log=<topic> <log-file-path>1819Options:20--log=<topic> The topic to filter by (case-insensitive). Required.21--help Show this help message.2223Example:24npx ts-node script/cleanLog.ts --log=InlineChat /path/to/extension.log2526This will filter the log file to only include entries containing [InlineChat]27and overwrite the original file with the filtered content.28`);29}3031function parseArgs(args: string[]): { logTopic: string; filePath: string } | 'help' {32if (args.includes('--help') || args.includes('-h')) {33return 'help';34}35let logTopic: string | undefined;36let filePath: string | undefined;3738for (const arg of args) {39if (arg.startsWith('--log=')) {40logTopic = arg.slice('--log='.length);41} else if (!arg.startsWith('-')) {42filePath = arg;43}44}4546if (!logTopic) {47throw new Error('Missing required argument: --log=<topic>');48}49if (!filePath) {50throw new Error('Missing required positional argument: <log-file-path>');51}5253return { logTopic, filePath };54}5556// Matches the start of a log line: timestamp [level] [TOPIC]...57const LOG_LINE_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} \[/;58// Matches the timestamp prefix to strip it59const TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} /;6061function stripTimestamp(line: string): string {62return line.replace(TIMESTAMP_PATTERN, '');63}6465function escapeRegExp(value: string): string {66return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');67}6869function filterLogByTopic(content: string, topic: string): string {70const lines = content.split('\n');71const result: string[] = [];72const topicPattern = new RegExp(`\\[${escapeRegExp(topic)}\\]`, 'i');7374let currentLogEntry: string[] = [];75let keepCurrentEntry = false;7677function flushEntry() {78if (keepCurrentEntry && currentLogEntry.length > 0) {79// Strip timestamp from the first line of the entry80currentLogEntry[0] = stripTimestamp(currentLogEntry[0]);81result.push(...currentLogEntry);82}83currentLogEntry = [];84keepCurrentEntry = false;85}8687for (const line of lines) {88if (LOG_LINE_PATTERN.test(line)) {89// New log entry starts - flush the previous one90flushEntry();91currentLogEntry.push(line);92keepCurrentEntry = topicPattern.test(line);93} else {94// Continuation line (like "- `ERROR: ...`")95currentLogEntry.push(line);96}97}9899// Flush the last entry100flushEntry();101102return result.join('\n');103}104105function main() {106const args = process.argv.slice(2);107const parsed = parseArgs(args);108109if (parsed === 'help') {110showHelp();111return;112}113114const { logTopic, filePath } = parsed;115116const absolutePath = path.isAbsolute(filePath)117? filePath118: path.join(process.cwd(), filePath);119120try {121const content = fs.readFileSync(absolutePath, 'utf-8');122const filtered = filterLogByTopic(content, logTopic);123124fs.writeFileSync(absolutePath, filtered, 'utf-8');125console.log(`Filtered log file to only include [${logTopic}] entries: ${absolutePath}`);126} catch (error) {127const err = error as NodeJS.ErrnoException;128if (err.code === 'ENOENT') {129console.error(`Failed to read log file "${absolutePath}": file does not exist.`);130} else if (err.code === 'EACCES' || err.code === 'EPERM') {131console.error(`Permission denied while accessing log file "${absolutePath}".`);132} else {133console.error(`Failed to process log file "${absolutePath}": ${err.message ?? err}`);134}135process.exitCode = 1;136}137}138139main();140141142