126 lines
2.2 KiB
Go
126 lines
2.2 KiB
Go
package console
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"aego/core/log"
|
|
)
|
|
|
|
type Entry struct {
|
|
Time float64
|
|
Level log.Level
|
|
Channel log.Channel
|
|
Text string
|
|
Path string
|
|
Line int
|
|
Count int
|
|
}
|
|
|
|
type Sink struct {
|
|
mu sync.Mutex
|
|
ring []Entry
|
|
start int
|
|
n int
|
|
rev uint64
|
|
counts [log.Fatal + 1]int
|
|
collect bool
|
|
}
|
|
|
|
func NewSink(capacity int) *Sink {
|
|
if capacity < 16 {
|
|
capacity = 16
|
|
}
|
|
return &Sink{ring: make([]Entry, capacity), collect: true}
|
|
}
|
|
|
|
func format(r log.Record) string {
|
|
if len(r.Fields) == 0 {
|
|
return r.Msg
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString(r.Msg)
|
|
for _, f := range r.Fields {
|
|
b.WriteByte(' ')
|
|
b.WriteString(f.Key)
|
|
b.WriteByte('=')
|
|
b.WriteString(fmt.Sprint(f.Val))
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func locOf(r log.Record) (string, int) {
|
|
p, line := "", 0
|
|
for _, f := range r.Fields {
|
|
switch f.Key {
|
|
case "path":
|
|
if s, ok := f.Val.(string); ok {
|
|
p = s
|
|
}
|
|
case "line":
|
|
if n, ok := f.Val.(int); ok {
|
|
line = n
|
|
}
|
|
}
|
|
}
|
|
return p, line
|
|
}
|
|
|
|
func (s *Sink) Write(r log.Record) {
|
|
text := format(r)
|
|
p, line := locOf(r)
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if int(r.Level) < len(s.counts) {
|
|
s.counts[r.Level]++
|
|
}
|
|
s.rev++
|
|
if s.n > 0 {
|
|
last := &s.ring[(s.start+s.n-1)%len(s.ring)]
|
|
if last.Level == r.Level && last.Channel == r.Channel && last.Text == text {
|
|
last.Count++
|
|
last.Time = r.Time
|
|
return
|
|
}
|
|
}
|
|
e := Entry{Time: r.Time, Level: r.Level, Channel: r.Channel, Text: text, Path: p, Line: line, Count: 1}
|
|
if s.n < len(s.ring) {
|
|
s.ring[(s.start+s.n)%len(s.ring)] = e
|
|
s.n++
|
|
return
|
|
}
|
|
s.ring[s.start] = e
|
|
s.start = (s.start + 1) % len(s.ring)
|
|
}
|
|
|
|
func (s *Sink) Revision() uint64 {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return s.rev
|
|
}
|
|
|
|
func (s *Sink) Counts() (info, warn, errors int) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return s.counts[log.Trace] + s.counts[log.Debug] + s.counts[log.Info], s.counts[log.Warn], s.counts[log.Error] + s.counts[log.Fatal]
|
|
}
|
|
|
|
func (s *Sink) Snapshot(dst []Entry) []Entry {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
dst = dst[:0]
|
|
for i := 0; i < s.n; i++ {
|
|
dst = append(dst, s.ring[(s.start+i)%len(s.ring)])
|
|
}
|
|
return dst
|
|
}
|
|
|
|
func (s *Sink) Clear() {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.start, s.n = 0, 0
|
|
s.counts = [log.Fatal + 1]int{}
|
|
s.rev++
|
|
}
|