Skip to content

Commit

Permalink
转义处理+性能优化
Browse files Browse the repository at this point in the history
  • Loading branch information
FishGoddess committed Dec 14, 2023
1 parent 91a7345 commit d1d2b0f
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 54 deletions.
2 changes: 1 addition & 1 deletion FUTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* [x] 增加属性解析器适配功能
* [x] 优化 handler 和 writer 设计
* [x] 增加一个可读性高的 handler 实现
* [ ] MixHandler 转义处理
* [x] MixHandler 转义处理
* [ ] 提高单元测试覆盖率到 70%
* [ ] 进一步提高单元测试覆盖率到 80%

Expand Down
3 changes: 2 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

### v1.5.4-alpha (Coming soon)

> 此版本发布于 2023-12-20
> 此版本发布于 2023-12-15
* 增加一个可读性高的 handler 实现
* 新的 mix handler 性能提升了 33%,且 print 类函数性能提升了 50%

### v1.5.3-alpha

Expand Down
13 changes: 7 additions & 6 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,19 @@ goos: linux
goarch: amd64
cpu: AMD EPYC 7K62 48-Core Processor

BenchmarkLogitLoggerTextHandler-2 1000000 1089 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 800017 1437 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 751623 1567 ns/op 48 B/op 1 allocs/op
BenchmarkLogitLogger-2 1465222 815 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerTextHandler-2 1000000 1097 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 803572 1414 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 1213560 999 ns/op 48 B/op 1 allocs/op
BenchmarkSlogLoggerTextHandler-2 725522 1629 ns/op 0 B/op 0 allocs/op
BenchmarkSlogLoggerJsonHandler-2 583214 2030 ns/op 120 B/op 3 allocs/op
BenchmarkZeroLogLogger-2 1929276 613 ns/op 0 B/op 0 allocs/op
BenchmarkZapLogger-2 976855 1168 ns/op 216 B/op 2 allocs/op
BenchmarkLogrusLogger-2 231723 4927 ns/op 2080 B/op 32 allocs/op

BenchmarkLogitFile-2 454489 2366 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBuffer-2 1038120 1154 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBatch-2 1026002 1179 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFile-2 580012 2011 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBuffer-2 1391960 865 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBatch-2 1331029 894 ns/op 0 B/op 0 allocs/op
BenchmarkSlogFile-2 407590 2944 ns/op 0 B/op 0 allocs/op
BenchmarkZeroLogFile-2 634375 1810 ns/op 0 B/op 0 allocs/op
BenchmarkZapFile-2 382790 2641 ns/op 216 B/op 2 allocs/op
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,19 @@ goos: linux
goarch: amd64
cpu: AMD EPYC 7K62 48-Core Processor

BenchmarkLogitLoggerTextHandler-2 1000000 1089 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 800017 1437 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 751623 1567 ns/op 48 B/op 1 allocs/op
BenchmarkLogitLogger-2 1465222 815 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerTextHandler-2 1000000 1097 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 803572 1414 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 1213560 999 ns/op 48 B/op 1 allocs/op
BenchmarkSlogLoggerTextHandler-2 725522 1629 ns/op 0 B/op 0 allocs/op
BenchmarkSlogLoggerJsonHandler-2 583214 2030 ns/op 120 B/op 3 allocs/op
BenchmarkZeroLogLogger-2 1929276 613 ns/op 0 B/op 0 allocs/op
BenchmarkZapLogger-2 976855 1168 ns/op 216 B/op 2 allocs/op
BenchmarkLogrusLogger-2 231723 4927 ns/op 2080 B/op 32 allocs/op

