Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/script/cleanLog.ts
13383 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as fs from 'fs';
7
import * as path from 'path';
8
9
function showHelp(): void {
10
console.log(`
11
cleanLog.ts - A utility script to filter log files by topic.
12
13
This script reads a log file, filters entries to only include those matching
14
a specified topic, strips timestamps from the output, and writes the filtered
15
content back to the file.
16
17
Usage:
18
npx ts-node script/cleanLog.ts --log=<topic> <log-file-path>
19
20
Options:
21
--log=<topic> The topic to filter by (case-insensitive). Required.
22
--help Show this help message.
23
24
Example:
25
npx ts-node script/cleanLog.ts --log=InlineChat /path/to/extension.log
26
27
This will filter the log file to only include entries containing [InlineChat]
28
and overwrite the original file with the filtered content.
29
`);
30
}
31
32
function parseArgs(args: string[]): { logTopic: string; filePath: string } | 'help' {
33
if (args.includes('--help') || args.includes('-h')) {
34
return 'help';
35
}
36
let logTopic: string | undefined;
37
let filePath: string | undefined;
38
39
for (const arg of args) {
40
if (arg.startsWith('--log=')) {
41
logTopic = arg.slice('--log='.length);
42
} else if (!arg.startsWith('-')) {
43
filePath = arg;
44
}
45
}
46
47
if (!logTopic) {
48
throw new Error('Missing required argument: --log=<topic>');
49
}
50
if (!filePath) {
51
throw new Error('Missing required positional argument: <log-file-path>');
52
}
53
54
return { logTopic, filePath };
55
}
56
57
// Matches the start of a log line: timestamp [level] [TOPIC]...
58
const LOG_LINE_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} \[/;
59
// Matches the timestamp prefix to strip it
60
const TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} /;
61
62
function stripTimestamp(line: string): string {
63
return line.replace(TIMESTAMP_PATTERN, '');
64
}
65
66
function escapeRegExp(value: string): string {
67
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
}
69
70
function filterLogByTopic(content: string, topic: string): string {
71
const lines = content.split('\n');
72
const result: string[] = [];
73
const topicPattern = new RegExp(`\\[${escapeRegExp(topic)}\\]`, 'i');
74
75
let currentLogEntry: string[] = [];
76
let keepCurrentEntry = false;
77
78
function flushEntry() {
79
if (keepCurrentEntry && currentLogEntry.length > 0) {
80
// Strip timestamp from the first line of the entry
81
currentLogEntry[0] = stripTimestamp(currentLogEntry[0]);
82
result.push(...currentLogEntry);
83
}
84
currentLogEntry = [];
85
keepCurrentEntry = false;
86
}
87
88
for (const line of lines) {
89
if (LOG_LINE_PATTERN.test(line)) {
90
// New log entry starts - flush the previous one
91
flushEntry();
92
currentLogEntry.push(line);
93
keepCurrentEntry = topicPattern.test(line);
94
} else {
95
// Continuation line (like "- `ERROR: ...`")
96
currentLogEntry.push(line);
97
}
98
}
99
100
// Flush the last entry
101
flushEntry();
102
103
return result.join('\n');
104
}
105
106
function main() {
107
const args = process.argv.slice(2);
108
const parsed = parseArgs(args);
109
110
if (parsed === 'help') {
111
showHelp();
112
return;
113
}
114
115
const { logTopic, filePath } = parsed;
116
117
const absolutePath = path.isAbsolute(filePath)
118
? filePath
119
: path.join(process.cwd(), filePath);
120
121
try {
122
const content = fs.readFileSync(absolutePath, 'utf-8');
123
const filtered = filterLogByTopic(content, logTopic);
124
125
fs.writeFileSync(absolutePath, filtered, 'utf-8');
126
console.log(`Filtered log file to only include [${logTopic}] entries: ${absolutePath}`);
127
} catch (error) {
128
const err = error as NodeJS.ErrnoException;
129
if (err.code === 'ENOENT') {
130
console.error(`Failed to read log file "${absolutePath}": file does not exist.`);
131
} else if (err.code === 'EACCES' || err.code === 'EPERM') {
132
console.error(`Permission denied while accessing log file "${absolutePath}".`);
133
} else {
134
console.error(`Failed to process log file "${absolutePath}": ${err.message ?? err}`);
135
}
136
process.exitCode = 1;
137
}
138
}
139
140
main();
141
142