forked from TrueCloudLab/rclone
vendor: update all dependencies to latest versions
This commit is contained in:
parent
8e83fb6fb9
commit
7d3a17725d
4878 changed files with 1974229 additions and 201215 deletions
460
vendor/github.com/pengsrc/go-shared/log/event.go
generated
vendored
Normal file
460
vendor/github.com/pengsrc/go-shared/log/event.go
generated
vendored
Normal file
|
@ -0,0 +1,460 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
// A EventCallerPool is a type-safe wrapper around a sync.BytesBufferPool.
|
||||
type EventCallerPool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewEventCallerPool constructs a new BytesBufferPool.
|
||||
func NewEventCallerPool() EventCallerPool {
|
||||
return EventCallerPool{
|
||||
p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &EventCaller{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a EventCaller from the pool, creating one if necessary.
|
||||
func (p EventCallerPool) Get() *EventCaller {
|
||||
e := p.p.Get().(*EventCaller)
|
||||
|
||||
e.pool = p
|
||||
|
||||
e.Defined = false
|
||||
e.PC = 0
|
||||
e.File = ""
|
||||
e.Line = 0
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (p EventCallerPool) put(caller *EventCaller) {
|
||||
p.p.Put(caller)
|
||||
}
|
||||
|
||||
// EventCaller represents the caller of a logging function.
|
||||
type EventCaller struct {
|
||||
pool EventCallerPool
|
||||
|
||||
Defined bool
|
||||
PC uintptr
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
// Free returns the EventCaller to its EventCallerPool.
|
||||
// Callers must not retain references to the EventCaller after calling Free.
|
||||
func (ec *EventCaller) Free() {
|
||||
ec.pool.put(ec)
|
||||
}
|
||||
|
||||
// String returns the full path and line number of the caller.
|
||||
func (ec EventCaller) String() string {
|
||||
return ec.FullPath()
|
||||
}
|
||||
|
||||
// FullPath returns a /full/path/to/package/file:line description of the
|
||||
// caller.
|
||||
func (ec EventCaller) FullPath() string {
|
||||
if !ec.Defined {
|
||||
return "undefined"
|
||||
}
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
buf.AppendString(ec.File)
|
||||
buf.AppendByte(':')
|
||||
buf.AppendInt(int64(ec.Line))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// TrimmedPath returns a package/file:line description of the caller,
|
||||
// preserving only the leaf directory name and file name.
|
||||
func (ec EventCaller) TrimmedPath() string {
|
||||
if !ec.Defined {
|
||||
return "undefined"
|
||||
}
|
||||
// nb. To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
//
|
||||
// Find the last separator.
|
||||
//
|
||||
idx := strings.LastIndexByte(ec.File, '/')
|
||||
if idx == -1 {
|
||||
return ec.FullPath()
|
||||
}
|
||||
// Find the penultimate separator.
|
||||
idx = strings.LastIndexByte(ec.File[:idx], '/')
|
||||
if idx == -1 {
|
||||
return ec.FullPath()
|
||||
}
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
// Keep everything after the penultimate separator.
|
||||
buf.AppendString(ec.File[idx+1:])
|
||||
buf.AppendByte(':')
|
||||
buf.AppendInt(int64(ec.Line))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// A EventPool is a type-safe wrapper around a sync.BytesBufferPool.
|
||||
type EventPool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewEventPool constructs a new BytesBufferPool.
|
||||
func NewEventPool() EventPool {
|
||||
return EventPool{
|
||||
p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Event{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a Event from the pool, creating one if necessary.
|
||||
func (p EventPool) Get() *Event {
|
||||
e := p.p.Get().(*Event)
|
||||
|
||||
e.buffer = buffer.GlobalBytesPool().Get()
|
||||
e.pool = p
|
||||
|
||||
e.level = MuteLevel
|
||||
e.lw = nil
|
||||
|
||||
e.ctx = nil
|
||||
e.ctxKeys = nil
|
||||
|
||||
e.isEnabled = false
|
||||
e.isCallerEnabled = false
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (p EventPool) put(event *Event) {
|
||||
event.buffer.Free()
|
||||
|
||||
event.ctx = nil
|
||||
event.ctxKeys = nil
|
||||
|
||||
p.p.Put(event)
|
||||
}
|
||||
|
||||
// Event represents a log event. It is instanced by one of the with method of
|
||||
// logger and finalized by the log method such as Debug().
|
||||
type Event struct {
|
||||
buffer *buffer.BytesBuffer
|
||||
pool EventPool
|
||||
|
||||
level Level
|
||||
lw LevelWriter
|
||||
|
||||
ctx context.Context
|
||||
ctxKeys *[]interface{}
|
||||
ctxKeysMap *map[interface{}]string
|
||||
|
||||
isEnabled bool
|
||||
isCallerEnabled bool
|
||||
}
|
||||
|
||||
// Free returns the Event to its EventPool.
|
||||
// Callers must not retain references to the Event after calling Free.
|
||||
func (e *Event) Free() {
|
||||
e.pool.put(e)
|
||||
}
|
||||
|
||||
// Message writes the *Event to level writer.
|
||||
//
|
||||
// NOTICE: Once this method is called, the *Event should be disposed.
|
||||
// Calling twice can have unexpected result.
|
||||
func (e *Event) Message(message string) {
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
e.write(message)
|
||||
}
|
||||
|
||||
// Messagef writes the *Event to level writer.
|
||||
//
|
||||
// NOTICE: Once this method is called, the *Event should be disposed.
|
||||
// Calling twice can have unexpected result.
|
||||
func (e *Event) Messagef(format string, v ...interface{}) {
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
e.write(format, v...)
|
||||
}
|
||||
|
||||
// Byte appends string key and byte value to event.
|
||||
func (e *Event) Byte(key string, value byte) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendByte(value) })
|
||||
}
|
||||
|
||||
// Bytes appends string key and bytes value to event.
|
||||
func (e *Event) Bytes(key string, value []byte) *Event {
|
||||
return e.appendField(key, func() {
|
||||
if needsQuote(string(value)) {
|
||||
e.buffer.AppendString(strconv.Quote(string(value)))
|
||||
} else {
|
||||
e.buffer.AppendBytes(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// String appends string key and string value to event.
|
||||
func (e *Event) String(key string, value string) *Event {
|
||||
return e.appendField(key, func() {
|
||||
if needsQuote(string(value)) {
|
||||
e.buffer.AppendString(strconv.Quote(value))
|
||||
} else {
|
||||
e.buffer.AppendString(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Int appends string key and int value to event.
|
||||
func (e *Event) Int(key string, value int) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
|
||||
}
|
||||
|
||||
// Int32 appends string key and int32 value to event.
|
||||
func (e *Event) Int32(key string, value int32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
|
||||
}
|
||||
|
||||
// Int64 appends string key and int64 value to event.
|
||||
func (e *Event) Int64(key string, value int64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(value) })
|
||||
}
|
||||
|
||||
// Uint appends string key and uint value to event.
|
||||
func (e *Event) Uint(key string, value uint) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
|
||||
}
|
||||
|
||||
// Uint32 appends string key and uint32 value to event.
|
||||
func (e *Event) Uint32(key string, value uint32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
|
||||
}
|
||||
|
||||
// Uint64 appends string key and uint64 value to event.
|
||||
func (e *Event) Uint64(key string, value uint64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(value) })
|
||||
}
|
||||
|
||||
// Float32 appends string key and float32 value to event.
|
||||
func (e *Event) Float32(key string, value float32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendFloat(float64(value), 32) })
|
||||
}
|
||||
|
||||
// Float64 appends string key and float value to event.
|
||||
func (e *Event) Float64(key string, value float64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendFloat(value, 64) })
|
||||
}
|
||||
|
||||
// Bool appends string key and bool value to event.
|
||||
func (e *Event) Bool(key string, value bool) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendBool(value) })
|
||||
}
|
||||
|
||||
// Time appends string key and time value to event.
|
||||
func (e *Event) Time(key string, value time.Time, format string) *Event {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
buf.AppendTime(value, format)
|
||||
return e.Bytes(key, buf.Bytes())
|
||||
}
|
||||
|
||||
// Error appends string key and error value to event.
|
||||
func (e *Event) Error(key string, err error) *Event {
|
||||
return e.String(key, err.Error())
|
||||
}
|
||||
|
||||
// Interface appends string key and interface value to event.
|
||||
func (e *Event) Interface(key string, value interface{}) *Event {
|
||||
switch v := value.(type) {
|
||||
case byte:
|
||||
e.Byte(key, v)
|
||||
case []byte:
|
||||
e.Bytes(key, v)
|
||||
case string:
|
||||
e.String(key, v)
|
||||
case int:
|
||||
e.Int(key, v)
|
||||
case int32:
|
||||
e.Int32(key, v)
|
||||
case int64:
|
||||
e.Int64(key, v)
|
||||
case uint:
|
||||
e.Uint(key, v)
|
||||
case uint32:
|
||||
e.Uint32(key, v)
|
||||
case uint64:
|
||||
e.Uint64(key, v)
|
||||
case float32:
|
||||
e.Float32(key, v)
|
||||
case float64:
|
||||
e.Float64(key, v)
|
||||
case bool:
|
||||
e.Bool(key, v)
|
||||
case time.Time:
|
||||
e.Time(key, v, convert.ISO8601Milli)
|
||||
case error:
|
||||
e.Error(key, v)
|
||||
case nil:
|
||||
e.String(key, "nil")
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown field type: %v", value))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Event) appendField(key string, appendFunc func()) *Event {
|
||||
if !e.isEnabled {
|
||||
return e
|
||||
}
|
||||
|
||||
// Ignore field with empty key.
|
||||
if len(key) <= 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
// Append space if event field not empty.
|
||||
if e.buffer.Len() != 0 {
|
||||
e.buffer.AppendByte(' ')
|
||||
}
|
||||
|
||||
e.buffer.AppendString(key)
|
||||
e.buffer.AppendString("=")
|
||||
|
||||
appendFunc()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Event) write(format string, v ...interface{}) {
|
||||
defer e.Free()
|
||||
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
// Append interested contexts.
|
||||
if e.ctx != nil && e.ctxKeys != nil && e.ctxKeysMap != nil {
|
||||
for _, key := range *e.ctxKeys {
|
||||
if value := e.ctx.Value(key); value != nil {
|
||||
e.Interface((*e.ctxKeysMap)[key], e.ctx.Value(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append caller.
|
||||
if e.isCallerEnabled {
|
||||
ec := newEventCaller(runtime.Caller(callerSkipOffset))
|
||||
e.String("source", ec.TrimmedPath())
|
||||
}
|
||||
|
||||
// Compose and store current log.
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
// Format print message.
|
||||
if format != "" {
|
||||
if len(v) == 0 {
|
||||
fmt.Fprint(buf, format)
|
||||
} else {
|
||||
fmt.Fprintf(buf, format, v...)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(buf, v...)
|
||||
}
|
||||
|
||||
// Append filed.
|
||||
buf.AppendByte(' ')
|
||||
buf.AppendBytes(e.buffer.Bytes())
|
||||
|
||||
// Finally write.
|
||||
if _, err := e.lw.WriteLevel(e.level, buf.Bytes()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "log: could not write event: %v", err)
|
||||
}
|
||||
|
||||
switch e.level {
|
||||
case PanicLevel:
|
||||
panic(buf.String())
|
||||
case FatalLevel:
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func newEventCaller(pc uintptr, file string, line int, ok bool) (ec *EventCaller) {
|
||||
ec = eventCallerPool.Get()
|
||||
|
||||
if ok {
|
||||
ec.PC = pc
|
||||
ec.File = file
|
||||
ec.Line = line
|
||||
ec.Defined = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newEvent(
|
||||
ctx context.Context,
|
||||
ctxKeys *[]interface{}, ctxKeysMap *map[interface{}]string,
|
||||
level Level, lw LevelWriter,
|
||||
isEnabled bool, isCallerEnabled bool,
|
||||
) (e *Event) {
|
||||
e = eventPool.Get()
|
||||
|
||||
e.level = level
|
||||
e.lw = lw
|
||||
|
||||
e.ctx = ctx
|
||||
e.ctxKeys = ctxKeys
|
||||
e.ctxKeysMap = ctxKeysMap
|
||||
|
||||
e.isEnabled = isEnabled
|
||||
e.isCallerEnabled = isCallerEnabled
|
||||
return
|
||||
}
|
||||
|
||||
func needsQuote(s string) bool {
|
||||
for i := range s {
|
||||
if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const callerSkipOffset = 2
|
||||
|
||||
// eventCallerPool is a pool of newEvent callers.
|
||||
var eventCallerPool = NewEventCallerPool()
|
||||
|
||||
// eventPool is a pool of events.
|
||||
var eventPool = NewEventPool()
|
145
vendor/github.com/pengsrc/go-shared/log/event_test.go
generated
vendored
Normal file
145
vendor/github.com/pengsrc/go-shared/log/event_test.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
func TestEventCallerPool(t *testing.T) {
|
||||
p := NewEventCallerPool()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < 10; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
eventCaller := p.Get()
|
||||
assert.NotNil(t, eventCaller)
|
||||
eventCaller.Free()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestEntryCaller(t *testing.T) {
|
||||
tests := []struct {
|
||||
caller *EventCaller
|
||||
full string
|
||||
short string
|
||||
}{
|
||||
{
|
||||
caller: newEventCaller(100, "/path/to/foo.go", 42, false),
|
||||
full: "undefined",
|
||||
short: "undefined",
|
||||
},
|
||||
{
|
||||
caller: newEventCaller(100, "/path/to/foo.go", 42, true),
|
||||
full: "/path/to/foo.go:42",
|
||||
short: "to/foo.go:42",
|
||||
},
|
||||
{
|
||||
caller: newEventCaller(100, "to/foo.go", 42, true),
|
||||
full: "to/foo.go:42",
|
||||
short: "to/foo.go:42",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(t, tt.full, tt.caller.String(), "Unexpected string from EntryCaller.")
|
||||
assert.Equal(t, tt.full, tt.caller.FullPath(), "Unexpected FullPath from EntryCaller.")
|
||||
assert.Equal(t, tt.short, tt.caller.TrimmedPath(), "Unexpected TrimmedPath from EntryCaller.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventPool(t *testing.T) {
|
||||
p := NewEventPool()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < 10; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
event := p.Get()
|
||||
assert.NotNil(t, event)
|
||||
event.Free()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestEvent(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
l, err := NewLogger(buf, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.DebugEvent(context.TODO()).Byte("b", 'b').Message("DEBUG b")
|
||||
assert.Contains(t, buf.String(), "DEBUG b b=b")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).Bytes("bs", []byte("bs")).Message("DEBUG bs")
|
||||
l.DebugEvent(context.TODO()).Bytes("bs", []byte("bs bs")).Messagef("DEBUG %s", "bs")
|
||||
assert.Contains(t, buf.String(), "DEBUG bs bs=bs")
|
||||
assert.Contains(t, buf.String(), `DEBUG bs bs="bs bs"`)
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).String("s", "s").Message("DEBUG s")
|
||||
l.DebugEvent(context.TODO()).String("s", "s s").Messagef("DEBUG %d", 1024)
|
||||
assert.Contains(t, buf.String(), "DEBUG s s=s")
|
||||
assert.Contains(t, buf.String(), `DEBUG 1024 s="s s"`)
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(context.TODO()).
|
||||
Int("i", 1).Int32("i32", int32(2)).Int64("i64", int64(3)).
|
||||
Messagef("INFO %d", 123)
|
||||
assert.Contains(t, buf.String(), "INFO 123 i=1 i32=2 i64=3")
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(context.TODO()).
|
||||
Uint("i", 1).Uint32("i32", uint32(2)).Uint64("i64", uint64(3)).
|
||||
Messagef("INFO %d", 123)
|
||||
assert.Contains(t, buf.String(), "INFO 123 i=1 i32=2 i64=3")
|
||||
buf.Reset()
|
||||
|
||||
l.WarnEvent(context.TODO()).
|
||||
Float32("f32", float32(32.2333)).Float64("f64", float64(64.6444)).
|
||||
Messagef("WARN %s %d.", "hello", 1024)
|
||||
assert.Contains(t, buf.String(), "WARN hello 1024. f32=32.2333 f64=64.6444")
|
||||
buf.Reset()
|
||||
|
||||
l.WarnEvent(context.TODO()).
|
||||
Bool("true", true).Bool("false", false).
|
||||
Message("WARN bool.")
|
||||
assert.Contains(t, buf.String(), "WARN bool. true=true false=false")
|
||||
buf.Reset()
|
||||
|
||||
l.ErrorEvent(context.TODO()).
|
||||
Time("time", time.Time{}, convert.RFC822).Error("error", errors.New("error message")).
|
||||
Message("Error.")
|
||||
assert.Contains(t, buf.String(), `Error. time="Mon, 01 Jan 0001 00:00:00 GMT" error="error message"`)
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).
|
||||
String("a", "a a").
|
||||
String("b", "b'b").
|
||||
String("c", `c"c`).
|
||||
Message("yes")
|
||||
assert.Contains(t, buf.String(), `a="a a"`)
|
||||
assert.Contains(t, buf.String(), `b=b'b`)
|
||||
assert.Contains(t, buf.String(), `c="c\"c"`)
|
||||
buf.Reset()
|
||||
}
|
151
vendor/github.com/pengsrc/go-shared/log/exported.go
generated
vendored
Normal file
151
vendor/github.com/pengsrc/go-shared/log/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
|
||||
func Fatal(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, FatalLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Panic logs a message with severity PANIC followed by a call to panic().
|
||||
func Panic(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, PanicLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs a message with severity ERROR.
|
||||
func Error(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, ErrorLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn logs a message with severity WARN.
|
||||
func Warn(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, WarnLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message with severity INFO.
|
||||
func Info(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, InfoLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs a message with severity DEBUG.
|
||||
func Debug(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, DebugLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf logs a message with severity FATAL in format followed by a call to
|
||||
// os.Exit(1).
|
||||
func Fatalf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, FatalLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Panicf logs a message with severity PANIC in format followed by a call to
|
||||
// panic().
|
||||
func Panicf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, PanicLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf logs a message with severity ERROR in format.
|
||||
func Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, ErrorLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf logs a message with severity WARN in format.
|
||||
func Warnf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, WarnLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof logs a message with severity INFO in format.
|
||||
func Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, InfoLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf logs a message with severity DEBUG in format.
|
||||
func Debugf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, DebugLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalEvent returns a log event with severity FATAL.
|
||||
func FatalEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, FatalLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PanicEvent returns a log event with severity PANIC.
|
||||
func PanicEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, PanicLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrorEvent returns a log event with severity ERROR.
|
||||
func ErrorEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, ErrorLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WarnEvent returns a log event with severity WARN.
|
||||
func WarnEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, WarnLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InfoEvent returns a log event with severity INFO.
|
||||
func InfoEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, InfoLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DebugEvent returns a log event with severity DEBUG.
|
||||
func DebugEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, DebugLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetGlobalLogger sets a logger as global logger.
|
||||
func SetGlobalLogger(l *Logger) {
|
||||
globalLogger = l
|
||||
}
|
||||
|
||||
// GlobalLogger returns the global logger.
|
||||
func GlobalLogger() *Logger {
|
||||
return globalLogger
|
||||
}
|
||||
|
||||
var globalLogger *Logger
|
21
vendor/github.com/pengsrc/go-shared/log/exported_test.go
generated
vendored
Normal file
21
vendor/github.com/pengsrc/go-shared/log/exported_test.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExported(t *testing.T) {
|
||||
l, err := NewTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
SetGlobalLogger(l)
|
||||
|
||||
c := context.Background()
|
||||
|
||||
l.Debug(c, "Singleton logger")
|
||||
assert.NotNil(t, InfoEvent(context.Background()))
|
||||
|
||||
Debugf(c, "Debug message test, hi %s", "Apple")
|
||||
}
|
67
vendor/github.com/pengsrc/go-shared/log/level.go
generated
vendored
Normal file
67
vendor/github.com/pengsrc/go-shared/log/level.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Level defines log levels.
|
||||
type Level uint8
|
||||
|
||||
// String returns name of the level.
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case MuteLevel:
|
||||
return "MUTE"
|
||||
case FatalLevel:
|
||||
return "FATAL"
|
||||
case PanicLevel:
|
||||
return "PANIC"
|
||||
case ErrorLevel:
|
||||
return "ERROR"
|
||||
case WarnLevel:
|
||||
return "WARN"
|
||||
case InfoLevel:
|
||||
return "INFO"
|
||||
case DebugLevel:
|
||||
return "DEBUG"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// MuteLevel disables the logger.
|
||||
MuteLevel Level = iota
|
||||
// FatalLevel defines fatal log level.
|
||||
FatalLevel
|
||||
// PanicLevel defines panic log level.
|
||||
PanicLevel
|
||||
// ErrorLevel defines error log level.
|
||||
ErrorLevel
|
||||
// WarnLevel defines warn log level.
|
||||
WarnLevel
|
||||
// InfoLevel defines info log level.
|
||||
InfoLevel
|
||||
// DebugLevel defines debug log level.
|
||||
DebugLevel
|
||||
)
|
||||
|
||||
// ParseLevel takes a string level and returns the log level constant.
|
||||
func ParseLevel(level string) (Level, error) {
|
||||
switch strings.ToUpper(level) {
|
||||
case "FATAL":
|
||||
return FatalLevel, nil
|
||||
case "PANIC":
|
||||
return PanicLevel, nil
|
||||
case "ERROR":
|
||||
return ErrorLevel, nil
|
||||
case "WARN":
|
||||
return WarnLevel, nil
|
||||
case "INFO":
|
||||
return InfoLevel, nil
|
||||
case "DEBUG":
|
||||
return DebugLevel, nil
|
||||
}
|
||||
|
||||
return MuteLevel, fmt.Errorf(`"%q" is not a valid log Level`, level)
|
||||
}
|
45
vendor/github.com/pengsrc/go-shared/log/level_test.go
generated
vendored
Normal file
45
vendor/github.com/pengsrc/go-shared/log/level_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
var l Level
|
||||
var err error
|
||||
assert.Equal(t, l, MuteLevel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l, err = ParseLevel("FATAL")
|
||||
assert.Equal(t, l, FatalLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("PANIC")
|
||||
assert.Equal(t, l, PanicLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("ERROR")
|
||||
assert.Equal(t, l, ErrorLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("WARN")
|
||||
assert.Equal(t, l, WarnLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("INFO")
|
||||
assert.Equal(t, l, InfoLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("DEBUG")
|
||||
assert.Equal(t, l, DebugLevel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l, err = ParseLevel("invalid")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLevelString(t *testing.T) {
|
||||
assert.Equal(t, "FATAL", FatalLevel.String())
|
||||
assert.Equal(t, "PANIC", PanicLevel.String())
|
||||
assert.Equal(t, "ERROR", ErrorLevel.String())
|
||||
assert.Equal(t, "WARN", WarnLevel.String())
|
||||
assert.Equal(t, "INFO", InfoLevel.String())
|
||||
assert.Equal(t, "DEBUG", DebugLevel.String())
|
||||
}
|
327
vendor/github.com/pengsrc/go-shared/log/logger.go
generated
vendored
Normal file
327
vendor/github.com/pengsrc/go-shared/log/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,327 @@
|
|||
// Package log provides support for logging to stdout, stderr and file.
|
||||
package log
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/check"
|
||||
"github.com/pengsrc/go-shared/reopen"
|
||||
)
|
||||
|
||||
// Logger presents a logger.
|
||||
// The only way to initialize a logger is using the convention construct
|
||||
// functions like NewLogger().
|
||||
type Logger struct {
|
||||
level Level
|
||||
lw LevelWriter
|
||||
|
||||
// Interested context keys.
|
||||
ctxKeys []interface{}
|
||||
ctxKeysMap map[interface{}]string
|
||||
|
||||
// isCallerEnabled sets whether to annotating logs with the calling
|
||||
// function's file name and line number. By default, all logs are annotated.
|
||||
isCallerEnabled bool
|
||||
}
|
||||
|
||||
// GetLevel get the log level string.
|
||||
func (l *Logger) GetLevel() string {
|
||||
return l.level.String()
|
||||
}
|
||||
|
||||
// SetLevel sets the log level.
|
||||
// Valid levels are "debug", "info", "warn", "error", and "fatal".
|
||||
func (l *Logger) SetLevel(level string) (err error) {
|
||||
levelFlag, err := ParseLevel(level)
|
||||
if err == nil {
|
||||
l.level = levelFlag
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetInterestContextKeys sets the contexts keys that the logger should be
|
||||
// interested in. Value of the interested context key will extract and print as
|
||||
// newEvent filed.
|
||||
func (l *Logger) SetInterestContextKeys(keys []interface{}) {
|
||||
l.ctxKeys = keys
|
||||
|
||||
l.ctxKeysMap = make(map[interface{}]string)
|
||||
for _, key := range l.ctxKeys {
|
||||
l.ctxKeysMap[key] = fmt.Sprintf("%v", key)
|
||||
}
|
||||
}
|
||||
|
||||
// SetCallerFlag sets whether to annotating logs with the caller.
|
||||
func (l *Logger) SetCallerFlag(isEnabled bool) {
|
||||
l.isCallerEnabled = isEnabled
|
||||
}
|
||||
|
||||
// Flush writes buffered logs.
|
||||
func (l *Logger) Flush() {
|
||||
if flusher, ok := l.lw.(Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
|
||||
func (l *Logger) Fatal(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, FatalLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Panic logs a message with severity PANIC followed by a call to panic().
|
||||
func (l *Logger) Panic(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, PanicLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Error logs a message with severity ERROR.
|
||||
func (l *Logger) Error(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, ErrorLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Warn logs a message with severity WARN.
|
||||
func (l *Logger) Warn(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, WarnLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Info logs a message with severity INFO.
|
||||
func (l *Logger) Info(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, InfoLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Debug logs a message with severity DEBUG.
|
||||
func (l *Logger) Debug(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, DebugLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message with severity FATAL in format followed by a call to
|
||||
// os.Exit(1).
|
||||
func (l *Logger) Fatalf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, FatalLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Panicf logs a message with severity PANIC in format followed by a call to
|
||||
// panic().
|
||||
func (l *Logger) Panicf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, PanicLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Errorf logs a message with severity ERROR in format.
|
||||
func (l *Logger) Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, ErrorLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Warnf logs a message with severity WARN in format.
|
||||
func (l *Logger) Warnf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, WarnLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Infof logs a message with severity INFO in format.
|
||||
func (l *Logger) Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, InfoLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Debugf logs a message with severity DEBUG in format.
|
||||
func (l *Logger) Debugf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, DebugLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// FatalEvent returns a log event with severity FATAL.
|
||||
func (l *Logger) FatalEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, FatalLevel)
|
||||
}
|
||||
|
||||
// PanicEvent returns a log event with severity PANIC.
|
||||
func (l *Logger) PanicEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, PanicLevel)
|
||||
}
|
||||
|
||||
// ErrorEvent returns a log event with severity ERROR.
|
||||
func (l *Logger) ErrorEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, ErrorLevel)
|
||||
}
|
||||
|
||||
// WarnEvent returns a log event with severity WARN.
|
||||
func (l *Logger) WarnEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, WarnLevel)
|
||||
}
|
||||
|
||||
// InfoEvent returns a log event with severity INFO.
|
||||
func (l *Logger) InfoEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, InfoLevel)
|
||||
}
|
||||
|
||||
// DebugEvent returns a log event with severity DEBUG.
|
||||
func (l *Logger) DebugEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, DebugLevel)
|
||||
}
|
||||
|
||||
func (l *Logger) event(ctx context.Context, level Level) (e *Event) {
|
||||
var ctxKeys *[]interface{}
|
||||
var ctxKeysMap *map[interface{}]string
|
||||
|
||||
if len(l.ctxKeys) > 0 {
|
||||
ctxKeys = &l.ctxKeys
|
||||
}
|
||||
if len(l.ctxKeysMap) > 0 {
|
||||
ctxKeysMap = &l.ctxKeysMap
|
||||
}
|
||||
|
||||
return newEvent(ctx, ctxKeys, ctxKeysMap, level, l.lw, level <= l.level, l.isCallerEnabled)
|
||||
}
|
||||
|
||||
// NewLogger creates a new logger for given out and level, and the level is
|
||||
// optional.
|
||||
func NewLogger(out io.Writer, level ...string) (*Logger, error) {
|
||||
return NewLoggerWithError(out, nil, level...)
|
||||
}
|
||||
|
||||
// NewLoggerWithError creates a new logger for given out, err out, level, and the
|
||||
// err out can be nil, and the level is optional.
|
||||
func NewLoggerWithError(out, errOut io.Writer, level ...string) (l *Logger, err error) {
|
||||
if out == nil {
|
||||
return nil, errors.New("logger output must specified")
|
||||
}
|
||||
|
||||
sw := &StandardWriter{w: out, ew: errOut, pid: os.Getpid()}
|
||||
l = &Logger{lw: sw}
|
||||
|
||||
if len(level) == 1 {
|
||||
if err = l.SetLevel(level[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// NewTerminalLogger creates a logger that write into terminal.
|
||||
func NewTerminalLogger(level ...string) (*Logger, error) {
|
||||
return NewLogger(os.Stdout, level...)
|
||||
}
|
||||
|
||||
// NewBufferedTerminalLogger creates a buffered logger that write into terminal.
|
||||
func NewBufferedTerminalLogger(level ...string) (*Logger, error) {
|
||||
return NewLogger(bufio.NewWriter(os.Stdout), level...)
|
||||
}
|
||||
|
||||
// NewFileLogger creates a logger that write into file.
|
||||
func NewFileLogger(filePath string, level ...string) (*Logger, error) {
|
||||
return NewFileLoggerWithError(filePath, "", level...)
|
||||
}
|
||||
|
||||
// NewFileLoggerWithError creates a logger that write into files.
|
||||
func NewFileLoggerWithError(filePath, errFilePath string, level ...string) (*Logger, error) {
|
||||
if err := check.Dir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := check.Dir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
out.Reopen()
|
||||
if errOut != nil {
|
||||
errOut.Reopen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if errOut == nil {
|
||||
return NewLoggerWithError(out, nil, level...)
|
||||
}
|
||||
return NewLoggerWithError(out, errOut, level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLogger creates a logger that write into file with buffer.
|
||||
// The flushSeconds's unit is second.
|
||||
func NewBufferedFileLogger(filePath string, flushInterval int, level ...string) (*Logger, error) {
|
||||
return NewBufferedFileLoggerWithError(filePath, "", flushInterval, level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLoggerWithError creates a logger that write into files with buffer.
|
||||
// The flushSeconds's unit is second.
|
||||
func NewBufferedFileLoggerWithError(filePath, errFilePath string, flushInterval int, level ...string) (*Logger, error) {
|
||||
if err := check.Dir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := check.Dir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if flushInterval == 0 {
|
||||
flushInterval = 10
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bufferedOut := reopen.NewBufferedFileWriter(out)
|
||||
var bufferedErrOut *reopen.BufferedFileWriter
|
||||
if errOut != nil {
|
||||
bufferedErrOut = reopen.NewBufferedFileWriter(errOut)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
bufferedOut.Reopen()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Reopen()
|
||||
}
|
||||
case <-time.After(time.Duration(flushInterval) * time.Second):
|
||||
bufferedOut.Flush()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if bufferedErrOut == nil {
|
||||
return NewLoggerWithError(bufferedOut, nil, level...)
|
||||
}
|
||||
return NewLoggerWithError(bufferedOut, bufferedErrOut, level...)
|
||||
}
|
331
vendor/github.com/pengsrc/go-shared/log/logger_test.go
generated
vendored
Normal file
331
vendor/github.com/pengsrc/go-shared/log/logger_test.go
generated
vendored
Normal file
|
@ -0,0 +1,331 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
)
|
||||
|
||||
func TestSetAndGetLevel(t *testing.T) {
|
||||
l, err := NewTerminalLogger()
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.SetLevel("ERROR")
|
||||
assert.Equal(t, "ERROR", l.GetLevel())
|
||||
}
|
||||
|
||||
func TestNewLogger(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
l, err := NewLogger(buf, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "DEBUG message")
|
||||
l.Info(context.Background(), "INFO message")
|
||||
|
||||
assert.NotContains(t, buf.String(), "DEBUG message")
|
||||
assert.Contains(t, buf.String(), "INFO message")
|
||||
buf.Reset()
|
||||
|
||||
// Test logging for context.
|
||||
type contextKey string
|
||||
const traceID contextKey = "trace_id"
|
||||
ctx := context.WithValue(nil, traceID, "60b725f10c9c85c70d97880dfe8191b3")
|
||||
|
||||
l.SetInterestContextKeys([]interface{}{traceID})
|
||||
|
||||
l.Info(ctx, "Hello World!")
|
||||
assert.Contains(t, buf.String(), "trace_id=60b725f10c9c85c70d97880dfe8191b3")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.SetCallerFlag(true)
|
||||
|
||||
l.Info(ctx, "Hello World!")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.Infof(ctx, "Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(ctx).Messagef("Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(ctx).Int("count", 1024).Messagef("Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
}
|
||||
|
||||
func TestNewLoggerWithError(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
errBuf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
defer errBuf.Free()
|
||||
|
||||
l, err := NewLoggerWithError(buf, errBuf, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "DEBUG message")
|
||||
l.Info(context.Background(), "INFO message")
|
||||
l.Error(context.Background(), "ERROR message")
|
||||
|
||||
assert.NotContains(t, buf.String(), "DEBUG message")
|
||||
assert.Contains(t, buf.String(), "INFO message")
|
||||
assert.Contains(t, buf.String(), "ERROR message")
|
||||
|
||||
assert.NotContains(t, errBuf.String(), "DEBUG message")
|
||||
assert.NotContains(t, errBuf.String(), "INFO message")
|
||||
assert.Contains(t, errBuf.String(), "ERROR message")
|
||||
}
|
||||
|
||||
func TestNewFileLogger(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
defer os.Remove(lFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewLoggerWithError(nil, nil, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewLoggerWithError(ioutil.Discard, ioutil.Discard, "INVALID")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
l, err := NewFileLogger(lFile, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Move log file.
|
||||
movedLFile := fmt.Sprintf(`%s.move`, lFile)
|
||||
err = os.Rename(lFile, movedLFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedLFile)
|
||||
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(movedLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(data), "\n")))
|
||||
}
|
||||
|
||||
func TestNewFileLoggerWithError(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
errLFile := path.Join(os.TempDir(), "test.log.wf")
|
||||
defer os.Remove(lFile)
|
||||
defer os.Remove(errLFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewFileLoggerWithError("/not/exists/dir", "/not/exists/dir", "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(lFile, "/not/exists/dir", "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(os.TempDir(), os.TempDir(), "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(lFile, os.TempDir(), "DEBUG")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
l, err := NewFileLoggerWithError(lFile, errLFile, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err := ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Move data file.
|
||||
movedLogFile := fmt.Sprintf(`%s.move`, lFile)
|
||||
err = os.Rename(lFile, movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedLogFile)
|
||||
|
||||
movedErrLogFile := fmt.Sprintf(`%s.move`, errLFile)
|
||||
err = os.Rename(errLFile, movedErrLogFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedErrLogFile)
|
||||
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 6, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(movedErrLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLogger(t *testing.T) {
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
defer os.Remove(lFile)
|
||||
|
||||
l, err := NewBufferedFileLogger(lFile, 1, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Wait timeout.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLoggerWithError(t *testing.T) {
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
errLFile := path.Join(os.TempDir(), "test.log.wf")
|
||||
defer os.Remove(lFile)
|
||||
defer os.Remove(errLFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewBufferedFileLoggerWithError("/not/exists/dir", "/not/exists/dir", 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(lFile, "/not/exists/dir", 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(os.TempDir(), os.TempDir(), 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(lFile, os.TempDir(), 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
errL, err := NewBufferedFileLoggerWithError(lFile, errLFile, 1, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
errL.Debug(context.Background(), "file - DEBUG message")
|
||||
errL.Info(context.Background(), "file - INFO message")
|
||||
errL.Warn(context.Background(), "file - WARN message")
|
||||
errL.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errData, err := ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(errData), "\n")))
|
||||
|
||||
// Flush log.
|
||||
errL.Flush()
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errData, err = ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errData), "\n")))
|
||||
|
||||
// Wait timeout to improve test coverage.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Reopen using signal to improve test coverage.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
}
|
||||
|
||||
func TestTerminalLogger(t *testing.T) {
|
||||
l, err := NewTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "terminal - DEBUG message")
|
||||
l.Info(context.Background(), "terminal - INFO message")
|
||||
l.Warn(context.Background(), "terminal - WARN message")
|
||||
l.Error(context.Background(), "terminal - ERROR message")
|
||||
|
||||
l.Debugf(context.Background(), "terminal - DEBUG message - %d", time.Now().Unix())
|
||||
l.Infof(context.Background(), "terminal - INFO message - %d", time.Now().Unix())
|
||||
l.Warnf(context.Background(), "terminal - WARN message - %d", time.Now().Unix())
|
||||
l.Errorf(context.Background(), "terminal - ERROR message - %d", time.Now().Unix())
|
||||
}
|
||||
|
||||
func TestBufferedTerminalLogger(t *testing.T) {
|
||||
l, err := NewBufferedTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "terminal - DEBUG message")
|
||||
l.Info(context.Background(), "terminal - INFO message")
|
||||
l.Warn(context.Background(), "terminal - WARN message")
|
||||
l.Error(context.Background(), "terminal - ERROR message")
|
||||
|
||||
l.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkLogger(b *testing.B) {
|
||||
l, err := NewLogger(ioutil.Discard, "DEBUG")
|
||||
assert.NoError(b, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.DebugEvent(ctx).String("key", "value").Messagef("Hello %s!", "World")
|
||||
}
|
||||
}
|
83
vendor/github.com/pengsrc/go-shared/log/writer.go
generated
vendored
Normal file
83
vendor/github.com/pengsrc/go-shared/log/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
// LevelWriter defines as interface a writer may implement in order
|
||||
// to receive level information with payload.
|
||||
type LevelWriter interface {
|
||||
io.Writer
|
||||
WriteLevel(level Level, message []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// Flusher defines a interface with Flush() method.
|
||||
type Flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// StandardWriter implements io.Writer{} and LevelWriter{} interface.
|
||||
type StandardWriter struct {
|
||||
w io.Writer
|
||||
ew io.Writer // Writer for WARN, ERROR, FATAL, PANIC
|
||||
|
||||
dl Level // Default level
|
||||
pid int
|
||||
}
|
||||
|
||||
// Write implements the io.Writer{} interface.
|
||||
func (sw *StandardWriter) Write(p []byte) (n int, err error) {
|
||||
return sw.WriteLevel(sw.dl, p)
|
||||
}
|
||||
|
||||
// WriteLevel implements the LevelWriter{} interface.
|
||||
func (sw *StandardWriter) WriteLevel(level Level, message []byte) (n int, err error) {
|
||||
levelString := level.String()
|
||||
if len(levelString) == 4 {
|
||||
levelString = " " + levelString
|
||||
}
|
||||
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
buf.AppendString("[")
|
||||
buf.AppendTime(time.Now().UTC(), convert.ISO8601Milli)
|
||||
buf.AppendString(" #")
|
||||
buf.AppendInt(int64(sw.pid))
|
||||
buf.AppendString("] ")
|
||||
buf.AppendString(levelString)
|
||||
buf.AppendString(" -- : ")
|
||||
buf.AppendBytes(message)
|
||||
buf.AppendString("\n")
|
||||
|
||||
if sw.ew != nil {
|
||||
if level > MuteLevel && level <= WarnLevel {
|
||||
n, err = sw.ew.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return sw.w.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// Flush implements the Flusher{} interface.
|
||||
func (sw *StandardWriter) Flush() (err error) {
|
||||
if flusher, ok := sw.w.(Flusher); ok {
|
||||
err = flusher.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if sw.ew != nil {
|
||||
if flusher, ok := sw.ew.(Flusher); ok {
|
||||
err = flusher.Flush()
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
36
vendor/github.com/pengsrc/go-shared/log/writer_test.go
generated
vendored
Normal file
36
vendor/github.com/pengsrc/go-shared/log/writer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStandardWriter(t *testing.T) {
|
||||
var w io.Writer
|
||||
w = &StandardWriter{
|
||||
w: os.Stdout, ew: os.Stderr,
|
||||
dl: MuteLevel, pid: os.Getpid(),
|
||||
}
|
||||
|
||||
lw, ok := w.(LevelWriter)
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = w.(Flusher)
|
||||
assert.True(t, ok)
|
||||
|
||||
_, err := lw.Write([]byte("Hello World!"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func BenchmarkStandardWriter(b *testing.B) {
|
||||
lw := &StandardWriter{w: ioutil.Discard, ew: ioutil.Discard}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lw.Write([]byte("Hello World!"))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue