Add SDK usage tracking to JSONL logs
Features: - New UsageLogger utility that writes usage metrics to daily JSONL files - Captures token counts, costs, timing, and cache metrics from SDK result messages - Usage logs stored in ~/.claude-mem/usage-logs/ (one file per day) - Added analyze-usage.js script for analyzing usage patterns Usage data captured: - Token counts (input, output, cache creation, cache read) - Total cost in USD per API call - Duration metrics (total and API time) - Number of turns per session - Session and project attribution Analysis script features: - Aggregates totals by project and model - Shows cache hit rates and savings - Displays cost breakdowns and averages - npm scripts: usage:analyze and usage:today Files: - src/utils/usage-logger.ts (new) - src/services/worker-service.ts (modified - captures SDK result messages) - scripts/analyze-usage.js (new) - package.json (added usage:* npm scripts) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import type { SDKSession } from '../sdk/prompts.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { ensureAllDataDirs } from '../shared/paths.js';
|
||||
import { execSync } from 'child_process';
|
||||
import { UsageLogger } from '../utils/usage-logger.js';
|
||||
|
||||
const MODEL = process.env.CLAUDE_MEM_MODEL || 'claude-sonnet-4-5';
|
||||
const DISALLOWED_TOOLS = ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch'];
|
||||
@@ -83,10 +84,12 @@ class WorkerService {
|
||||
private app: express.Application;
|
||||
private port: number | null = null;
|
||||
private sessions: Map<number, ActiveSession> = new Map();
|
||||
private usageLogger: UsageLogger;
|
||||
|
||||
constructor() {
|
||||
this.app = express();
|
||||
this.app.use(express.json({ limit: '50mb' }));
|
||||
this.usageLogger = new UsageLogger();
|
||||
|
||||
// Health check
|
||||
this.app.get('/health', this.handleHealth.bind(this));
|
||||
@@ -409,6 +412,41 @@ class WorkerService {
|
||||
// Parse and store with prompt number
|
||||
this.handleAgentMessage(session, textContent, session.lastPromptNumber);
|
||||
}
|
||||
|
||||
// Capture usage data from result messages
|
||||
if (message.type === 'result' && message.subtype === 'success') {
|
||||
const usageData = {
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionDbId: session.sessionDbId,
|
||||
claudeSessionId: session.claudeSessionId,
|
||||
project: session.project,
|
||||
promptNumber: session.lastPromptNumber,
|
||||
model: MODEL,
|
||||
sessionId: message.session_id,
|
||||
uuid: message.uuid,
|
||||
durationMs: message.duration_ms,
|
||||
durationApiMs: message.duration_api_ms,
|
||||
numTurns: message.num_turns,
|
||||
totalCostUsd: message.total_cost_usd,
|
||||
usage: {
|
||||
inputTokens: message.usage.input_tokens,
|
||||
outputTokens: message.usage.output_tokens,
|
||||
cacheCreationInputTokens: message.usage.cache_creation_input_tokens,
|
||||
cacheReadInputTokens: message.usage.cache_read_input_tokens
|
||||
}
|
||||
};
|
||||
|
||||
this.usageLogger.logUsage(usageData);
|
||||
|
||||
logger.info('SDK', 'Usage data logged', {
|
||||
sessionId: session.sessionDbId,
|
||||
inputTokens: message.usage.input_tokens,
|
||||
outputTokens: message.usage.output_tokens,
|
||||
cacheCreation: message.usage.cache_creation_input_tokens,
|
||||
cacheRead: message.usage.cache_read_input_tokens,
|
||||
totalCostUsd: message.total_cost_usd
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Mark completed
|
||||
|
||||
Reference in New Issue
Block a user