Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.

Commit c337c12

Browse files
feat(tracing): add support for Rust Tracing (#30)
* feature(traces): support rust tracing with spans * Make the extension hyper optimized for Rust Tracing * Add support JSON logs * Responsive Span Visualizer * Change click behavior * chore(package.json): bumps version to 0.5.0 --------- Co-authored-by: Priyank Chodisetti <priyank.ch@gmail.com>
1 parent 2b1fb43 commit c337c12

13 files changed

Lines changed: 7368 additions & 4424 deletions

package-lock.json

Lines changed: 5156 additions & 2016 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "traceback",
33
"displayName": "TraceBack",
44
"description": "A VS Code extension that brings telemetry data (traces, logs, and metrics) into your code.",
5-
"version": "0.4.14",
5+
"version": "0.5.0",
66
"publisher": "hyperdrive-eng",
77
"repository": {
88
"type": "git",
@@ -35,11 +35,11 @@
3535
"configuration": {
3636
"title": "TraceBack",
3737
"properties": {
38-
"traceback.claudeApiKey": {
39-
"type": "string",
40-
"default": "",
41-
"description": "API key for Claude AI service"
42-
}
38+
"traceback.claudeApiKey": {
39+
"type": "string",
40+
"default": "",
41+
"description": "API key for Claude AI service"
42+
}
4343
}
4444
},
4545
"commands": [
@@ -111,6 +111,23 @@
111111
"command": "traceback.inspectVariableFromContext",
112112
"title": "Inspect Value",
113113
"icon": "$(eye)"
114+
},
115+
{
116+
"command": "traceback.showSpanVisualizer",
117+
"title": "Show Span Visualizer",
118+
"category": "TraceBack"
119+
},
120+
{
121+
"command": "traceback.importLogs",
122+
"title": "Import Logs from File",
123+
"category": "Traceback",
124+
"icon": "$(file-add)"
125+
},
126+
{
127+
"command": "traceback.pasteLogs",
128+
"title": "Import Logs from Clipboard",
129+
"category": "Traceback",
130+
"icon": "$(clippy)"
114131
}
115132
],
116133
"viewsContainers": {
@@ -160,6 +177,21 @@
160177
"command": "traceback.openSettings",
161178
"when": "view == logExplorer",
162179
"group": "navigation@3"
180+
},
181+
{
182+
"command": "traceback.showSpanVisualizer",
183+
"when": "view == logExplorer",
184+
"group": "navigation@4"
185+
},
186+
{
187+
"command": "traceback.importLogs",
188+
"when": "view == tracebackLogs",
189+
"group": "navigation"
190+
},
191+
{
192+
"command": "traceback.pasteLogs",
193+
"when": "view == tracebackLogs",
194+
"group": "navigation"
163195
}
164196
],
165197
"view/item/context": [
@@ -181,6 +213,10 @@
181213
{
182214
"command": "traceback.openSettings",
183215
"title": "TraceBack: Open Settings"
216+
},
217+
{
218+
"command": "traceback.showSpanVisualizer",
219+
"title": "TraceBack: Show Span Visualizer"
184220
}
185221
]
186222
},
@@ -189,12 +225,24 @@
189225
"compile": "webpack --mode development",
190226
"watch": "webpack --mode development --watch",
191227
"pretest": "npm run compile",
192-
"test": "node ./out/test/runTest.js",
228+
"test": "jest",
193229
"package": "vsce package",
194230
"lint": "eslint src --ext ts"
195231
},
232+
"jest": {
233+
"preset": "ts-jest",
234+
"testEnvironment": "node",
235+
"testMatch": [
236+
"<rootDir>/src/**/*.test.ts"
237+
],
238+
"moduleFileExtensions": [
239+
"ts",
240+
"js"
241+
]
242+
},
196243
"devDependencies": {
197244
"@types/glob": "^7.2.0",
245+
"@types/jest": "^29.5.14",
198246
"@types/node": "^16.18.36",
199247
"@types/node-fetch": "^2.6.12",
200248
"@types/vscode": "^1.74.0",
@@ -203,6 +251,8 @@
203251
"@vscode/vsce": "^3.3.2",
204252
"eslint": "^8.42.0",
205253
"glob": "^8.1.0",
254+
"jest": "^29.7.0",
255+
"ts-jest": "^29.3.2",
206256
"ts-loader": "^9.5.2",
207257
"typescript": "^5.1.3",
208258
"webpack": "^5.99.3",

src/callStackExplorer.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as vscode from 'vscode';
2-
import { LogEntry, Span } from './logExplorer';
2+
import { RustLogEntry } from './logExplorer';
33
import { CallerAnalysis } from './claudeService';
44
import { ClaudeService } from './claudeService';
55
import * as path from 'path';
@@ -141,7 +141,7 @@ export class CallStackExplorerProvider implements vscode.TreeDataProvider<CallSt
141141
new vscode.EventEmitter<CallStackTreeItem | undefined | null | void>();
142142
readonly onDidChangeTreeData: vscode.Event<CallStackTreeItem | undefined | null | void> = this._onDidChangeTreeData.event;
143143

144-
private currentLogEntry: LogEntry | undefined;
144+
private currentLogEntry: RustLogEntry | undefined;
145145
private callerAnalysis: CallerNode[] = [];
146146
private claudeService: ClaudeService = ClaudeService.getInstance();
147147
private isAnalyzing: boolean = false;
@@ -152,7 +152,7 @@ export class CallStackExplorerProvider implements vscode.TreeDataProvider<CallSt
152152
/**
153153
* Set the spans for the current log entry and refresh the view
154154
*/
155-
public setLogEntry(log: LogEntry | undefined, isAnalyzing: boolean = false): void {
155+
public setLogEntry(log: RustLogEntry | undefined, isAnalyzing: boolean = false): void {
156156
this.currentLogEntry = log;
157157
this.callerAnalysis = [];
158158
this.isAnalyzing = isAnalyzing;
@@ -414,19 +414,15 @@ export class CallStackExplorerProvider implements vscode.TreeDataProvider<CallSt
414414
async analyzeCallers(
415415
currentLogLine: string,
416416
staticSearchString: string,
417-
allLogs: LogEntry[],
417+
allLogs: RustLogEntry[],
418418
potentialCallers: Array<{ filePath: string; lineNumber: number; code: string; functionName: string; functionRange?: vscode.Range }>
419419
): Promise<void> {
420420
try {
421421
this.isAnalyzing = true;
422422
this._onDidChangeTreeData.fire();
423423

424424
vscode.window.showInformationMessage('Computing call stack analysis...');
425-
const allLogLines = allLogs.map(log =>
426-
log.message ||
427-
log.jsonPayload?.fields?.message ||
428-
''
429-
).filter(msg => msg);
425+
const allLogLines = allLogs.map(log => log.message || '').filter(msg => msg);
430426

431427
const analysis = await this.claudeService.analyzeCallers(
432428
currentLogLine,

src/claudeService.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export interface CallerAnalysis {
2828
export interface RegexPattern {
2929
pattern: string;
3030
description: string;
31-
extractionMap: Record<string, string>;
31+
extractionMap: {
32+
[key: string]: string; // Maps regex capture group names to RustLogEntry fields
33+
};
3234
}
3335

3436
export class ClaudeService {
@@ -59,6 +61,16 @@ export class ClaudeService {
5961
await vscode.workspace.getConfiguration('traceback').update('claudeApiKey', key, true);
6062
}
6163

64+
/**
65+
* Analyze a log line to extract structured information
66+
*
67+
* @param logLine The log line to analyze
68+
* @param language The programming language of the codebase
69+
* @returns {Promise<LLMLogAnalysis>} Analysis results including:
70+
* 1. A static search string for finding similar logs
71+
* 2. Extracted variables and their values
72+
* 3. Include an "extractionMap" that maps regex capture group names to RustLogEntry field names
73+
*/
6274
public async analyzeLog(logMessage: string, language: string): Promise<LLMLogAnalysis> {
6375
if (!this.apiKey) {
6476
throw new Error('Claude API key not set. Please set your API key first.');
@@ -143,7 +155,7 @@ export class ClaudeService {
143155
const request = {
144156
messages: [{
145157
role: 'user',
146-
content: `Analyze this log message and extract:
158+
content: `Analyze this log message from Rust's tracing library and extract:
147159
1. Think and infer a possibly longest static substring that can be searched in the code base.
148160
2. Key-value pairs of any variables or dynamic values in the log
149161
@@ -153,17 +165,26 @@ Rules for static search string:
153165
- Predicted staticSearchString should be exact substring of logMessage.
154166
- logMessage.substring(staticSearchString) should be true.
155167
- No regular expressions allowed.
168+
- For Rust tracing library logs, focus on the span name and event message.
156169
157170
Rules for variables:
158-
- Extract all key-value pairs and dynamic values
171+
- Extract all key-value pairs and dynamic values from tracing fields
159172
- Preserve variable names as they appear in the log
173+
- Look for structured fields in the format key="value" or key=value
160174
161175
Examples:
162-
Input: "[PlaceOrder] user_id=\"3790d414-165b-11f0-8ee4-96dac6adf53a\" user_currency=\"USD\""
163-
Static: "PlaceOrder" (Note: brackets and log level removed)
176+
Input: "[TRACE traceback::span] span_name=\"PlaceOrder\" user_id=\"3790d414-165b-11f0-8ee4-96dac6adf53a\" user_currency=\"USD\""
177+
Static: "PlaceOrder" (Note: brackets, log level, and module path removed)
164178
Variables: {
165179
"user_id": "3790d414-165b-11f0-8ee4-96dac6adf53a",
166180
"user_currency": "USD"
181+
}
182+
183+
Input: "[DEBUG traceback::event] message=\"Processing order\" order_id=123 status=\"pending\""
184+
Static: "Processing order"
185+
Variables: {
186+
"order_id": 123,
187+
"status": "pending"
167188
}`
168189
}],
169190
model: this.model,

0 commit comments

Comments
 (0)