Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e8221d3
refactor(chat): restructure message processing and event lifecycle ha…
chryzxc Jun 15, 2026
ce49529
feat: enhance raw response handling and session processing
chryzxc Jun 18, 2026
f0f36f0
refactor(chat): enhance message lifecycle
chryzxc Jun 20, 2026
f3489c3
refactor(chat): update debug payload filtering and test coverage
chryzxc Jun 23, 2026
97a0751
refactor(chat): enhance session handling and loading indicators
chryzxc Jun 24, 2026
7973840
refactor: add UI formatter module for subagent processing
chryzxc Jun 27, 2026
f306516
refactor: enhance interactive event handling and message processing
chryzxc Jun 28, 2026
80a99fb
refactor(tests): reorganize test structure from unit to regression
chryzxc Jul 3, 2026
2608df9
refactor(services): enhance message processing and event lifecycle
chryzxc Jul 3, 2026
5e0ff8a
refactor(providers): update chat and plan view providers
chryzxc Jul 3, 2026
3e93b2f
refactor(webview): restructure chat components and message handling
chryzxc Jul 3, 2026
3afdf1e
refactor(webview): add background task ownership and presentation
chryzxc Jul 3, 2026
78ac3fd
test: update test patterns to match current implementation
chryzxc Jul 3, 2026
e0bf438
refactor(webview): restructure message processing and event lifecycle
chryzxc Jul 5, 2026
70cde8f
refactor(webview): suppress non-fatal stderr warnings and add block c…
chryzxc Jul 5, 2026
4a087de
refactor(webview): consolidate state management and simplify message …
chryzxc Jul 6, 2026
08e18b4
refactor(webview): streamline session state handling and enhance test…
chryzxc Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
198 changes: 198 additions & 0 deletions LOGGING_BEST_PRACTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Logging Best Practices Guide

This guide provides best practices for structured logging in the OpenCode VSCode extension to ensure logs are readable, searchable, and useful for debugging.

## Core Principles

### 1. Use Structured Messages
❌ **Bad:** `Session ${session.id}: ${existingMessages.length} existing messages`
✅ **Good:** `"Session message context loaded"` with context `{ sessionId, existingMessageCount, isNewSession }`

**Why:** Structured messages are parseable by log analysis tools and easier to search.

### 2. Keep Messages Action-Oriented
❌ **Bad:** `"handleGetMcpStatus error"`
✅ **Good:** `"Failed to get MCP server status"`

**Why:** Action-oriented messages clearly describe what happened or what failed.

### 3. Move Dynamic Data to Context
❌ **Bad:** `log.error("Failed to read file ${filePath}", { filePath })`
✅ **Good:** `log.error("Failed to read attached file", { filePath, error })`

**Why:** Keeps message format consistent and makes logs easier to parse.

### 4. Use Appropriate Log Levels
- **error**: Operation failed and functionality is broken
- **warn**: Potential issue but operation continues (fallbacks, retries)
- **info**: Normal operation milestones and state changes
- **debug**: Detailed diagnostic information for troubleshooting

### 5. Include Relevant Context
Always include relevant identifiers and metadata:
```typescript
log.error("Failed to abort active request", {
sessionId: resolvedSessionId,
error: error instanceof Error ? error.message : String(error),
}, error as Error);
```

### 6. Use Feature Flow Tracking
For multi-step operations, use feature flow tracking:
```typescript
const flow = log.startFeatureFlow('DataImport', { source: 'api' });
log.featureStep(flow, 'validation', { valid: 95, invalid: 5 });
log.endFeatureFlow(flow, { success: true, imported: 95 });
```

## Message Format Patterns

### Error Messages
**Pattern:** `"Failed to <action> <target>"`
```typescript
log.error("Failed to read attached image", {
filePath: uri.fsPath,
error: error.message,
}, error);
```

### Warning Messages
**Pattern:** `"<target> failed, <fallback action>"` or `"Failed to <action>, <consequence>"`
```typescript
log.warn("SDK file search failed, using VS Code fallback", {
query,
error: error.message,
});
```

### Info Messages
**Pattern:** `"<target> <action> past tense>"` or `"<action> <target>"`
```typescript
log.info("MCP server status sent to webview", {
serverCount: Object.keys(servers).length,
toolCount: toolIds.length,
});
```

### Debug Messages
**Pattern:** `"<component> <state/detailed action>"` or `"<action> completed"`
```typescript
log.debug("Session message context loaded", {
sessionId: session.id,
existingMessageCount: existingMessages.length,
isNewSession,
});
```

## Context Object Guidelines

### Always Include
- **Identifiers**: `sessionId`, `filePath`, `messageId`, `correlationId`
- **Error details**: `error: error.message` (if Error object)
- **Counts/quantities**: `count`, `duration`, `size`

### Naming Convention
Use **camelCase** for context keys:
```typescript
✅ { sessionId, existingMessageCount, isNewSession }
❌ { session_id, existing_message_count, is_new_session }
```

### Derived Data
Calculate and include relevant derived values:
```typescript
log.debug("AI response received", {
sessionId: session.id,
durationSeconds: duration, // Calculated value
hasData: Boolean(responseData),
status: response.response?.status,
});
```

