Skip to content

Commit 6322a02

Browse files
committed
Refactor RollingLog
1 parent 6b2bfea commit 6322a02

File tree

2 files changed

+107
-26
lines changed

2 files changed

+107
-26
lines changed

src/ui/SwiftOutputChannel.ts

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@ import configuration from "../configuration";
1717

1818
export class SwiftOutputChannel implements vscode.OutputChannel {
1919
private channel: vscode.OutputChannel;
20-
private logStore = new RollingLog(1024 * 1024 * 5);
21-
private logToConsole: boolean;
22-
23-
public name: string;
20+
private logStore: RollingLog;
2421

2522
/**
2623
* Creates a vscode.OutputChannel that allows for later retrival of logs.
2724
* @param name
2825
*/
29-
constructor(name: string, logToConsole: boolean = true) {
26+
constructor(
27+
public name: string,
28+
private logToConsole: boolean = true,
29+
logStoreLinesSize: number = 250_000 // default to capturing 250k log lines
30+
) {
3031
this.name = name;
3132
this.logToConsole = process.env["CI"] !== "1" && logToConsole;
3233
this.channel = vscode.window.createOutputChannel(name);
34+
this.logStore = new RollingLog(logStoreLinesSize);
3335
}
3436

3537
append(value: string): void {
@@ -109,40 +111,52 @@ export class SwiftOutputChannel implements vscode.OutputChannel {
109111
}
110112

111113
class RollingLog {
112-
private _logs: string[] = [];
113-
private currentLogLength: number = 0;
114+
private _logs: (string | null)[];
115+
private startIndex: number = 0;
116+
private endIndex: number = 0;
117+
private logCount: number = 0;
114118

115-
constructor(private maxSizeCharacters: number) {}
119+
constructor(private maxLogs: number) {
120+
this._logs = new Array(maxLogs).fill(null);
121+
}
116122

117123
public get logs(): string[] {
118-
return [...this._logs];
124+
const logs: string[] = [];
125+
for (let i = 0; i < this.logCount; i++) {
126+
logs.push(this._logs[(this.startIndex + i) % this.maxLogs]!);
127+
}
128+
return logs;
129+
}
130+
131+
private incrementIndex(index: number): number {
132+
return (index + 1) % this.maxLogs;
119133
}
120134

121135
appendLine(log: string) {
122-
// It can be costly to calculate the actual memory size of a string in Node so just
123-
// use the total number of characters in the logs as a huristic for total size.
124-
const logSize = log.length;
125-
126-
while (this.currentLogLength + logSize > this.maxSizeCharacters && this.logs.length > 0) {
127-
const oldestLog = this.logs.shift();
128-
if (oldestLog) {
129-
this.currentLogLength -= oldestLog.length;
130-
}
136+
if (this.logCount === this.maxLogs) {
137+
this.startIndex = this.incrementIndex(this.startIndex);
138+
} else {
139+
this.logCount++;
131140
}
132141

133-
this._logs.push(log);
134-
135-
this.currentLogLength += logSize;
142+
this._logs[this.endIndex] = log;
143+
this.endIndex = this.incrementIndex(this.endIndex);
136144
}
137145

138146
append(log: string) {
139-
const line = this._logs.pop();
140-
this.currentLogLength -= line?.length ?? 0;
141-
this.appendLine((line ?? "") + log);
147+
const endIndex = this.endIndex - 1 < 0 ? Math.max(this.logCount - 1, 0) : this.endIndex;
148+
if (this._logs[endIndex] === null) {
149+
this.logCount = 1;
150+
}
151+
const newLogLine = (this._logs[endIndex] ?? "") + log;
152+
this._logs[endIndex] = newLogLine;
142153
}
143154

144155
replace(log: string) {
145-
this._logs = [log];
146-
this.currentLogLength = log.length;
156+
this._logs = new Array(this.maxLogs).fill(null);
157+
this._logs[0] = log;
158+
this.startIndex = 0;
159+
this.endIndex = 1;
160+
this.logCount = 1;
147161
}
148162
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as assert from "assert";
16+
import { SwiftOutputChannel } from "../../../src/ui/SwiftOutputChannel";
17+
18+
suite("SwiftOutputChannel", function () {
19+
let channel: SwiftOutputChannel;
20+
setup(() => {
21+
channel = new SwiftOutputChannel("SwiftOutputChannel Tests", false, 3);
22+
});
23+
24+
teardown(() => {
25+
channel.dispose();
26+
});
27+
28+
test("Appends logs", () => {
29+
channel.append("a");
30+
channel.append("b");
31+
channel.append("c");
32+
assert.deepEqual(channel.logs, ["abc"]);
33+
});
34+
35+
test("Appends lines", () => {
36+
channel.appendLine("a");
37+
channel.appendLine("b");
38+
channel.appendLine("c");
39+
assert.deepEqual(channel.logs, ["a", "b", "c"]);
40+
});
41+
42+
test("Appends lines and rolls over", () => {
43+
channel.appendLine("a");
44+
channel.appendLine("b");
45+
channel.appendLine("c");
46+
channel.appendLine("d");
47+
assert.deepEqual(channel.logs, ["b", "c", "d"]);
48+
});
49+
50+
test("Appends and rolls over", () => {
51+
channel.appendLine("a");
52+
channel.appendLine("b");
53+
channel.appendLine("c");
54+
channel.append("d");
55+
channel.appendLine("e");
56+
assert.deepEqual(channel.logs, ["b", "cd", "e"]);
57+
});
58+
59+
test("Replaces", () => {
60+
channel.appendLine("a");
61+
channel.appendLine("b");
62+
channel.appendLine("c");
63+
channel.appendLine("d");
64+
channel.replace("e");
65+
assert.deepEqual(channel.logs, ["e"]);
66+
});
67+
});

0 commit comments

Comments
 (0)