BenchmarkLogitFile-2 454489 2366 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBuffer-2 1038120 1154 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBatch-2 1026002 1179 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFile-2 580012 2011 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBuffer-2 1391960 865 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBatch-2 1331029 894 ns/op 0 B/op 0 allocs/op
BenchmarkSlogFile-2 407590 2944 ns/op 0 B/op 0 allocs/op
BenchmarkZeroLogFile-2 634375 1810 ns/op 0 B/op 0 allocs/op
BenchmarkZapFile-2 382790 2641 ns/op 216 B/op 2 allocs/op
Expand Down
1 change: 0 additions & 1 deletion _examples/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ func main() {

// More options can be found in logit package which have prefix "With".
// What's more? We provide a options pack that we think it's useful in production.
// It outputs logs to a rotate file using batch write, so you should call Sync() or Close() when shutdown.
opts := logit.ProductionOptions()

logger = logit.NewLogger(opts...)
Expand Down
14 changes: 7 additions & 7 deletions _examples/performance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ goos: linux
goarch: amd64
cpu: AMD EPYC 7K62 48-Core Processor
BenchmarkLogitLogger-2 1079007 984 ns/op 56 B/op 5 allocs/op
BenchmarkLogitLoggerTextHandler-2 1000000 1102 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 813913 1419 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 1000000 1038 ns/op 72 B/op 2 allocs/op
BenchmarkLogitLogger-2 1465222 815 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerTextHandler-2 1000000 1097 ns/op 0 B/op 0 allocs/op
BenchmarkLogitLoggerJsonHandler-2 803572 1414 ns/op 120 B/op 3 allocs/op
BenchmarkLogitLoggerPrint-2 1213560 999 ns/op 48 B/op 1 allocs/op
BenchmarkSlogLoggerTextHandler-2 725522 1629 ns/op 0 B/op 0 allocs/op
BenchmarkSlogLoggerJsonHandler-2 583214 2030 ns/op 120 B/op 3 allocs/op
BenchmarkZeroLogLogger-2 1929276 613 ns/op 0 B/op 0 allocs/op
BenchmarkZapLogger-2 976855 1168 ns/op 216 B/op 2 allocs/op
BenchmarkLogrusLogger-2 231723 4927 ns/op 2080 B/op 32 allocs/op
BenchmarkLogitFile-2 539060 2243 ns/op 56 B/op 5 allocs/op
BenchmarkLogitFileWithBuffer-2 1000000 1062 ns/op 56 B/op 5 allocs/op
BenchmarkLogitFileWithBatch-2 1000000 1088 ns/op 56 B/op 5 allocs/op
BenchmarkLogitFile-2 580012 2011 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBuffer-2 1391960 865 ns/op 0 B/op 0 allocs/op
BenchmarkLogitFileWithBatch-2 1331029 894 ns/op 0 B/op 0 allocs/op
BenchmarkSlogFile-2 407590 2944 ns/op 0 B/op 0 allocs/op
BenchmarkZeroLogFile-2 634375 1810 ns/op 0 B/op 0 allocs/op
BenchmarkZapFile-2 382790 2641 ns/op 216 B/op 2 allocs/op
Expand Down
18 changes: 11 additions & 7 deletions core/handler/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,22 @@ import (
var bufferPool = sync.Pool{
New: func() any {
bs := make([]byte, 0, defaults.MinBufferSize)
return &bs
return &buffer{bs: bs}
},
}

func newBuffer() *[]byte {
return bufferPool.Get().(*[]byte)
type buffer struct {
bs []byte
}

func freeBuffer(b *[]byte) {
func newBuffer() *buffer {
return bufferPool.Get().(*buffer)
}

func freeBuffer(buffer *buffer) {
// Return only smaller buffers for reducing peak allocation.
if cap(*b) <= defaults.MaxBufferSize {
*b = (*b)[:0]
bufferPool.Put(b)
if cap(buffer.bs) <= defaults.MaxBufferSize {
buffer.bs = buffer.bs[:0]
bufferPool.Put(buffer)
}
}
16 changes: 9 additions & 7 deletions core/handler/buffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,24 @@ import (
func TestBufferPool(t *testing.T) {
for i := 0; i < 100; i++ {
bs := make([]byte, 0, 2*defaults.MaxBufferSize)
freeBuffer(&bs)
buffer := &buffer{bs: bs}
freeBuffer(buffer)
}

for i := 0; i < 100; i++ {
buffer := newBuffer()
bs := buffer.bs

if len(*buffer) != 0 {
t.Fatalf("len %d of buffer is wrong", len(*buffer))
if len(bs) != 0 {
t.Fatalf("len %d of buffer is wrong", len(bs))
}

if cap(*buffer) < defaults.MinBufferSize {
t.Fatalf("cap %d of buffer too small", cap(*buffer))
if cap(bs) < defaults.MinBufferSize {
t.Fatalf("cap %d of buffer too small", cap(bs))
}

if cap(*buffer) > defaults.MaxBufferSize {
t.Fatalf("cap %d of buffer too large", cap(*buffer))
if cap(bs) > defaults.MaxBufferSize {
t.Fatalf("cap %d of buffer too large", cap(bs))
}
}
}
75 changes: 60 additions & 15 deletions core/handler/mix.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,48 @@ func (mh *mixHandler) appendKey(bs []byte, key string) []byte {
return bs
}

func (mh *mixHandler) appendBool(bs []byte, value bool) []byte {
bs = strconv.AppendBool(bs, value)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendInt64(bs []byte, value int64) []byte {
bs = strconv.AppendInt(bs, value, 10)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendUint64(bs []byte, value uint64) []byte {
bs = strconv.AppendUint(bs, value, 10)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendFloat64(bs []byte, value float64) []byte {
bs = strconv.AppendFloat(bs, value, 'f', -1, 64)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendString(bs []byte, value string) []byte {
bs = appendEscapedString(bs, value)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendDuration(bs []byte, value time.Duration) []byte {
bs = append(bs, value.String()...)
bs = append(bs, attrSeparator...)

return bs
}

func (mh *mixHandler) appendTime(bs []byte, value time.Time) []byte {
// Time format is an usual but expensive operation if using time.AppendFormat,
// so we use a stupid but faster way to format time.
Expand Down Expand Up @@ -232,12 +267,22 @@ func (mh *mixHandler) appendAttr(bs []byte, attr slog.Attr) []byte {
bs = mh.appendKey(bs, attr.Key)

switch attr.Value.Kind() {
case slog.KindGroup:
bs = mh.appendGroupAttrs(bs, attr.Key, attr.Value.Group())
case slog.KindBool:
bs = mh.appendBool(bs, attr.Value.Bool())
case slog.KindInt64:
bs = mh.appendInt64(bs, attr.Value.Int64())
case slog.KindUint64:
bs = mh.appendUint64(bs, attr.Value.Uint64())
case slog.KindFloat64:
bs = mh.appendFloat64(bs, attr.Value.Float64())
case slog.KindDuration:
bs = mh.appendDuration(bs, attr.Value.Duration())
case slog.KindTime:
bs = mh.appendTime(bs, attr.Value.Time())
case slog.KindAny:
bs = mh.appendAny(bs, attr.Value.Any())
case slog.KindGroup:
bs = mh.appendGroupAttrs(bs, attr.Key, attr.Value.Group())
default:
bs = mh.appendString(bs, attr.Value.String())
}
Expand Down Expand Up @@ -278,35 +323,35 @@ func (mh *mixHandler) appendSource(bs []byte, pc uintptr) []byte {
// Handle handles one record and returns an error if failed.
func (mh *mixHandler) Handle(ctx context.Context, record slog.Record) error {
// Setup a buffer for handling record.
bufferPtr := newBuffer()
buffer := *bufferPtr
buffer := newBuffer()
bs := buffer.bs

defer func() {
bufferPtr = &buffer
freeBuffer(bufferPtr)
buffer.bs = bs
freeBuffer(buffer)
}()

// Handling record.
buffer = mh.appendTime(buffer, record.Time)
buffer = mh.appendString(buffer, record.Level.String())
buffer = mh.appendString(buffer, record.Message)
buffer = mh.appendSource(buffer, record.PC)
bs = mh.appendTime(bs, record.Time)
bs = mh.appendString(bs, record.Level.String())
bs = mh.appendString(bs, record.Message)
bs = mh.appendSource(bs, record.PC)

buffer = append(buffer, mh.attrsBytes...)
bs = append(bs, mh.attrsBytes...)
if record.NumAttrs() > 0 {
record.Attrs(func(attr slog.Attr) bool {
buffer = mh.appendAttr(buffer, attr)
bs = mh.appendAttr(bs, attr)
return true
})
}

buffer = bytes.TrimSuffix(buffer, attrSeparator)
buffer = append(buffer, lineBreak)
bs = bytes.TrimSuffix(bs, attrSeparator)
bs = append(bs, lineBreak)

// Write handled record.
mh.lock.Lock()
defer mh.lock.Unlock()

_, err := mh.w.Write(buffer)
_, err := mh.w.Write(bs)
return err
}
1 change: 0 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ Package logit provides an easy way to use foundation for your logging operations
// More options can be found in logit package which have prefix "With".
// What's more? We provide a options pack that we think it's useful in production.
// It outputs logs to a rotate file using batch write, so you should call Sync() or Close() when shutdown.
opts := logit.ProductionOptions()
logger = logit.NewLogger(opts...)
Expand Down
2 changes: 0 additions & 2 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,9 @@ func WithSyncTimer(d time.Duration) Option {

// ProductionOptions returns some options that we think they are useful in production.
// We recommend you to use them, so we provide this convenient way to create such a logger.
// The logger using these options will use rotate file and batch writer.
func ProductionOptions() []Option {
opts := []Option{
WithInfoLevel(), WithPID(), WithRotateFile("./logit.log"),
WithBatch(16), WithSyncTimer(time.Second),
}

return opts
Expand Down

0 comments on commit d1d2b0f

Please sign in to comment.