mirror of
https://github.com/go-micro/go-micro.git
synced 2026-04-30 19:15:24 +02:00
181 lines
3.5 KiB
Go
181 lines
3.5 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL"))
|
|
if err != nil {
|
|
lvl = InfoLevel
|
|
}
|
|
|
|
DefaultLogger = NewLogger(WithLevel(lvl))
|
|
}
|
|
|
|
type defaultLogger struct {
|
|
opts Options
|
|
slog *slog.Logger
|
|
sync.RWMutex
|
|
}
|
|
|
|
// Init (opts...) should only overwrite provided options.
|
|
func (l *defaultLogger) Init(opts ...Option) error {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
|
|
for _, o := range opts {
|
|
o(&l.opts)
|
|
}
|
|
|
|
// Recreate slog logger with new options
|
|
handlerOpts := &slog.HandlerOptions{
|
|
Level: l.opts.Level.ToSlog(),
|
|
AddSource: true,
|
|
}
|
|
|
|
// Create text handler for stdout
|
|
textHandler := slog.NewTextHandler(l.opts.Out, handlerOpts)
|
|
|
|
// Create debug log handler for debug/log buffer
|
|
debugHandler := newDebugLogHandler(handlerOpts.Level)
|
|
|
|
// Combine both handlers
|
|
handler := newMultiHandler(textHandler, debugHandler)
|
|
|
|
l.slog = slog.New(handler)
|
|
|
|
// Add fields if any
|
|
if len(l.opts.Fields) > 0 {
|
|
const fieldsPerKV = 2
|
|
args := make([]any, 0, len(l.opts.Fields)*fieldsPerKV)
|
|
for k, v := range l.opts.Fields {
|
|
args = append(args, k, v)
|
|
}
|
|
|
|
l.slog = l.slog.With(args...)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (l *defaultLogger) String() string {
|
|
return "default"
|
|
}
|
|
|
|
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
|
l.RLock()
|
|
nfields := copyFields(l.opts.Fields)
|
|
opts := l.opts
|
|
l.RUnlock()
|
|
|
|
for k, v := range fields {
|
|
nfields[k] = v
|
|
}
|
|
|
|
// Create new logger without locks
|
|
newLogger := NewLogger(
|
|
WithLevel(opts.Level),
|
|
WithFields(nfields),
|
|
WithOutput(opts.Out),
|
|
WithCallerSkipCount(opts.CallerSkipCount),
|
|
)
|
|
|
|
return newLogger
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (l *defaultLogger) Log(level Level, v ...interface{}) {
|
|
// TODO decide does we need to write message if log level not used?
|
|
if !l.opts.Level.Enabled(level) {
|
|
return
|
|
}
|
|
|
|
l.RLock()
|
|
slogger := l.slog
|
|
|
|
if slogger == nil {
|
|
// Fallback if not initialized
|
|
slogger = slog.Default()
|
|
}
|
|
l.RUnlock()
|
|
|
|
// Get caller information
|
|
var pcs [1]uintptr
|
|
|
|
runtime.Callers(l.opts.CallerSkipCount, pcs[:])
|
|
r := slog.NewRecord(time.Now(), level.ToSlog(), fmt.Sprint(v...), pcs[0])
|
|
|
|
_ = slogger.Handler().Handle(context.Background(), r)
|
|
}
|
|
|
|
func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
|
|
// TODO decide does we need to write message if log level not used?
|
|
if !l.opts.Level.Enabled(level) {
|
|
return
|
|
}
|
|
|
|
l.RLock()
|
|
slogger := l.slog
|
|
|
|
if slogger == nil {
|
|
// Fallback if not initialized
|
|
slogger = slog.Default()
|
|
}
|
|
l.RUnlock()
|
|
|
|
// Get caller information
|
|
var pcs [1]uintptr
|
|
|
|
runtime.Callers(l.opts.CallerSkipCount, pcs[:])
|
|
r := slog.NewRecord(time.Now(), level.ToSlog(), fmt.Sprintf(format, v...), pcs[0])
|
|
|
|
_ = slogger.Handler().Handle(context.Background(), r)
|
|
}
|
|
|
|
func (l *defaultLogger) Options() Options {
|
|
// not guard against options Context values
|
|
l.RLock()
|
|
defer l.RUnlock()
|
|
|
|
opts := l.opts
|
|
opts.Fields = copyFields(l.opts.Fields)
|
|
|
|
return opts
|
|
}
|
|
|
|
// NewLogger builds a new logger based on options.
|
|
func NewLogger(opts ...Option) Logger {
|
|
// Default options
|
|
const defaultCallerSkipCount = 2
|
|
|
|
options := Options{
|
|
Level: InfoLevel,
|
|
Fields: make(map[string]interface{}),
|
|
Out: os.Stderr,
|
|
CallerSkipCount: defaultCallerSkipCount,
|
|
Context: context.Background(),
|
|
}
|
|
|
|
l := &defaultLogger{opts: options}
|
|
if err := l.Init(opts...); err != nil {
|
|
l.Log(FatalLevel, err)
|
|
}
|
|
|
|
return l
|
|
}
|