Files
huya-go/internal/logger/async.go
T

95 lines
1.9 KiB
Go

package logger
import (
"fmt"
"io"
"sync"
"sync/atomic"
"time"
)
// asyncBufferSize 异步写盘队列长度。队列满时丢弃日志,优先保护抢购链路。
const asyncBufferSize = 8192
// AsyncWriter 异步日志写入器,队列满时丢弃日志以保护主链路。
type AsyncWriter struct {
mu sync.RWMutex
dst io.Writer
ch chan []byte
wg sync.WaitGroup
closed uint32
dropped uint64
}
// NewAsyncWriter 创建异步日志写入器。
func NewAsyncWriter(dst io.Writer, buffer int) *AsyncWriter {
if buffer <= 0 {
buffer = 1024
}
w := &AsyncWriter{
dst: dst,
ch: make(chan []byte, buffer),
}
w.wg.Add(1)
go w.run()
return w
}
// Write 异步写日志;队列满时丢弃,避免抢购请求被磁盘 I/O 阻塞。
func (w *AsyncWriter) Write(p []byte) (int, error) {
if w == nil {
return len(p), nil
}
w.mu.RLock()
defer w.mu.RUnlock()
if atomic.LoadUint32(&w.closed) == 1 {
return len(p), nil
}
buf := append([]byte(nil), p...)
select {
case w.ch <- buf:
default:
atomic.AddUint64(&w.dropped, 1)
}
return len(p), nil
}
// Dropped 返回因队列满被丢弃的日志条数。
func (w *AsyncWriter) Dropped() uint64 {
if w == nil {
return 0
}
return atomic.LoadUint64(&w.dropped)
}
func (w *AsyncWriter) run() {
defer w.wg.Done()
for p := range w.ch {
_, _ = w.dst.Write(p)
}
if dropped := atomic.LoadUint64(&w.dropped); dropped > 0 {
notice := formatLogLine(Entry{
Time: time.Now().Format(entryTimeLayout),
Level: LevelWarn,
Category: "日志",
Message: fmt.Sprintf("日志队列已丢弃 %d 条,已优先保护抢购链路", dropped),
at: time.Now(),
})
_, _ = w.dst.Write([]byte(notice))
}
}
// Close 关闭写入器,等待队列清空。
func (w *AsyncWriter) Close() error {
if w == nil {
return nil
}
w.mu.Lock()
defer w.mu.Unlock()
if atomic.CompareAndSwapUint32(&w.closed, 0, 1) {
close(w.ch)
w.wg.Wait()
}
return nil
}