Commit a50f59d5153a2b98845378b4ced1829c5e7ee582
1 parent
e287643c
perf: billing.log date-sharding - write to billing-YYYY-MM-DD.log, read with 30-…
…day window, skip old files
Showing
1 changed file
with
36 additions
and
21 deletions
server/routes/agent-chat.js
| @@ -791,28 +791,42 @@ router.get('/quota', (req, res) => { | @@ -791,28 +791,42 @@ router.get('/quota', (req, res) => { | ||
| 791 | try { | 791 | try { |
| 792 | const DATA_ROOT = path.resolve(__dirname, '../../data'); | 792 | const DATA_ROOT = path.resolve(__dirname, '../../data'); |
| 793 | const records = []; | 793 | const records = []; |
| 794 | + const daysBack = parseInt(String(req.query.days || '30')) || 30; // 默认读最近 30 天 | ||
| 795 | + const cutoff = new Date(Date.now() - daysBack * 86400000).toISOString(); | ||
| 794 | 796 | ||
| 795 | - // 读取所有 billing.log | 797 | + // 读取 billing 日志(兼容旧 billing.log + 新 billing-YYYY-MM-DD.log) |
| 796 | const readBilling = (dir) => { | 798 | const readBilling = (dir) => { |
| 797 | - const p = path.join(dir, 'billing.log'); | ||
| 798 | - if (fs.existsSync(p)) { | ||
| 799 | - fs.readFileSync(p, 'utf-8').split('\n').forEach(line => { | ||
| 800 | - line = line.trim(); | ||
| 801 | - if (line.startsWith('{')) { | ||
| 802 | - try { records.push(JSON.parse(line)); } catch(e) {} | ||
| 803 | - } else if (line) { | ||
| 804 | - // 旧的纯文本兼容: 2026-03-28T18:00:00.000Z | Agent: master | Conv: 123 | Cost: $0.123 | Duration: 123ms | ||
| 805 | - try { | ||
| 806 | - const parts = line.split('|').map(p => p.trim()); | ||
| 807 | - const ts = parts[0]; | ||
| 808 | - const ai = parts[1].replace('Agent:', '').trim(); | ||
| 809 | - const ci = parts[2].replace('Conv:', '').trim(); | ||
| 810 | - const cost = parseFloat(parts[3].replace('Cost: $', '').trim()); | ||
| 811 | - const dur = parseInt(parts[4].replace('Duration:', '').replace('ms', '').trim()); | ||
| 812 | - records.push({ timestamp: ts, agentId: ai, convId: ci, costUsd: cost || 0, durationMs: dur || 0, usage: { input: 0, output: 0 } }); | ||
| 813 | - } catch(e) {} | ||
| 814 | - } | ||
| 815 | - }); | 799 | + if (!fs.existsSync(dir)) return; |
| 800 | + const files = fs.readdirSync(dir).filter(f => f.startsWith('billing')); | ||
| 801 | + for (const file of files) { | ||
| 802 | + // 按日分片优化:跳过超出时间范围的文件 | ||
| 803 | + const dateMatch = file.match(/billing-(\d{4}-\d{2}-\d{2})\.log/); | ||
| 804 | + if (dateMatch && dateMatch[1] < cutoff.substring(0, 10)) continue; | ||
| 805 | + | ||
| 806 | + const p = path.join(dir, file); | ||
| 807 | + try { | ||
| 808 | + fs.readFileSync(p, 'utf-8').split('\n').forEach(line => { | ||
| 809 | + line = line.trim(); | ||
| 810 | + if (line.startsWith('{')) { | ||
| 811 | + try { | ||
| 812 | + const rec = JSON.parse(line); | ||
| 813 | + if (rec.timestamp >= cutoff) records.push(rec); | ||
| 814 | + } catch(e) {} | ||
| 815 | + } else if (line) { | ||
| 816 | + // 旧的纯文本兼容 | ||
| 817 | + try { | ||
| 818 | + const parts = line.split('|').map(p => p.trim()); | ||
| 819 | + const ts = parts[0]; | ||
| 820 | + if (ts < cutoff) return; | ||
| 821 | + const ai = parts[1].replace('Agent:', '').trim(); | ||
| 822 | + const ci = parts[2].replace('Conv:', '').trim(); | ||
| 823 | + const cost = parseFloat(parts[3].replace('Cost: $', '').trim()); | ||
| 824 | + const dur = parseInt(parts[4].replace('Duration:', '').replace('ms', '').trim()); | ||
| 825 | + records.push({ timestamp: ts, agentId: ai, convId: ci, costUsd: cost || 0, durationMs: dur || 0, usage: { input: 0, output: 0 } }); | ||
| 826 | + } catch(e) {} | ||
| 827 | + } | ||
| 828 | + }); | ||
| 829 | + } catch(e) {} | ||
| 816 | } | 830 | } |
| 817 | }; | 831 | }; |
| 818 | 832 | ||
| @@ -2226,7 +2240,8 @@ router.post('/:agentId/chat', async (req, res) => { | @@ -2226,7 +2240,8 @@ router.post('/:agentId/chat', async (req, res) => { | ||
| 2226 | durationMs: event.params.duration_ms, | 2240 | durationMs: event.params.duration_ms, |
| 2227 | usage: event.params.usage || { input: 0, output: 0 } | 2241 | usage: event.params.usage || { input: 0, output: 0 } |
| 2228 | }; | 2242 | }; |
| 2229 | - fs.appendFileSync(path.join(billingDir, 'billing.log'), JSON.stringify(logEntry) + '\n', 'utf-8'); | 2243 | + const billingDate = new Date().toISOString().substring(0, 10); |
| 2244 | + fs.appendFileSync(path.join(billingDir, `billing-${billingDate}.log`), JSON.stringify(logEntry) + '\n', 'utf-8'); | ||
| 2230 | } catch (err) {} | 2245 | } catch (err) {} |
| 2231 | } | 2246 | } |
| 2232 | if (!fullOutput && event.params?.result) { | 2247 | if (!fullOutput && event.params?.result) { |