Skip to content

Commit

Permalink
refactor!: 不再允许 Handler 为空
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Nov 10, 2023
1 parent 6862586 commit a31cedf
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 86 deletions.
12 changes: 0 additions & 12 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,6 @@ func BenchmarkLogs_disableRecorder(b *testing.B) {
}
}

func BenchmarkLogs_nop(b *testing.B) {
a := assert.New(b, false)
l := New(nil)
a.NotNil(l)
l.Enable(LevelError)

err := l.ERROR()
for i := 0; i < b.N; i++ {
err.With("k1", "v1").Printf("p1")
}
}

func BenchmarkLogger_LogLogger(b *testing.B) {
a := assert.New(b, false)
buf := new(bytes.Buffer)
Expand Down
11 changes: 0 additions & 11 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import (
"github.com/issue9/logs/v7/writers"
)

var nop = &nopHandler{}

var defaultTermColors = map[Level]colors.Color{
LevelInfo: colors.Green,
LevelDebug: colors.Yellow,
Expand Down Expand Up @@ -76,8 +74,6 @@ type (
mergeHandler struct {
handlers []Handler
}

nopHandler struct{}
)

// NewTextHandler 返回将 [Record] 以普通文本的形式写入 w 的对象
Expand Down Expand Up @@ -397,10 +393,3 @@ func (h *mergeHandler) New(detail bool, lv Level, attrs []Attr) Handler {
}
return MergeHandler(slices...)
}

// NewNopHandler 空的 [Handler] 接口实现
func NewNopHandler() Handler { return nop }

func (h *nopHandler) Handle(*Record) {}

func (h *nopHandler) New(bool, Level, []Attr) Handler { return h }
43 changes: 14 additions & 29 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,9 @@ func TestTextHandler(t *testing.T) {
layout := MilliLayout
now := time.Now()

l := New(nil, WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))

buf := new(bytes.Buffer)
l = New(NewTextHandler(buf), WithCreated(layout), WithLocation(true))
e = newRecord(a, l)
l := New(NewTextHandler(buf), WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))
e.Output(l.WARN())
Expand All @@ -82,14 +77,14 @@ func TestTextHandler(t *testing.T) {
h := NewTextHandler(buf)
l = New(h, WithLocation(true), WithDetail(true))
e = newRecord(a, l)
h = h.New(l.Detail(), LevelWarn, []Attr{{K: "attr1", V: 3.51}})
h = h.New(l.detail, LevelWarn, []Attr{{K: "attr1", V: 3.51}})
h.Handle(e)
a.Equal(buf.String(), "[WARN] path.go:20\tmsg attr1=3.51 k1=v1 k2=v2\n")

// Handler.New().New()
buf.Reset()
e = newRecord(a, l)
h.New(l.Detail(), LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
h.New(l.detail, LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
a.Equal(buf.String(), "[WARN] path.go:20\tmsg attr1=3.51 a1=5 a2=8 k1=v1 k2=v2\n")
}

Expand All @@ -98,16 +93,11 @@ func TestJSONFormat(t *testing.T) {
layout := MilliLayout
now := time.Now()

l := New(nil, WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))

a.Panic(func() { NewJSONHandler() })

buf := new(bytes.Buffer)
l = New(NewJSONHandler(buf), WithCreated(layout), WithLocation(true))
e = newRecord(a, l)
l := New(NewJSONHandler(buf), WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))
e.Output(l.WARN())
Expand Down Expand Up @@ -139,14 +129,14 @@ func TestJSONFormat(t *testing.T) {
h := NewJSONHandler(buf)
l = New(h, WithLocation(true), WithDetail(true))
e = newRecord(a, l)
h = h.New(l.Detail(), LevelWarn, []Attr{{K: "attr1", V: 3.5}})
h = h.New(l.detail, LevelWarn, []Attr{{K: "attr1", V: 3.5}})
h.Handle(e)
a.Equal(buf.String(), `{"level":"WARN","message":"msg","path":"path.go:20","attrs":[{"attr1":3.5},{"k1":"v1"},{"k2":"v2"}]}`)

// Handler.New().New()
buf.Reset()
e = newRecord(a, l)
h.New(l.Detail(), LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
h.New(l.detail, LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
a.Equal(buf.String(), `{"level":"WARN","message":"msg","path":"path.go:20","attrs":[{"attr1":3.5},{"a1":5},{"a2":8},{"k1":"v1"},{"k2":"v2"}]}`)
}

Expand All @@ -158,14 +148,9 @@ func TestTermHandler(t *testing.T) {
layout := MilliLayout
now := time.Now()

l := New(nil, WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))

buf := new(bytes.Buffer)
l = New(NewTermHandler(buf, nil), WithCreated(layout), WithLocation(true))
e = newRecord(a, l)
l := New(NewTermHandler(buf, nil), WithCreated(layout), WithLocation(true))
e := newRecord(a, l)
e.AppendCreated = func(b *Buffer) { b.AppendTime(now, l.createdFormat) }
e.with("m1", marshalObject("m1"))
e.Output(l.WARN())
Expand All @@ -177,14 +162,14 @@ func TestTermHandler(t *testing.T) {
h := NewTermHandler(buf, nil)
l = New(h, WithLocation(true), WithDetail(true))
e = newRecord(a, l)
h = h.New(l.Detail(), LevelWarn, []Attr{{K: "attr1", V: 3.51}})
h = h.New(l.detail, LevelWarn, []Attr{{K: "attr1", V: 3.51}})
h.Handle(e)
a.Equal(buf.String(), "[\033[33;49mWARN\033[0m] path.go:20\tmsg attr1=3.51 k1=v1 k2=v2\n")

// Handler.New().New()
buf.Reset()
e = newRecord(a, l)
h.New(l.Detail(), LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
h.New(l.detail, LevelWarn, []Attr{{K: "a1", V: int8(5)}, {K: "a2", V: uint(8)}}).Handle(e)
a.Equal(buf.String(), "[\033[33;49mWARN\033[0m] path.go:20\tmsg attr1=3.51 a1=5 a2=8 k1=v1 k2=v2\n")
}

Expand Down Expand Up @@ -238,7 +223,7 @@ func TestMergeHandler(t *testing.T) {
textBuf.Reset()
jsonBuf.Reset()

w = w.New(l.Detail(), LevelWarn, []Attr{{K: "a1", V: "v1"}})
w = w.New(l.detail, LevelWarn, []Attr{{K: "a1", V: "v1"}})
l = New(w)
l.WARN().Printf("warnf test")

Expand All @@ -251,7 +236,7 @@ func TestMergeHandler(t *testing.T) {
textBuf.Reset()
jsonBuf.Reset()

w = w.New(l.Detail(), LevelWarn, []Attr{{K: "a2", V: uint8(3)}})
w = w.New(l.detail, LevelWarn, []Attr{{K: "a2", V: uint8(3)}})
l = New(w)
l.WARN().Printf("warnf test")

Expand Down
4 changes: 4 additions & 0 deletions level.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ var levelStrings = map[Level]string{
LevelFatal: "FATL",
}

func AllLevels() []Level {
return []Level{LevelInfo, LevelWarn, LevelTrace, LevelFatal, LevelError, LevelDebug}
}

func IsValidLevel(l Level) bool { return l >= LevelInfo && l <= LevelFatal }

func (l Level) String() string { return levelStrings[l] }
Expand Down
2 changes: 1 addition & 1 deletion logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (l *Logger) New(attrs map[string]any) *Logger {
return &Logger{
lv: l.lv,
logs: l.logs,
h: l.h.New(l.logs.Detail(), l.Level(), map2Slice(l.logs.printer, attrs)),
h: l.h.New(l.logs.detail, l.Level(), map2Slice(l.logs.printer, attrs)),
}
}

Expand Down
41 changes: 26 additions & 15 deletions logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ import (
)

type Logs struct {
handler Handler
loggers map[Level]*Logger
enables map[Level]bool

attrs map[string]any // 仅用于被 Option 函数存取,没有其它用处。
location, detail bool
createdFormat string
printer *localeutil.Printer
levels []Level
attrs map[string]any
location bool
detail bool
createdFormat string
printer *localeutil.Printer
}

// Marshaler 定义了序列化日志属性的方法
//
// [Recorder.With] 的 val 如果实现了该接口,
// 那么在传递进去之后会调用该接口转换成字符串之后保存。
type Marshaler interface {
MarshalLog() string
}
Expand Down Expand Up @@ -52,15 +56,13 @@ func map2Slice(p *localeutil.Printer, attrs map[string]any) []Attr {
// h 如果为 nil,则表示采用 [NewNopHandler]。
func New(h Handler, o ...Option) *Logs {
if h == nil {
h = NewNopHandler()
panic("参数 h 不能为空")
}

l := &Logs{
handler: h,
levels: AllLevels(),
attrs: make(map[string]any, 10),
loggers: make(map[Level]*Logger, len(levelStrings)),
enables: make(map[Level]bool, len(levelStrings)),

attrs: make(map[string]any, 10),
}
for _, opt := range o {
opt(l)
Expand All @@ -73,7 +75,6 @@ func New(h Handler, o ...Option) *Logs {
lv: lv,
h: h.New(l.detail, lv, attrs),
}
l.enables[lv] = true
}

return l
Expand All @@ -83,15 +84,25 @@ func New(h Handler, o ...Option) *Logs {
//
// 调用此函数之后,所有不在 level 参数的通道都将被关闭。
func (logs *Logs) Enable(level ...Level) {
for lv := range logs.enables {
logs.enables[lv] = sliceutil.Exists(level, func(ll Level, _ int) bool { return ll == lv })
// TODO(go1.21): 采用 slices.Clone 代替
ls := make([]Level, 0, len(level))
logs.levels = append(ls, level...)
}

// AppendAttrs 为所有的 [Logger] 对象添加属性
func (logs *Logs) AppendAttrs(attrs map[string]any) {
for _, l := range logs.loggers {
l.AppendAttrs(attrs)
}
}

// IsEnable 指定级别日志是否会真实被启用
//
// 如果设置了 [Handler] 为空值或是未在 [Logs.Enable] 中指定都将返回 false。
func (logs *Logs) IsEnable(l Level) bool { return logs.enables[l] && logs.handler != nop }
func (logs *Logs) IsEnable(l Level) bool {
// TODO(go1.21): 采用 slices.Index 代替
return sliceutil.Exists(logs.levels, func(v Level, _ int) bool { return v == l })
}

func (logs *Logs) INFO() *Logger { return logs.Logger(LevelInfo) }

Expand Down
5 changes: 0 additions & 5 deletions logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ func TestLogs_IsEnable(t *testing.T) {
True(l.IsEnable(LevelWarn)).
True(l.IsEnable(LevelError))

// WARN 属于 enable,但是 logs.w 为 nop
l = New(nil)
l.Enable(LevelWarn, LevelError)
a.False(l.WARN().IsEnable())

buf := new(bytes.Buffer)
l = New(NewTextHandler(buf))
a.NotNil(l)
Expand Down
5 changes: 3 additions & 2 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const (

type Option func(*Logs)

// WithLevels 指定启用的日志通道
func WithLevels(lv ...Level) Option { return func(l *Logs) { l.levels = lv } }

// WithLocale 指定本地化信息
//
// 如果为 nil,那么将禁用本地化输出,如果多次调用,则以最后一次为准。
Expand Down Expand Up @@ -79,5 +82,3 @@ func (logs *Logs) SetLocation(v bool) { logs.location = v }
//
// 如果 v 为空将会禁用日期显示。
func (logs *Logs) SetCreated(v string) { logs.createdFormat = v }

func (logs *Logs) Detail() bool { return logs.detail }
7 changes: 4 additions & 3 deletions record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package logs
import (
"bytes"
"errors"
"io"
"strings"
"testing"

Expand Down Expand Up @@ -38,7 +39,7 @@ func (e *err) FormatError(p xerrors.Printer) error {

func TestRecord_location(t *testing.T) {
a := assert.New(t, false)
l := New(nil, WithLocation(true), WithCreated(MicroLayout))
l := New(NewTextHandler(io.Discard), WithLocation(true), WithCreated(MicroLayout))

e := l.NewRecord()
a.NotNil(e)
Expand All @@ -48,7 +49,7 @@ func TestRecord_location(t *testing.T) {
b := NewBuffer(false)
defer b.Free()
e.AppendLocation(b)
a.True(strings.HasSuffix(string(b.data), "record_test.go:47"), string(b.data))
a.True(strings.HasSuffix(string(b.data), "record_test.go:48"), string(b.data))
}

func TestRecord_Error(t *testing.T) {
Expand All @@ -60,7 +61,7 @@ func TestRecord_Error(t *testing.T) {
l := New(NewTextHandler(buf), WithLocation(true), WithCreated(MicroLayout))
a.NotNil(l)
l.WARN().Error(err1) // 输出定位
a.True(strings.Contains(buf.String(), "record_test.go:62"), buf.String())
a.True(strings.Contains(buf.String(), "record_test.go:63"), buf.String())
})

err2 := &err{err: err1}
Expand Down
16 changes: 8 additions & 8 deletions slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var slog2Logs = map[slog.Level]Level{
slog.LevelError: LevelError,
}

type logsHandler struct {
type slogHandler struct {
l *Logs
attrs []slog.Attr
prefix string // groups 组成
Expand All @@ -27,16 +27,16 @@ type logsHandler struct {
// SLogHandler 将 logs 转换为 [slog.Handler] 接口
//
// 所有的 group 会作为普通 attr 的名称前缀,但是不影响 Level、Message 等字段。
func (l *Logs) SLogHandler() slog.Handler { return &logsHandler{l: l} }
func (l *Logs) SLogHandler() slog.Handler { return &slogHandler{l: l} }

// SLog 将 Logs 作为 [slog.Logger] 的后端
func (l *Logs) SLog() *slog.Logger { return slog.New(l.SLogHandler()) }

func (h *logsHandler) Enabled(ctx context.Context, lv slog.Level) bool {
func (h *slogHandler) Enabled(ctx context.Context, lv slog.Level) bool {
return h.l.IsEnable(slog2Logs[lv])
}

func (h *logsHandler) Handle(ctx context.Context, r slog.Record) error {
func (h *slogHandler) Handle(ctx context.Context, r slog.Record) error {
rr := h.l.NewRecord()
rr.AppendCreated = func(b *Buffer) { b.AppendTime(r.Time, h.l.createdFormat) }
rr.AppendMessage = func(b *Buffer) { b.AppendString(r.Message) }
Expand All @@ -61,27 +61,27 @@ func (h *logsHandler) Handle(ctx context.Context, r slog.Record) error {
return nil
}

func (h *logsHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
func (h *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
var a []slog.Attr
if len(h.attrs) == 0 {
a = attrs
} else {
a = append(slices.Clip(h.attrs), attrs...)
}

return &logsHandler{
return &slogHandler{
l: h.l,
attrs: a,
prefix: h.prefix,
}
}

func (h *logsHandler) WithGroup(name string) slog.Handler {
func (h *slogHandler) WithGroup(name string) slog.Handler {
if name == "" {
return h
}

return &logsHandler{
return &slogHandler{
l: h.l,
attrs: h.attrs,
prefix: name + "." + h.prefix,
Expand Down

0 comments on commit a31cedf

Please sign in to comment.