优化日志系统:网络请求降噪并按接口聚合,补齐双时间说明与实例标识,发布0.3.2

- 逐条网络日志(请求完成 / 接口返回异常 / 响应刷新CSRF已同步)从 info/warn 降为 debug:
  实测一次 6 账号的模拟抢购里 233 条日志有 174 条(74%)属于这类;真实抢购会产生几千条,
  把界面仅 800 条的内存日志冲掉,反而看不到关键信息。HTTP/解析/传输层错误仍为 error 级
- 新增按接口聚合的网络统计,抢购任务结束时输出一行,替代逐条刷屏:
  "接口统计: 锁定兑换商品 ×341 失败341 平均=95ms 最慢=312ms 最后错误=活动火爆,请稍后再试"
- 日志文件开头新增表头:实例名、数据目录、双时间格式说明(本地时间 + D 斗鱼估算时间)、
  并说明"校准完成前偏移为 0,只显示本地时间"。双时间一直存在却没有任何地方解释过,
  多实例日志复制或汇总后也分不清来源
- 界面日志面板:显示日志级别并按级别着色(此前级别只存在于 CSS class,复制出去就丢);
  新增"复制"按钮,按与日志文件一致的单行格式导出(此前直接复制会变成 5 行一条、
  1204 行里其实只有 233 条日志)
- 设置页"日志显示等级"补充说明:Info 起只显示关键日志,需要逐请求细节时切到 Debug
- 新增测试:TestRequestStatsAggregatesByLabel、TestLogFileHeaderDocumentsFormat、
  TestRunNowLogsAggregateInsteadOfPerRequest(断言必须有聚合行、且不得出现逐条请求日志)
This commit is contained in:
yml2213
2026-10-03 19:33:14 +08:00
parent 43270e04a6
commit dce9ce2a5a
12 changed files with 328 additions and 11 deletions
+42
View File
@@ -8,6 +8,7 @@ import (
"strings"
"sync/atomic"
"testing"
"time"
"douyu_go/internal/activity"
"douyu_go/internal/trace"
@@ -499,3 +500,44 @@ func testCTNClient(t *testing.T, ctnURL string) *DouyuClient {
cli.HTTPClient = http.DefaultClient
return cli
}
// TestRequestStatsAggregatesByLabel 验证网络日志降噪后仍保留诊断价值:
// 同类接口聚合成一条统计(次数/失败/平均/最慢/最后错误),并可按任务重置。
func TestRequestStatsAggregatesByLabel(t *testing.T) {
cli := &DouyuClient{}
cli.recordRequest("锁定兑换商品", 80*time.Millisecond, true, "活动火爆,请稍后再试")
cli.recordRequest("锁定兑换商品", 120*time.Millisecond, true, "当前网络繁忙,请稍后再试")
cli.recordRequest("锁定兑换商品", 60*time.Millisecond, false, "")
cli.recordRequest("支付锁单", 300*time.Millisecond, false, "")
stats := cli.RequestStats()
if len(stats) != 2 {
t.Fatalf("统计条目 = %d, want 2: %#v", len(stats), stats)
}
// 次数多的排前面
if stats[0].Label != "锁定兑换商品" {
t.Fatalf("首条应为调用最多的接口, got %q", stats[0].Label)
}
lock := stats[0]
if lock.Count != 3 || lock.Failed != 2 {
t.Fatalf("锁定兑换商品 次数=%d 失败=%d, want 3/2", lock.Count, lock.Failed)
}
if lock.Slowest != 120*time.Millisecond {
t.Fatalf("最慢 = %v, want 120ms", lock.Slowest)
}
if lock.Total != 260*time.Millisecond {
t.Fatalf("总耗时 = %v, want 260ms", lock.Total)
}
if lock.LastErr != "当前网络繁忙,请稍后再试" {
t.Fatalf("最后一次错误 = %q", lock.LastErr)
}
if stats[1].Failed != 0 || stats[1].LastErr != "" {
t.Fatalf("支付锁单不应有失败记录: %#v", stats[1])
}
cli.ResetRequestStats()
if got := cli.RequestStats(); len(got) != 0 {
t.Fatalf("重置后应为空, got %#v", got)
}
}