logger with helper methods (#1216)

* support unix daemon socket

* refactor(logger): logger fields changed to map[string]interface{}

* improvement(logger): adding string to Level Parser

* improvement(logger): rename ParseLevel to GetLevel

* refactor(logger): adding basic logger

adding micro default logger, and refactor logger interface

* refactor(logger): moved basic logger to top level package

* refactor(logger): adding default logger
This commit is contained in:
Sumanth Chinthagunta 2020-02-20 23:57:59 -08:00 committed by GitHub
parent 88457b812e
commit 3fa7c26946
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 196 additions and 58 deletions

96
logger/default.go Normal file
View File

@ -0,0 +1,96 @@
package logger
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
)
type defaultLogger struct {
opts Options
err error
}
// 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 {
l.opts.Fields = fields
return l
}
func (l *defaultLogger) Error(err error) Logger {
l.err = err
return l
}
func (l *defaultLogger) Log(level Level, v ...interface{}) {
if !l.opts.Level.Enabled(level) {
return
}
msg := fmt.Sprint(v...)
fields := l.opts.Fields
fields["level"] = level.String()
fields["message"] = msg
if l.err != nil {
fields["error"] = l.err.Error()
}
enc := json.NewEncoder(l.opts.Out)
if err := enc.Encode(fields); err != nil {
log.Fatal(err)
}
}
func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
if level < l.opts.Level {
return
}
msg := fmt.Sprintf(format, v...)
fields := l.opts.Fields
fields["level"] = level.String()
fields["message"] = msg
if l.err != nil {
fields["error"] = l.err.Error()
}
enc := json.NewEncoder(l.opts.Out)
if err := enc.Encode(fields); err != nil {
log.Fatal(err)
}
}
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{
Level: InfoLevel,
Fields: make(map[string]interface{}),
Out: os.Stderr,
Context: context.Background(),
}
l := &defaultLogger{opts: options}
_ = l.Init(opts...)
return l
}

View File

@ -1,14 +1,24 @@
package logger package logger
import "fmt"
type Level int8 type Level int8
const ( const (
TraceLevel Level = iota - 1 // TraceLevel level. Designates finer-grained informational events than the Debug.
TraceLevel Level = iota - 2
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
DebugLevel DebugLevel
// InfoLevel is the default logging priority.
// General operational entries about what's going on inside the application.
InfoLevel InfoLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel WarnLevel
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
ErrorLevel ErrorLevel
// PanicLevel level, logs the message and then panics.
PanicLevel PanicLevel
// FatalLevel level. Logs and then calls `logger.Exit(1)`. highest level of severity.
FatalLevel FatalLevel
) )
@ -24,10 +34,37 @@ func (l Level) String() string {
return "warn" return "warn"
case ErrorLevel: case ErrorLevel:
return "error" return "error"
case FatalLevel:
return "fatal"
case PanicLevel: case PanicLevel:
return "panic" return "panic"
case FatalLevel:
return "fatal"
} }
return "" return ""
} }
// Enabled returns true if the given level is at or above this level.
func (l Level) Enabled(lvl Level) bool {
return lvl >= l
}
// GetLevel converts a level string into a logger Level value.
// returns an error if the input string does not match known values.
func GetLevel(levelStr string) (Level, error) {
switch levelStr {
case TraceLevel.String():
return TraceLevel, nil
case DebugLevel.String():
return DebugLevel, nil
case InfoLevel.String():
return InfoLevel, nil
case WarnLevel.String():
return WarnLevel, nil
case ErrorLevel.String():
return ErrorLevel, nil
case PanicLevel.String():
return PanicLevel, nil
case FatalLevel.String():
return FatalLevel, nil
}
return InfoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
}

View File

@ -1,9 +1,9 @@
// Package log provides a log interface // Package log provides a log interface
package logger package logger
import ( var (
"fmt" // Default logger
"sync" DefaultLogger Logger = NewLogger()
) )
// Logger is a generic logging interface // Logger is a generic logging interface
@ -24,45 +24,32 @@ type Logger interface {
String() string String() string
} }
var ( func Init(opts ...Option) error {
mtx sync.Mutex return DefaultLogger.Init(opts...)
loggerMap = map[string]Logger{}
)
func Register(logger Logger) {
mtx.Lock()
defer mtx.Unlock()
loggerMap[logger.String()] = logger
} }
func GetLogger(name string) (Logger, error) { func Error(err error) Logger {
l := loggerMap[name] return DefaultLogger.Error(err)
if l == nil { }
return nil, fmt.Errorf("no such name logger found %s", name)
func Fields(fields map[string]interface{}) Logger {
return DefaultLogger.Fields(fields)
}
func Log(level Level, v ...interface{}) {
DefaultLogger.Log(level, v...)
}
func Logf(level Level, format string, v ...interface{}) {
DefaultLogger.Logf(level, format, v...)
}
func SetGlobalLevel(lvl Level) {
if err := Init(WithLevel(lvl)); err != nil {
print(err)
} }
return l, nil
} }
// GetLevel converts a level string into a logger Level value. func String() string {
// returns an error if the input string does not match known values. return DefaultLogger.String()
func GetLevel(levelStr string) (Level, error) {
switch levelStr {
case TraceLevel.String():
return TraceLevel, nil
case DebugLevel.String():
return DebugLevel, nil
case InfoLevel.String():
return InfoLevel, nil
case WarnLevel.String():
return WarnLevel, nil
case ErrorLevel.String():
return ErrorLevel, nil
case FatalLevel.String():
return FatalLevel, nil
case PanicLevel.String():
return PanicLevel, nil
}
return InfoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
} }

View File

@ -2,30 +2,48 @@ package logger
import ( import (
"context" "context"
"io"
) )
// Option for load profiles maybe
// eg. yml
// micro:
// logger:
// name:
// dialect: zap/default/logrus
// zap:
// xxx:
// logrus:
// xxx:
type Option func(*Options) type Option func(*Options)
type Options struct { type Options struct {
// The Log Level // The logging level the logger should log at. default is `InfoLevel`
Level Level Level Level
// Other opts // fields to always be logged
Fields map[string]interface{}
// It's common to set this to a file, or leave it default which is `os.Stderr`
Out io.Writer
// Alternative options
Context context.Context Context context.Context
} }
// WithLevel sets the log level // WithFields set default fields for the logger
func WithLevel(l Level) Option { func WithFields(fields map[string]interface{}) Option {
return func(o *Options) { return func(args *Options) {
o.Level = l args.Fields = fields
}
}
// WithLevel set default level for the logger
func WithLevel(level Level) Option {
return func(args *Options) {
args.Level = level
}
}
// WithOutput set default output writer for the logger
func WithOutput(out io.Writer) Option {
return func(args *Options) {
args.Out = out
}
}
func SetOption(k, v interface{}) Option {
return func(o *Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
} }
} }