Skip to content

Commit bcb4268

Browse files
author
Eric Wheeler
committed
feat: add global state storage size logging with threshold filter
Adds a utility function to log global state storage sizes with a configurable threshold filter (default 10KB) to help users identify large items. This provides visibility into which global state items are consuming significant storage space, making it easier to diagnose performance issues. Fixes: #3809 Signed-off-by: Eric Wheeler <[email protected]>
1 parent b8aa4b4 commit bcb4268

File tree

2 files changed

+91
-3
lines changed

2 files changed

+91
-3
lines changed

src/extension.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
2121
import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry"
2222
import { McpServerManager } from "./services/mcp/McpServerManager"
2323
import { telemetryService } from "./services/telemetry/TelemetryService"
24+
import { logGlobalStorageSize } from "./utils/storageUtils"
2425
import { API } from "./exports/api"
2526
import { migrateSettings } from "./utils/migrateSettings"
2627
import { formatLanguage } from "./shared/language"
@@ -43,12 +44,23 @@ import { initializeI18n } from "./i18n"
4344
*/
4445

4546
let outputChannel: vscode.OutputChannel
46-
let extensionContext: vscode.ExtensionContext
47+
let _extensionContext: vscode.ExtensionContext | undefined
48+
49+
/**
50+
* Returns the extension context.
51+
* @throws Error if the extension context is not available.
52+
*/
53+
export function getExtensionContext(): vscode.ExtensionContext {
54+
if (!_extensionContext) {
55+
throw new Error("Extension context is not available.")
56+
}
57+
return _extensionContext
58+
}
4759

4860
// This method is called when your extension is activated.
4961
// Your extension is activated the very first time the command is executed.
5062
export async function activate(context: vscode.ExtensionContext) {
51-
extensionContext = context
63+
_extensionContext = context
5264
outputChannel = vscode.window.createOutputChannel(Package.outputChannel)
5365
context.subscriptions.push(outputChannel)
5466
outputChannel.appendLine(`${Package.name} extension activated`)
@@ -130,6 +142,9 @@ export async function activate(context: vscode.ExtensionContext) {
130142
const socketPath = process.env.ROO_CODE_IPC_SOCKET_PATH
131143
const enableLogging = typeof socketPath === "string"
132144

145+
// Log global storage items larger than 10KB
146+
logGlobalStorageSize(10 * 1024)
147+
133148
// Watch the core files and automatically reload the extension host
134149
const enableCoreAutoReload = process.env?.NODE_ENV === "development"
135150
if (enableCoreAutoReload) {
@@ -150,7 +165,7 @@ export async function activate(context: vscode.ExtensionContext) {
150165
// This method is called when your extension is deactivated.
151166
export async function deactivate() {
152167
outputChannel.appendLine(`${Package.name} extension deactivated`)
153-
await McpServerManager.cleanup(extensionContext)
168+
await McpServerManager.cleanup(getExtensionContext())
154169
telemetryService.shutdown()
155170
TerminalRegistry.cleanup()
156171
}

src/utils/storageUtils.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { getExtensionContext } from "../extension"
2+
3+
/**
4+
* Formats a number of bytes into a human-readable string with units (Bytes, KB, MB, GB, etc.).
5+
*
6+
* @param bytes The number of bytes.
7+
* @param decimals The number of decimal places to include (default is 2).
8+
* @returns A string representing the formatted bytes.
9+
*/
10+
export function formatBytes(bytes: number, decimals = 2): string {
11+
if (bytes === 0) {
12+
return "0 Bytes"
13+
}
14+
const k = 1024
15+
const dm = decimals < 0 ? 0 : decimals
16+
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
17+
const i = Math.floor(Math.log(bytes) / Math.log(k))
18+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]
19+
}
20+
21+
/**
22+
* Logs the estimated sizes of items in the VS Code global state to the console.
23+
* Only logs items that exceed the specified minimum size threshold.
24+
*
25+
* @param minSizeBytes The minimum size in bytes to log (default: 10KB)
26+
*/
27+
export function logGlobalStorageSize(minSizeBytes: number = 10 * 1024): void {
28+
const context = getExtensionContext()
29+
try {
30+
console.log(`[Roo Code] Global State Storage Estimates (items > ${formatBytes(minSizeBytes)}):`)
31+
const globalStateKeys = context.globalState.keys()
32+
const stateSizes: { key: string; size: number }[] = []
33+
let totalSize = 0
34+
let itemsSkipped = 0
35+
36+
for (const key of globalStateKeys) {
37+
const value = context.globalState.get(key)
38+
try {
39+
const valueString = JSON.stringify(value)
40+
const size = valueString.length
41+
totalSize += size
42+
43+
if (size >= minSizeBytes) {
44+
stateSizes.push({ key, size })
45+
} else {
46+
itemsSkipped++
47+
}
48+
} catch (e) {
49+
// Handle cases where value might not be stringifiable
50+
stateSizes.push({ key, size: -1 }) // Indicate an error or unmeasurable size
51+
console.log(` - ${key}: (Error calculating size)`)
52+
}
53+
}
54+
55+
stateSizes.sort((a, b) => b.size - a.size)
56+
57+
stateSizes.forEach((item) => {
58+
if (item.size === -1) {
59+
// Already logged error
60+
} else if (item.size === undefined) {
61+
console.log(` - ${item.key}: (undefined value)`)
62+
} else {
63+
console.log(` - ${item.key}: ${formatBytes(item.size)}`)
64+
}
65+
})
66+
67+
console.log(` Total size of all items: ${formatBytes(totalSize)}`)
68+
console.log(` Items below threshold (${itemsSkipped}): not shown`)
69+
console.log("---")
70+
} catch (e: any) {
71+
console.log(`Error displaying global state sizes: ${e.message}`)
72+
}
73+
}

0 commit comments

Comments
 (0)