## Error Handling Patterns

### With Error Object
```typescript
} catch (error) {
log.error("Failed to read attached file", {
filePath,
error: error instanceof Error ? error.message : String(error),
}, error as Error);
}
```

### Without Error Object
```typescript
} catch (error) {
log.error("Failed to abort active request", {
sessionId: resolvedSessionId,
error: error instanceof Error ? error.message : String(error),
}, error as Error);
}
```

## Feature Flow Examples

### Simple Flow
```typescript
const flow = log.startFeatureFlow('FileSearch', { query });
try {
const results = await searchFiles(query);
log.endFeatureFlow(flow, {
result: 'completed',
resultCount: results.length
});
} catch (error) {
log.endFeatureFlow(flow, {
result: 'failed',
error: String(error)
});
}
```

### Multi-Step Flow
```typescript
const flow = log.startFeatureFlow('DataImport', { source: 'api' });
log.featureStep(flow, 'validation', { valid: 95, invalid: 5 });
log.featureStep(flow, 'transformation', { transformed: 95 });
log.featureStep(flow, 'loading', { loaded: 95 });
log.endFeatureFlow(flow, { success: true, imported: 95 });
```

## Searchable Logs

Good logs make it easy to:
1. **Find errors:** Search for "Failed" or "error" level
2. **Track sessions:** Search for specific `sessionId`
3. **Measure performance:** Search for "duration" or "performance"
4. **Debug features:** Search for feature names in feature flows

## Console Output Examples

### Pretty Mode (default)
```
[14:23:45.123] ❌ [ERROR] [ChatView] Failed to read attached file {"filePath":"src/app.ts","error":"ENOENT"}
[14:23:45.456] ⚠️ [WARN] [ChatView] SDK file search failed, using VS Code fallback {"query":"test"}
[14:23:46.789] ℹ️ [INFO] [ChatView] MCP server status sent to webview {"serverCount":3,"toolCount":15}
[14:23:47.012] 🔍 [DEBUG] [ChatView] Session message context loaded {"sessionId":"abc-123","existingMessageCount":42}
```

### JSON Mode
```json
{"timestamp":"2026-06-07T14:23:45.123Z","level":"error","category":"ChatView","message":"Failed to read attached file","context":{"filePath":"src/app.ts","error":"ENOENT"}}
```

## Common Mistakes to Avoid

1. **Embedding data in messages:** Don't use template strings for dynamic values
2. **Vague messages:** Avoid "error occurred" or "failed" without context
3. **Missing identifiers:** Always include relevant IDs (sessionId, filePath, etc.)
4. **Inconsistent naming:** Use the same terminology across logs
5. **Wrong log levels:** Don't use `error` for recoverable issues

## Testing Your Logs

1. **Enable JSON mode** and verify logs parse correctly
2. **Search for specific patterns** to ensure they're found
3. **Check console output** in both pretty and hybrid modes
4. **Verify error context** includes all relevant information
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,15 @@
"maximum": 0.99,
"description": "Fraction of model context limit at which auto-compaction fires (0.9 = 90%)"
},
"opencode.requestTimeout": {
"type": "number",
"default": 120000,
"minimum": 10000,
"maximum": 600000,
"description": "Request timeout in milliseconds for API calls (default: 120s, min: 10s, max: 10min)",
"scope": "window",
"order": 10
},
"opencode.requestTimeout": {
"type": "number",
"default": 60000,
"minimum": 10000,
"maximum": 600000,
"description": "Request timeout in milliseconds for API calls (default: 60s, min: 10s, max: 10min)",
"scope": "window",
"order": 10
},
"opencode.complexQueryMultiplier": {
"type": "number",
"default": 1.5,
Expand All @@ -262,15 +262,15 @@
}
}
},
"scripts": {
"build": "npm run structured-output:sync && npm run webview:build && npm run compile",
"verify": "npm run structured-output:check && npm run build && npm run lint && npm test",
"scripts": {
"build": "npm run webview:build && npm run compile",
"verify": "npm run build && npm run lint && npm test",
"vscode:prepublish": "npm run build",
"compile": "node esbuild.config.cjs",
"watch": "node esbuild.config.cjs --watch",
"dev": "node scripts/dev-simple.mjs",
"dev:full": "node scripts/dev-hotreload.mjs",
"webview:build": "npm run structured-output:sync && npm --prefix webview/shared run build",
"webview:build": "npm --prefix webview/shared run build",
"webview:watch": "npm --prefix webview/shared run build -- --watch",
"structured-output:sync": "node scripts/sync-structured-output-contract.mjs",
"structured-output:check": "node scripts/sync-structured-output-contract.mjs --check",
Expand Down
18 changes: 18 additions & 0 deletions scripts/sync-structured-output-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ const targets = [
"structuredOutputValidator.ts",
),
},
{
source: path.join(
repoRoot,
"src",
"shared",
"centralizedDebugPayloadFilter.ts",
),
dest: path.join(
repoRoot,
"webview",
"shared",
"src",
"chat",
"lib",
"generated",
"centralizedDebugPayloadFilter.ts",
),
},
];

function normalize(value) {
Expand Down
1 change: 1 addition & 0 deletions src/providers/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading