package proxy import ( "bufio" "fmt" "log" "os" "sync" "time" ) // 原始体会话与访问日志。 // 原始请求/响应落库的上限:开启 LogRaw 时流式响应可能是整段 SSE(百 KB~MB 级), // 单条日志几十 MB 会让 usage_logs 膨胀、列表查询和备份都变慢。超出部分截断并标注。 const ( rawRequestCap = 256 << 10 // 256 KiB rawResponseCap = 1 << 20 // 1 MiB ) // capRaw 把原始体截到 limit,并在末尾标注原始大小。 func capRaw(b []byte, limit int) string { if len(b) <= limit { return string(b) } return string(b[:limit]) + fmt.Sprintf("\n…(truncated by openteam: %d bytes total)", len(b)) } // accessLog 访问日志:每个请求一行,直接写 stderr 会在高 QPS 下变成系统调用热点。 // 这里套一层 64KB 缓冲 + 每秒 flush,log.Logger 自带互斥,写入只是内存拷贝。 var ( accessLogOnce sync.Once accessLog *log.Logger ) // syncBuf 让 bufio.Writer 的写与定时 flush 互斥: // bufio 本身不支持并发访问,而 flush 在独立 goroutine 里跑。 type syncBuf struct { mu sync.Mutex w *bufio.Writer } func (s *syncBuf) Write(p []byte) (int, error) { s.mu.Lock() defer s.mu.Unlock() return s.w.Write(p) } func (s *syncBuf) Flush() error { s.mu.Lock() defer s.mu.Unlock() return s.w.Flush() } func initAccessLog() { w := &syncBuf{w: bufio.NewWriterSize(os.Stderr, 64<<10)} accessLog = log.New(w, "", log.LstdFlags) go func() { t := time.NewTicker(time.Second) defer t.Stop() for range t.C { _ = w.Flush() } }() } // accessf 写一行访问日志(带缓冲,不阻塞请求)。 func accessf(format string, args ...any) { accessLogOnce.Do(initAccessLog) accessLog.Printf(format, args...) }