route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
338 lines • 13.6 kB
JavaScript
"use strict";
/**
* Request Lifecycle Logger with Individual Request Tracking
* 按request单独文件记录全流程,支持时间分块和图形界面解析
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequestLifecycleLogger = void 0;
const fs_1 = require("fs");
const path = __importStar(require("path"));
const os = __importStar(require("os"));
class RequestLifecycleLogger {
config;
baseLogDir;
portLogDir;
timeBlockSizeMs;
requestFiles = new Map();
nodeResponseFiles = new Map();
systemLogFile = null;
stopReasonLogFile = null;
constructor(config) {
this.config = {
port: config.port,
logDir: config.logDir || path.join(os.homedir(), '.route-claude-code', 'request-lifecycle-logs'),
timeBlockSize: config.timeBlockSize || 5,
maxRetentionDays: config.maxRetentionDays || 7
};
this.baseLogDir = this.config.logDir;
this.portLogDir = path.join(this.baseLogDir, `port-${this.config.port}`);
this.timeBlockSizeMs = this.config.timeBlockSize * 60 * 1000;
this.initialize().catch(error => {
console.error('Failed to initialize request lifecycle logger:', error);
});
}
async initialize() {
try {
// Create base directories
await fs_1.promises.mkdir(this.baseLogDir, { recursive: true });
await fs_1.promises.mkdir(this.portLogDir, { recursive: true });
// Initialize log files
await this.initializeSystemLogFile();
await this.initializeStopReasonLogFile();
// 使用统一日志系统而不是直接console输出
// console.log('Request lifecycle logger initialized');
}
catch (error) {
console.error('Failed to initialize request lifecycle logger:', error);
}
}
getBeijingTimestamp() {
const now = new Date();
const beijingOffset = 8 * 60 * 60 * 1000;
const beijingTime = new Date(now.getTime() + beijingOffset);
const year = beijingTime.getUTCFullYear();
const month = String(beijingTime.getUTCMonth() + 1).padStart(2, '0');
const day = String(beijingTime.getUTCDate()).padStart(2, '0');
const hours = String(beijingTime.getUTCHours()).padStart(2, '0');
const minutes = String(beijingTime.getUTCMinutes()).padStart(2, '0');
const seconds = String(beijingTime.getUTCSeconds()).padStart(2, '0');
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
}
getTimeBlockDirectory(timestamp) {
const time = timestamp ? new Date(timestamp) : new Date();
const timeBlockMs = Math.floor(time.getTime() / this.timeBlockSizeMs) * this.timeBlockSizeMs;
const timeBlockTime = new Date(timeBlockMs);
const year = timeBlockTime.getFullYear();
const month = String(timeBlockTime.getMonth() + 1).padStart(2, '0');
const day = String(timeBlockTime.getDate()).padStart(2, '0');
const hours = String(timeBlockTime.getHours()).padStart(2, '0');
const minutes = String(timeBlockTime.getMinutes()).padStart(2, '0');
return path.join(this.portLogDir, `timeblock-${year}${month}${day}-${hours}${minutes}`);
}
async initializeSystemLogFile() {
const filename = `system-${this.getBeijingTimestamp()}.log`;
const filepath = path.join(this.portLogDir, filename);
this.systemLogFile = await fs_1.promises.open(filepath, 'a');
// Write header
const header = {
timestamp: new Date().toISOString(),
beijingTimestamp: this.getBeijingTimestamp(),
type: 'system_log_initialized',
port: this.config.port
};
await this.systemLogFile.writeFile(JSON.stringify(header) + '\n');
}
async initializeStopReasonLogFile() {
const filename = `stop-reason-${this.getBeijingTimestamp()}.log`;
const filepath = path.join(this.portLogDir, filename);
this.stopReasonLogFile = await fs_1.promises.open(filepath, 'a');
// Write header
const header = {
timestamp: new Date().toISOString(),
beijingTimestamp: this.getBeijingTimestamp(),
type: 'stop_reason_log_initialized',
port: this.config.port
};
await this.stopReasonLogFile.writeFile(JSON.stringify(header) + '\n');
}
async getRequestFileHandle(requestId) {
if (this.requestFiles.has(requestId)) {
return this.requestFiles.get(requestId);
}
const filename = `request-${requestId}.jsonl`;
const filepath = path.join(this.portLogDir, filename);
const fileHandle = await fs_1.promises.open(filepath, 'a');
this.requestFiles.set(requestId, fileHandle);
return fileHandle;
}
async getNodeResponseFileHandle(timeBlockDir) {
const cacheKey = timeBlockDir;
if (this.nodeResponseFiles.has(cacheKey)) {
return this.nodeResponseFiles.get(cacheKey);
}
await fs_1.promises.mkdir(timeBlockDir, { recursive: true });
const filename = 'node-responses.jsonl';
const filepath = path.join(timeBlockDir, filename);
const fileHandle = await fs_1.promises.open(filepath, 'a');
this.nodeResponseFiles.set(cacheKey, fileHandle);
return fileHandle;
}
/**
* 开始记录请求生命周期
*/
async startRequestLifecycle(requestId, provider, model, method, path, data) {
try {
const startTime = new Date().toISOString();
const beijingTimestamp = this.getBeijingTimestamp();
const lifecycle = {
requestId,
port: this.config.port,
startTime,
provider,
model,
status: 'started',
stages: [{
stage: 'request_start',
timestamp: startTime,
beijingTimestamp,
data: { method, path, ...data }
}]
};
const fileHandle = await this.getRequestFileHandle(requestId);
await fileHandle.writeFile(JSON.stringify(lifecycle) + '\n');
// Log to system file
if (this.systemLogFile) {
await this.systemLogFile.writeFile(JSON.stringify({
timestamp: startTime,
beijingTimestamp,
type: 'request_started',
requestId,
provider,
model,
method,
path
}) + '\n');
}
// 使用统一日志系统而不是直接console输出
// console.log(`Request lifecycle started for ${requestId}`);
}
catch (error) {
console.error('Failed to start request lifecycle:', error);
}
}
/**
* 记录请求阶段
*/
async logRequestStage(requestId, stage, data, duration) {
try {
const timestamp = new Date().toISOString();
const beijingTimestamp = this.getBeijingTimestamp();
const stageData = {
stage,
timestamp,
beijingTimestamp,
data,
duration
};
const fileHandle = await this.getRequestFileHandle(requestId);
// Read current lifecycle, add stage, and write back
// For performance, we'll append a stage entry instead of rewriting the whole file
const stageEntry = {
type: 'stage',
requestId,
stage: stageData
};
await fileHandle.writeFile(JSON.stringify(stageEntry) + '\n');
}
catch (error) {
console.error('Failed to log request stage:', error);
}
}
/**
* 记录节点响应(按时间分块)
*/
async logNodeResponse(nodeId, response, metadata) {
try {
const timestamp = new Date().toISOString();
const beijingTimestamp = this.getBeijingTimestamp();
const timeBlockDir = this.getTimeBlockDirectory();
const responseEntry = {
timestamp,
beijingTimestamp,
nodeId,
response,
metadata,
timeBlock: timeBlockDir.split('-').pop()
};
const fileHandle = await this.getNodeResponseFileHandle(timeBlockDir);
await fileHandle.writeFile(JSON.stringify(responseEntry) + '\n');
}
catch (error) {
console.error('Failed to log node response:', error);
}
}
/**
* 完成请求生命周期
*/
async completeRequestLifecycle(requestId, status, responseTime, finishReason, responseData) {
try {
const endTime = new Date().toISOString();
const beijingTimestamp = this.getBeijingTimestamp();
const completionEntry = {
type: 'completion',
requestId,
endTime,
beijingTimestamp,
status,
responseTime,
finishReason,
responseData
};
const fileHandle = await this.getRequestFileHandle(requestId);
await fileHandle.writeFile(JSON.stringify(completionEntry) + '\n');
// Log finish reason to separate file
if (finishReason) {
await this.logStopReason(requestId, finishReason, endTime, beijingTimestamp);
}
// Log to system file
if (this.systemLogFile) {
await this.systemLogFile.writeFile(JSON.stringify({
timestamp: endTime,
beijingTimestamp,
type: 'request_completed',
requestId,
status,
responseTime,
finishReason
}) + '\n');
}
// 使用统一日志系统而不是直接console输出
// console.log(`Request lifecycle completed for ${requestId} in ${responseTime}ms`);
}
catch (error) {
console.error('Failed to complete request lifecycle:', error);
}
}
/**
* 记录stop reason
*/
async logStopReason(requestId, finishReason, timestamp, beijingTimestamp) {
if (!this.stopReasonLogFile)
return;
const stopReasonEntry = {
timestamp,
beijingTimestamp,
requestId,
finishReason,
port: this.config.port
};
await this.stopReasonLogFile.writeFile(JSON.stringify(stopReasonEntry) + '\n');
}
/**
* 清理资源
*/
async cleanup() {
try {
// Close all request file handles
for (const [requestId, fileHandle] of this.requestFiles) {
await fileHandle.close();
}
this.requestFiles.clear();
// Close all node response file handles
for (const [timeBlockDir, fileHandle] of this.nodeResponseFiles) {
await fileHandle.close();
}
this.nodeResponseFiles.clear();
// Close system log file
if (this.systemLogFile) {
await this.systemLogFile.close();
this.systemLogFile = null;
}
// Close stop reason log file
if (this.stopReasonLogFile) {
await this.stopReasonLogFile.close();
this.stopReasonLogFile = null;
}
// 使用统一日志系统而不是直接console输出
// console.log('Request lifecycle logger cleanup completed');
}
catch (error) {
console.error('Failed to cleanup request lifecycle logger:', error);
}
}
}
exports.RequestLifecycleLogger = RequestLifecycleLogger;
//# sourceMappingURL=request-lifecycle-logger.js.map