2018-09-30 23:51:02 +03:00
|
|
|
package logrus
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-08-11 05:48:35 +03:00
|
|
|
"context"
|
2018-09-30 23:51:02 +03:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2021-08-11 05:48:35 +03:00
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
2018-09-30 23:51:02 +03:00
|
|
|
"testing"
|
2021-08-11 05:48:35 +03:00
|
|
|
"time"
|
2018-09-30 23:51:02 +03:00
|
|
|
|
2019-01-22 15:08:19 +03:00
|
|
|
"github.com/stretchr/testify/assert"
|
2018-09-30 23:51:02 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestFieldValueError(t *testing.T) {
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
l := &Logger{
|
|
|
|
Out: buf,
|
|
|
|
Formatter: new(JSONFormatter),
|
|
|
|
Hooks: make(LevelHooks),
|
|
|
|
Level: DebugLevel,
|
|
|
|
}
|
|
|
|
l.WithField("func", func() {}).Info("test")
|
|
|
|
fmt.Println(buf.String())
|
|
|
|
var data map[string]interface{}
|
2019-10-14 03:45:01 +03:00
|
|
|
if err := json.Unmarshal(buf.Bytes(), &data); err != nil {
|
|
|
|
t.Error("unexpected error", err)
|
|
|
|
}
|
2018-09-30 23:51:02 +03:00
|
|
|
_, ok := data[FieldKeyLogrusError]
|
2021-02-16 12:29:37 +03:00
|
|
|
require.True(t, ok, `cannot found expected "logrus_error" field: %v`, data)
|
2018-09-30 23:51:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoFieldValueError(t *testing.T) {
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
l := &Logger{
|
|
|
|
Out: buf,
|
|
|
|
Formatter: new(JSONFormatter),
|
|
|
|
Hooks: make(LevelHooks),
|
|
|
|
Level: DebugLevel,
|
|
|
|
}
|
|
|
|
l.WithField("str", "str").Info("test")
|
|
|
|
fmt.Println(buf.String())
|
|
|
|
var data map[string]interface{}
|
2019-10-14 03:45:01 +03:00
|
|
|
if err := json.Unmarshal(buf.Bytes(), &data); err != nil {
|
|
|
|
t.Error("unexpected error", err)
|
|
|
|
}
|
2018-09-30 23:51:02 +03:00
|
|
|
_, ok := data[FieldKeyLogrusError]
|
|
|
|
require.False(t, ok)
|
|
|
|
}
|
2019-01-22 15:08:19 +03:00
|
|
|
|
|
|
|
func TestWarninglnNotEqualToWarning(t *testing.T) {
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
bufln := &bytes.Buffer{}
|
|
|
|
|
|
|
|
formatter := new(TextFormatter)
|
|
|
|
formatter.DisableTimestamp = true
|
|
|
|
formatter.DisableLevelTruncation = true
|
|
|
|
|
|
|
|
l := &Logger{
|
|
|
|
Out: buf,
|
|
|
|
Formatter: formatter,
|
|
|
|
Hooks: make(LevelHooks),
|
|
|
|
Level: DebugLevel,
|
|
|
|
}
|
|
|
|
l.Warning("hello,", "world")
|
|
|
|
|
|
|
|
l.SetOutput(bufln)
|
|
|
|
l.Warningln("hello,", "world")
|
|
|
|
|
|
|
|
assert.NotEqual(t, buf.String(), bufln.String(), "Warning() and Wantingln() should not be equal")
|
|
|
|
}
|
2021-04-20 05:48:30 +03:00
|
|
|
|
|
|
|
type testBufferPool struct {
|
|
|
|
buffers []*bytes.Buffer
|
2021-08-11 05:48:35 +03:00
|
|
|
get int
|
2021-04-20 05:48:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *testBufferPool) Get() *bytes.Buffer {
|
|
|
|
p.get++
|
|
|
|
return new(bytes.Buffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *testBufferPool) Put(buf *bytes.Buffer) {
|
|
|
|
p.buffers = append(p.buffers, buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLogger_SetBufferPool(t *testing.T) {
|
|
|
|
out := &bytes.Buffer{}
|
|
|
|
l := New()
|
|
|
|
l.SetOutput(out)
|
|
|
|
|
|
|
|
pool := new(testBufferPool)
|
|
|
|
l.SetBufferPool(pool)
|
|
|
|
|
|
|
|
l.Info("test")
|
|
|
|
|
|
|
|
assert.Equal(t, pool.get, 1, "Logger.SetBufferPool(): The BufferPool.Get() must be called")
|
|
|
|
assert.Len(t, pool.buffers, 1, "Logger.SetBufferPool(): The BufferPool.Put() must be called")
|
|
|
|
}
|
2021-08-11 05:48:35 +03:00
|
|
|
|
|
|
|
func TestLogger_concurrentLock(t *testing.T) {
|
|
|
|
SetFormatter(&LogFormatter{})
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
func() {
|
|
|
|
defer func() {
|
|
|
|
if p := recover(); p != nil {
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
hook := AddTraceIdHook("123")
|
|
|
|
defer RemoveTraceHook(hook)
|
|
|
|
Infof("test why ")
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
func() {
|
|
|
|
defer func() {
|
|
|
|
if p := recover(); p != nil {
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
hook := AddTraceIdHook("1233")
|
|
|
|
defer RemoveTraceHook(hook)
|
|
|
|
Infof("test why 2")
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}()
|
2021-08-11 05:54:10 +03:00
|
|
|
time.Sleep(1 * time.Minute)
|
2021-08-11 05:48:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var traceLock = &sync.Mutex{}
|
|
|
|
|
|
|
|
func AddTraceIdHook(traceId string) Hook {
|
|
|
|
defer traceLock.Unlock()
|
|
|
|
traceLock.Lock()
|
|
|
|
traceHook := newTraceIdHook(traceId)
|
|
|
|
if StandardLogger().Hooks == nil {
|
|
|
|
hooks := new(LevelHooks)
|
|
|
|
StandardLogger().ReplaceHooks(*hooks)
|
|
|
|
}
|
|
|
|
AddHook(traceHook)
|
|
|
|
return traceHook
|
|
|
|
}
|
|
|
|
|
|
|
|
func RemoveTraceHook(hook Hook) {
|
|
|
|
allHooks := StandardLogger().Hooks
|
|
|
|
func() {
|
|
|
|
defer Unlock()
|
|
|
|
Lock()
|
|
|
|
for key, hooks := range allHooks {
|
|
|
|
replaceHooks := hooks
|
|
|
|
for index, h := range hooks {
|
|
|
|
if h == hook {
|
|
|
|
replaceHooks = append(hooks[:index], hooks[index:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
allHooks[key] = replaceHooks
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
StandardLogger().ReplaceHooks(allHooks)
|
|
|
|
}
|
|
|
|
|
|
|
|
type TraceIdHook struct {
|
|
|
|
TraceId string
|
|
|
|
GID uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTraceIdHook(traceId string) Hook {
|
|
|
|
return &TraceIdHook{
|
|
|
|
TraceId: traceId,
|
|
|
|
GID: getGID(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t TraceIdHook) Levels() []Level {
|
|
|
|
return AllLevels
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t TraceIdHook) Fire(entry *Entry) error {
|
|
|
|
if getGID() == t.GID {
|
|
|
|
entry.Context = context.WithValue(context.Background(), "trace_id", t.TraceId)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type LogFormatter struct{}
|
|
|
|
|
2021-09-01 16:34:26 +03:00
|
|
|
|
2021-08-11 05:48:35 +03:00
|
|
|
func (s *LogFormatter) Format(entry *Entry) ([]byte, error) {
|
|
|
|
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
|
|
|
var file string
|
|
|
|
var line int
|
|
|
|
if entry.Caller != nil {
|
|
|
|
file = filepath.Base(entry.Caller.File)
|
|
|
|
line = entry.Caller.Line
|
|
|
|
}
|
|
|
|
level := entry.Level.String()
|
|
|
|
if entry.Context == nil || entry.Context.Value("trace_id") == "" {
|
|
|
|
uuid := "NO UUID"
|
|
|
|
entry.Context = context.WithValue(context.Background(), "trace_id", uuid)
|
|
|
|
}
|
|
|
|
msg := fmt.Sprintf("%-15s [%-3d] [%-5s] [%s] %s:%d %s\n", timestamp, getGID(), level, entry.Context.Value("trace_id"), file, line, entry.Message)
|
|
|
|
return []byte(msg), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getGID() uint64 {
|
|
|
|
b := make([]byte, 64)
|
|
|
|
b = b[:runtime.Stack(b, false)]
|
|
|
|
b = bytes.TrimPrefix(b, []byte("goroutine "))
|
|
|
|
b = b[:bytes.IndexByte(b, ' ')]
|
|
|
|
n, _ := strconv.ParseUint(string(b), 10, 64)
|
|
|
|
return n
|
|
|
|
}
|