279 lines
5.1 KiB
Go
279 lines
5.1 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type Level int8
|
|
|
|
const (
|
|
DEBUG Level = iota
|
|
INFO
|
|
WARN
|
|
ERROR
|
|
FATAL
|
|
PANIC
|
|
)
|
|
|
|
func (l Level) String() string {
|
|
switch l {
|
|
case DEBUG:
|
|
return "DEBUG"
|
|
case INFO:
|
|
return "INFO"
|
|
case WARN:
|
|
return "WARN"
|
|
case ERROR:
|
|
return "ERROR"
|
|
case FATAL:
|
|
return "FATAL"
|
|
case PANIC:
|
|
return "PANIC"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
type Format int8
|
|
|
|
const (
|
|
TEXT Format = iota
|
|
JSON
|
|
)
|
|
|
|
type Output int8
|
|
|
|
const (
|
|
Stdout Output = iota
|
|
Stderr
|
|
)
|
|
|
|
type Entry struct {
|
|
Time time.Time
|
|
Level Level
|
|
Message string
|
|
Fields map[string]interface{}
|
|
Caller string
|
|
}
|
|
|
|
type Logger struct {
|
|
opts options
|
|
mu sync.Mutex
|
|
writer io.Writer
|
|
formatter Formatter
|
|
}
|
|
|
|
func New(opts ...Option) *Logger {
|
|
o := defaultOptions()
|
|
for _, opt := range opts {
|
|
opt(&o)
|
|
}
|
|
|
|
var writer io.Writer
|
|
switch o.output {
|
|
case Stdout:
|
|
writer = os.Stdout
|
|
case Stderr:
|
|
writer = os.Stderr
|
|
default:
|
|
writer = os.Stdout
|
|
}
|
|
|
|
if o.outputFile != "" {
|
|
f, err := os.OpenFile(o.outputFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
|
if err == nil {
|
|
writer = f
|
|
}
|
|
}
|
|
|
|
return &Logger{
|
|
opts: o,
|
|
writer: writer,
|
|
formatter: newFormatter(o.format),
|
|
}
|
|
}
|
|
|
|
func (l *Logger) log(level Level, msg string, args ...interface{}) {
|
|
if level < l.opts.level {
|
|
return
|
|
}
|
|
|
|
entry := &Entry{
|
|
Time: time.Now(),
|
|
Level: level,
|
|
Message: fmt.Sprintf(msg, args...),
|
|
Fields: make(map[string]interface{}),
|
|
}
|
|
|
|
if l.opts.caller {
|
|
entry.Caller = getCaller(3)
|
|
}
|
|
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.formatter.Format(l.writer, entry)
|
|
}
|
|
|
|
func (l *Logger) logCtx(level Level, ctx context.Context, msg string, args ...interface{}) {
|
|
if level < l.opts.level {
|
|
return
|
|
}
|
|
|
|
entry := &Entry{
|
|
Time: time.Now(),
|
|
Level: level,
|
|
Message: fmt.Sprintf(msg, args...),
|
|
Fields: make(map[string]interface{}),
|
|
}
|
|
|
|
if ctx != nil {
|
|
if traceID := ctx.Value(l.opts.fieldKey); traceID != nil {
|
|
entry.Fields[l.opts.fieldKey] = traceID
|
|
}
|
|
}
|
|
|
|
if l.opts.caller {
|
|
entry.Caller = getCaller(3)
|
|
}
|
|
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.formatter.Format(l.writer, entry)
|
|
}
|
|
|
|
func (l *Logger) logField(level Level, message string, keyValues ...interface{}) {
|
|
if level < l.opts.level {
|
|
return
|
|
}
|
|
|
|
entry := &Entry{
|
|
Time: time.Now(),
|
|
Level: level,
|
|
Message: message,
|
|
Fields: parseFields(keyValues),
|
|
}
|
|
|
|
if l.opts.caller {
|
|
entry.Caller = getCaller(3)
|
|
}
|
|
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.formatter.Format(l.writer, entry)
|
|
}
|
|
|
|
func (l *Logger) Debug(format string, args ...interface{}) {
|
|
l.log(DEBUG, format, args...)
|
|
}
|
|
|
|
func (l *Logger) Info(format string, args ...interface{}) {
|
|
l.log(INFO, format, args...)
|
|
}
|
|
|
|
func (l *Logger) Warn(format string, args ...interface{}) {
|
|
l.log(WARN, format, args...)
|
|
}
|
|
|
|
func (l *Logger) Error(format string, args ...interface{}) {
|
|
l.log(ERROR, format, args...)
|
|
}
|
|
|
|
func (l *Logger) Fatal(format string, args ...interface{}) {
|
|
l.log(FATAL, format, args...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func (l *Logger) Panic(format string, args ...interface{}) {
|
|
l.log(PANIC, format, args...)
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
panic(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func (l *Logger) DebugCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(DEBUG, ctx, format, args...)
|
|
}
|
|
|
|
func (l *Logger) InfoCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(INFO, ctx, format, args...)
|
|
}
|
|
|
|
func (l *Logger) WarnCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(WARN, ctx, format, args...)
|
|
}
|
|
|
|
func (l *Logger) ErrorCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(ERROR, ctx, format, args...)
|
|
}
|
|
|
|
func (l *Logger) FatalCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(FATAL, ctx, format, args...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func (l *Logger) PanicCtx(ctx context.Context, format string, args ...interface{}) {
|
|
l.logCtx(PANIC, ctx, format, args...)
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
panic(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func (l *Logger) DebugField(message string, keyValues ...interface{}) {
|
|
l.logField(DEBUG, message, keyValues...)
|
|
}
|
|
|
|
func (l *Logger) InfoField(message string, keyValues ...interface{}) {
|
|
l.logField(INFO, message, keyValues...)
|
|
}
|
|
|
|
func (l *Logger) WarnField(message string, keyValues ...interface{}) {
|
|
l.logField(WARN, message, keyValues...)
|
|
}
|
|
|
|
func (l *Logger) ErrorField(message string, keyValues ...interface{}) {
|
|
l.logField(ERROR, message, keyValues...)
|
|
}
|
|
|
|
func (l *Logger) FatalField(message string, keyValues ...interface{}) {
|
|
l.logField(FATAL, message, keyValues...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func (l *Logger) PanicField(message string, keyValues ...interface{}) {
|
|
l.logField(PANIC, message, keyValues...)
|
|
panic(message)
|
|
}
|
|
|
|
func (l *Logger) Close() error {
|
|
if f, ok := l.writer.(*os.File); ok {
|
|
return f.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getCaller(skip int) string {
|
|
_, file, line, ok := runtime.Caller(skip)
|
|
if !ok {
|
|
return "?:?"
|
|
}
|
|
return fmt.Sprintf("%s:%d", file, line)
|
|
}
|
|
|
|
func parseFields(keyValues []interface{}) map[string]interface{} {
|
|
fields := make(map[string]interface{})
|
|
for i := 0; i+1 < len(keyValues); i += 2 {
|
|
key, ok := keyValues[i].(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
fields[key] = keyValues[i+1]
|
|
}
|
|
return fields
|
|
}
|