2020-02-21 10:57:59 +03:00
|
|
|
package logger
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2020-04-26 17:11:53 +03:00
|
|
|
"runtime"
|
2020-03-03 11:07:38 +03:00
|
|
|
"sort"
|
2020-02-24 16:07:40 +03:00
|
|
|
"sync"
|
2020-02-23 16:45:20 +03:00
|
|
|
"time"
|
|
|
|
|
|
|
|
dlog "github.com/micro/go-micro/v2/debug/log"
|
2020-02-21 10:57:59 +03:00
|
|
|
)
|
|
|
|
|
2020-03-26 01:00:43 +03:00
|
|
|
func init() {
|
|
|
|
lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL"))
|
|
|
|
if err != nil {
|
|
|
|
lvl = InfoLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultLogger = NewHelper(NewLogger(WithLevel(lvl)))
|
|
|
|
}
|
|
|
|
|
2020-02-21 10:57:59 +03:00
|
|
|
type defaultLogger struct {
|
2020-02-24 16:07:40 +03:00
|
|
|
sync.RWMutex
|
2020-02-21 10:57:59 +03:00
|
|
|
opts Options
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init(opts...) should only overwrite provided options
|
|
|
|
func (l *defaultLogger) Init(opts ...Option) error {
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&l.opts)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *defaultLogger) String() string {
|
|
|
|
return "default"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
2020-02-24 16:07:40 +03:00
|
|
|
l.Lock()
|
|
|
|
l.opts.Fields = copyFields(fields)
|
|
|
|
l.Unlock()
|
2020-02-21 10:57:59 +03:00
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:07:40 +03:00
|
|
|
func copyFields(src map[string]interface{}) map[string]interface{} {
|
|
|
|
dst := make(map[string]interface{}, len(src))
|
|
|
|
for k, v := range src {
|
|
|
|
dst[k] = v
|
|
|
|
}
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
|
2020-02-21 10:57:59 +03:00
|
|
|
func (l *defaultLogger) Log(level Level, v ...interface{}) {
|
2020-02-23 16:45:20 +03:00
|
|
|
// TODO decide does we need to write message if log level not used?
|
2020-02-21 10:57:59 +03:00
|
|
|
if !l.opts.Level.Enabled(level) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:07:40 +03:00
|
|
|
l.RLock()
|
|
|
|
fields := copyFields(l.opts.Fields)
|
|
|
|
l.RUnlock()
|
|
|
|
|
|
|
|
fields["level"] = level.String()
|
2020-02-21 10:57:59 +03:00
|
|
|
|
2020-04-26 17:11:53 +03:00
|
|
|
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
|
|
|
fields["caller"] = fmt.Sprintf("%s:%d", file, line)
|
|
|
|
}
|
|
|
|
|
2020-02-23 16:45:20 +03:00
|
|
|
rec := dlog.Record{
|
|
|
|
Timestamp: time.Now(),
|
|
|
|
Message: fmt.Sprint(v...),
|
2020-02-24 16:07:40 +03:00
|
|
|
Metadata: make(map[string]string, len(fields)),
|
2020-02-21 10:57:59 +03:00
|
|
|
}
|
2020-03-03 11:07:38 +03:00
|
|
|
|
|
|
|
keys := make([]string, 0, len(fields))
|
2020-02-23 16:45:20 +03:00
|
|
|
for k, v := range fields {
|
2020-03-03 11:07:38 +03:00
|
|
|
keys = append(keys, k)
|
2020-02-23 16:45:20 +03:00
|
|
|
rec.Metadata[k] = fmt.Sprintf("%v", v)
|
|
|
|
}
|
|
|
|
|
2020-03-03 11:07:38 +03:00
|
|
|
sort.Strings(keys)
|
|
|
|
metadata := ""
|
|
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
metadata += fmt.Sprintf(" %s=%v", k, fields[k])
|
|
|
|
}
|
|
|
|
|
2020-02-23 16:45:20 +03:00
|
|
|
dlog.DefaultLog.Write(rec)
|
|
|
|
|
|
|
|
t := rec.Timestamp.Format("2006-01-02 15:04:05")
|
2020-03-03 11:07:38 +03:00
|
|
|
fmt.Printf("%s %s %v\n", t, metadata, rec.Message)
|
2020-02-21 10:57:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
|
2020-02-23 16:45:20 +03:00
|
|
|
// TODO decide does we need to write message if log level not used?
|
2020-02-21 10:57:59 +03:00
|
|
|
if level < l.opts.Level {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:07:40 +03:00
|
|
|
l.RLock()
|
|
|
|
fields := copyFields(l.opts.Fields)
|
|
|
|
l.RUnlock()
|
|
|
|
|
|
|
|
fields["level"] = level.String()
|
2020-02-21 10:57:59 +03:00
|
|
|
|
2020-04-26 17:11:53 +03:00
|
|
|
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
|
|
|
fields["caller"] = fmt.Sprintf("%s:%d", file, line)
|
|
|
|
}
|
|
|
|
|
2020-02-23 16:45:20 +03:00
|
|
|
rec := dlog.Record{
|
|
|
|
Timestamp: time.Now(),
|
|
|
|
Message: fmt.Sprintf(format, v...),
|
2020-02-24 16:07:40 +03:00
|
|
|
Metadata: make(map[string]string, len(fields)),
|
2020-02-21 10:57:59 +03:00
|
|
|
}
|
2020-03-03 11:07:38 +03:00
|
|
|
|
|
|
|
keys := make([]string, 0, len(fields))
|
2020-02-23 16:45:20 +03:00
|
|
|
for k, v := range fields {
|
2020-03-03 11:07:38 +03:00
|
|
|
keys = append(keys, k)
|
2020-02-23 16:45:20 +03:00
|
|
|
rec.Metadata[k] = fmt.Sprintf("%v", v)
|
|
|
|
}
|
|
|
|
|
2020-03-03 11:07:38 +03:00
|
|
|
sort.Strings(keys)
|
|
|
|
metadata := ""
|
|
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
metadata += fmt.Sprintf(" %s=%v", k, fields[k])
|
|
|
|
}
|
|
|
|
|
2020-02-23 16:45:20 +03:00
|
|
|
dlog.DefaultLog.Write(rec)
|
2020-02-21 10:57:59 +03:00
|
|
|
|
2020-02-23 16:45:20 +03:00
|
|
|
t := rec.Timestamp.Format("2006-01-02 15:04:05")
|
2020-03-03 11:07:38 +03:00
|
|
|
fmt.Printf("%s %s %v\n", t, metadata, rec.Message)
|
2020-02-21 10:57:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *defaultLogger) Options() Options {
|
|
|
|
return n.opts
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewLogger builds a new logger based on options
|
|
|
|
func NewLogger(opts ...Option) Logger {
|
|
|
|
// Default options
|
|
|
|
options := Options{
|
2020-04-26 17:11:53 +03:00
|
|
|
Level: InfoLevel,
|
|
|
|
Fields: make(map[string]interface{}),
|
|
|
|
Out: os.Stderr,
|
|
|
|
CallerSkipCount: 1,
|
|
|
|
Context: context.Background(),
|
2020-02-21 10:57:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
l := &defaultLogger{opts: options}
|
2020-02-24 16:07:40 +03:00
|
|
|
if err := l.Init(opts...); err != nil {
|
|
|
|
l.Log(FatalLevel, err)
|
|
|
|
}
|
|
|
|
|
2020-02-21 10:57:59 +03:00
|
|
|
return l
|
|
|
|
}
|