Refactor Logger to make a logger interface and make it possible to wrap loggers for specific purposes. Co-authored-by: techknowlogick <techknowlogick@gitea.io>tags/v1.15.0-dev
@@ -15,7 +15,7 @@ import ( | |||
// XORMLogBridge a logger bridge from Logger to xorm | |||
type XORMLogBridge struct { | |||
showSQL bool | |||
logger *log.Logger | |||
logger log.Logger | |||
} | |||
// NewXORMLogger inits a log bridge for xorm | |||
@@ -40,7 +40,7 @@ type ElasticSearchIndexer struct { | |||
} | |||
type elasticLogger struct { | |||
*log.Logger | |||
log.Logger | |||
} | |||
func (l elasticLogger) Printf(format string, args ...interface{}) { | |||
@@ -27,11 +27,11 @@ type ElasticSearchIndexer struct { | |||
} | |||
type elasticLogger struct { | |||
*log.Logger | |||
log.LevelLogger | |||
} | |||
func (l elasticLogger) Printf(format string, args ...interface{}) { | |||
_ = l.Logger.Log(2, l.Logger.GetLevel(), format, args...) | |||
_ = l.Log(2, l.GetLevel(), format, args...) | |||
} | |||
// NewElasticSearchIndexer creates a new elasticsearch indexer | |||
@@ -158,15 +158,15 @@ func ColorBytes(attrs ...ColorAttribute) []byte { | |||
return bytes | |||
} | |||
var levelToColor = map[Level]string{ | |||
TRACE: ColorString(Bold, FgCyan), | |||
DEBUG: ColorString(Bold, FgBlue), | |||
INFO: ColorString(Bold, FgGreen), | |||
WARN: ColorString(Bold, FgYellow), | |||
ERROR: ColorString(Bold, FgRed), | |||
CRITICAL: ColorString(Bold, BgMagenta), | |||
FATAL: ColorString(Bold, BgRed), | |||
NONE: ColorString(Reset), | |||
var levelToColor = map[Level][]byte{ | |||
TRACE: ColorBytes(Bold, FgCyan), | |||
DEBUG: ColorBytes(Bold, FgBlue), | |||
INFO: ColorBytes(Bold, FgGreen), | |||
WARN: ColorBytes(Bold, FgYellow), | |||
ERROR: ColorBytes(Bold, FgRed), | |||
CRITICAL: ColorBytes(Bold, BgMagenta), | |||
FATAL: ColorBytes(Bold, BgRed), | |||
NONE: ColorBytes(Reset), | |||
} | |||
var resetBytes = ColorBytes(Reset) | |||
@@ -73,6 +73,16 @@ func (l Level) String() string { | |||
return "info" | |||
} | |||
// Color returns the color string for this Level | |||
func (l Level) Color() *[]byte { | |||
color, ok := levelToColor[l] | |||
if ok { | |||
return &(color) | |||
} | |||
none := levelToColor[NONE] | |||
return &none | |||
} | |||
// MarshalJSON takes a Level and turns it into text | |||
func (l Level) MarshalJSON() ([]byte, error) { | |||
buffer := bytes.NewBufferString(`"`) | |||
@@ -16,16 +16,16 @@ type loggerMap struct { | |||
sync.Map | |||
} | |||
func (m *loggerMap) Load(k string) (*Logger, bool) { | |||
func (m *loggerMap) Load(k string) (*MultiChannelledLogger, bool) { | |||
v, ok := m.Map.Load(k) | |||
if !ok { | |||
return nil, false | |||
} | |||
l, ok := v.(*Logger) | |||
l, ok := v.(*MultiChannelledLogger) | |||
return l, ok | |||
} | |||
func (m *loggerMap) Store(k string, v *Logger) { | |||
func (m *loggerMap) Store(k string, v *MultiChannelledLogger) { | |||
m.Map.Store(k, v) | |||
} | |||
@@ -42,7 +42,7 @@ var ( | |||
) | |||
// NewLogger create a logger for the default logger | |||
func NewLogger(bufLen int64, name, provider, config string) *Logger { | |||
func NewLogger(bufLen int64, name, provider, config string) *MultiChannelledLogger { | |||
err := NewNamedLogger(DEFAULT, bufLen, name, provider, config) | |||
if err != nil { | |||
CriticalWithSkip(1, "Unable to create default logger: %v", err) | |||
@@ -83,7 +83,7 @@ func DelLogger(name string) error { | |||
} | |||
// GetLogger returns either a named logger or the default logger | |||
func GetLogger(name string) *Logger { | |||
func GetLogger(name string) *MultiChannelledLogger { | |||
logger, ok := NamedLoggers.Load(name) | |||
if ok { | |||
return logger | |||
@@ -196,7 +196,7 @@ func IsFatal() bool { | |||
// Pause pauses all the loggers | |||
func Pause() { | |||
NamedLoggers.Range(func(key, value interface{}) bool { | |||
logger := value.(*Logger) | |||
logger := value.(*MultiChannelledLogger) | |||
logger.Pause() | |||
logger.Flush() | |||
return true | |||
@@ -206,7 +206,7 @@ func Pause() { | |||
// Resume resumes all the loggers | |||
func Resume() { | |||
NamedLoggers.Range(func(key, value interface{}) bool { | |||
logger := value.(*Logger) | |||
logger := value.(*MultiChannelledLogger) | |||
logger.Resume() | |||
return true | |||
}) | |||
@@ -216,7 +216,7 @@ func Resume() { | |||
func ReleaseReopen() error { | |||
var accumulatedErr error | |||
NamedLoggers.Range(func(key, value interface{}) bool { | |||
logger := value.(*Logger) | |||
logger := value.(*MultiChannelledLogger) | |||
if err := logger.ReleaseReopen(); err != nil { | |||
if accumulatedErr == nil { | |||
accumulatedErr = fmt.Errorf("Error reopening %s: %v", key.(string), err) | |||
@@ -250,15 +250,15 @@ func Log(skip int, level Level, format string, v ...interface{}) { | |||
// LoggerAsWriter is a io.Writer shim around the gitea log | |||
type LoggerAsWriter struct { | |||
ourLoggers []*Logger | |||
ourLoggers []*MultiChannelledLogger | |||
level Level | |||
} | |||
// NewLoggerAsWriter creates a Writer representation of the logger with setable log level | |||
func NewLoggerAsWriter(level string, ourLoggers ...*Logger) *LoggerAsWriter { | |||
func NewLoggerAsWriter(level string, ourLoggers ...*MultiChannelledLogger) *LoggerAsWriter { | |||
if len(ourLoggers) == 0 { | |||
l, _ := NamedLoggers.Load(DEFAULT) | |||
ourLoggers = []*Logger{l} | |||
ourLoggers = []*MultiChannelledLogger{l} | |||
} | |||
l := &LoggerAsWriter{ | |||
ourLoggers: ourLoggers, | |||
@@ -11,7 +11,7 @@ import ( | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func baseConsoleTest(t *testing.T, logger *Logger) (chan []byte, chan bool) { | |||
func baseConsoleTest(t *testing.T, logger *MultiChannelledLogger) (chan []byte, chan bool) { | |||
written := make(chan []byte) | |||
closed := make(chan bool) | |||
@@ -4,149 +4,140 @@ | |||
package log | |||
import ( | |||
"fmt" | |||
"os" | |||
"runtime" | |||
"strings" | |||
"time" | |||
) | |||
// Logger is default logger in the Gitea application. | |||
// it can contain several providers and log message into all providers. | |||
type Logger struct { | |||
*MultiChannelledLog | |||
bufferLength int64 | |||
} | |||
// newLogger initializes and returns a new logger. | |||
func newLogger(name string, buffer int64) *Logger { | |||
l := &Logger{ | |||
MultiChannelledLog: NewMultiChannelledLog(name, buffer), | |||
bufferLength: buffer, | |||
} | |||
return l | |||
} | |||
// SetLogger sets new logger instance with given logger provider and config. | |||
func (l *Logger) SetLogger(name, provider, config string) error { | |||
eventLogger, err := NewChannelledLog(name, provider, config, l.bufferLength) | |||
if err != nil { | |||
return fmt.Errorf("Failed to create sublogger (%s): %v", name, err) | |||
} | |||
l.MultiChannelledLog.DelLogger(name) | |||
err = l.MultiChannelledLog.AddLogger(eventLogger) | |||
if err != nil { | |||
if IsErrDuplicateName(err) { | |||
return fmt.Errorf("Duplicate named sublogger %s %v", name, l.MultiChannelledLog.GetEventLoggerNames()) | |||
} | |||
return fmt.Errorf("Failed to add sublogger (%s): %v", name, err) | |||
} | |||
return nil | |||
} | |||
// DelLogger deletes a sublogger from this logger. | |||
func (l *Logger) DelLogger(name string) (bool, error) { | |||
return l.MultiChannelledLog.DelLogger(name), nil | |||
} | |||
// Log msg at the provided level with the provided caller defined by skip (0 being the function that calls this function) | |||
func (l *Logger) Log(skip int, level Level, format string, v ...interface{}) error { | |||
if l.GetLevel() > level { | |||
return nil | |||
} | |||
caller := "?()" | |||
pc, filename, line, ok := runtime.Caller(skip + 1) | |||
if ok { | |||
// Get caller function name. | |||
fn := runtime.FuncForPC(pc) | |||
if fn != nil { | |||
caller = fn.Name() + "()" | |||
} | |||
} | |||
msg := format | |||
if len(v) > 0 { | |||
msg = ColorSprintf(format, v...) | |||
} | |||
stack := "" | |||
if l.GetStacktraceLevel() <= level { | |||
stack = Stack(skip + 1) | |||
} | |||
return l.SendLog(level, caller, strings.TrimPrefix(filename, prefix), line, msg, stack) | |||
} | |||
// SendLog sends a log event at the provided level with the information given | |||
func (l *Logger) SendLog(level Level, caller, filename string, line int, msg string, stack string) error { | |||
if l.GetLevel() > level { | |||
return nil | |||
} | |||
event := &Event{ | |||
level: level, | |||
caller: caller, | |||
filename: filename, | |||
line: line, | |||
msg: msg, | |||
time: time.Now(), | |||
stacktrace: stack, | |||
} | |||
l.LogEvent(event) | |||
return nil | |||
import "os" | |||
// Logger is the basic interface for logging | |||
type Logger interface { | |||
LevelLogger | |||
Trace(format string, v ...interface{}) | |||
IsTrace() bool | |||
Debug(format string, v ...interface{}) | |||
IsDebug() bool | |||
Info(format string, v ...interface{}) | |||
IsInfo() bool | |||
Warn(format string, v ...interface{}) | |||
IsWarn() bool | |||
Error(format string, v ...interface{}) | |||
ErrorWithSkip(skip int, format string, v ...interface{}) | |||
IsError() bool | |||
Critical(format string, v ...interface{}) | |||
CriticalWithSkip(skip int, format string, v ...interface{}) | |||
IsCritical() bool | |||
Fatal(format string, v ...interface{}) | |||
FatalWithSkip(skip int, format string, v ...interface{}) | |||
IsFatal() bool | |||
} | |||
// LevelLogger is the simplest logging interface | |||
type LevelLogger interface { | |||
Flush() | |||
Close() | |||
GetLevel() Level | |||
Log(skip int, level Level, format string, v ...interface{}) error | |||
} | |||
// SettableLogger is the interface of loggers which have subloggers | |||
type SettableLogger interface { | |||
SetLogger(name, provider, config string) error | |||
DelLogger(name string) (bool, error) | |||
} | |||
// StacktraceLogger is a logger that can log stacktraces | |||
type StacktraceLogger interface { | |||
GetStacktraceLevel() Level | |||
} | |||
// LevelLoggerLogger wraps a LevelLogger as a Logger | |||
type LevelLoggerLogger struct { | |||
LevelLogger | |||
} | |||
// Trace records trace log | |||
func (l *Logger) Trace(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Trace(format string, v ...interface{}) { | |||
l.Log(1, TRACE, format, v...) | |||
} | |||
// IsTrace returns true if the logger is TRACE | |||
func (l *LevelLoggerLogger) IsTrace() bool { | |||
return l.GetLevel() <= TRACE | |||
} | |||
// Debug records debug log | |||
func (l *Logger) Debug(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Debug(format string, v ...interface{}) { | |||
l.Log(1, DEBUG, format, v...) | |||
} | |||
// IsDebug returns true if the logger is DEBUG | |||
func (l *LevelLoggerLogger) IsDebug() bool { | |||
return l.GetLevel() <= DEBUG | |||
} | |||
// Info records information log | |||
func (l *Logger) Info(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Info(format string, v ...interface{}) { | |||
l.Log(1, INFO, format, v...) | |||
} | |||
// IsInfo returns true if the logger is INFO | |||
func (l *LevelLoggerLogger) IsInfo() bool { | |||
return l.GetLevel() <= INFO | |||
} | |||
// Warn records warning log | |||
func (l *Logger) Warn(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Warn(format string, v ...interface{}) { | |||
l.Log(1, WARN, format, v...) | |||
} | |||
// IsWarn returns true if the logger is WARN | |||
func (l *LevelLoggerLogger) IsWarn() bool { | |||
return l.GetLevel() <= WARN | |||
} | |||
// Error records error log | |||
func (l *Logger) Error(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Error(format string, v ...interface{}) { | |||
l.Log(1, ERROR, format, v...) | |||
} | |||
// ErrorWithSkip records error log from "skip" calls back from this function | |||
func (l *Logger) ErrorWithSkip(skip int, format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) ErrorWithSkip(skip int, format string, v ...interface{}) { | |||
l.Log(skip+1, ERROR, format, v...) | |||
} | |||
// IsError returns true if the logger is ERROR | |||
func (l *LevelLoggerLogger) IsError() bool { | |||
return l.GetLevel() <= ERROR | |||
} | |||
// Critical records critical log | |||
func (l *Logger) Critical(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Critical(format string, v ...interface{}) { | |||
l.Log(1, CRITICAL, format, v...) | |||
} | |||
// CriticalWithSkip records critical log from "skip" calls back from this function | |||
func (l *Logger) CriticalWithSkip(skip int, format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) CriticalWithSkip(skip int, format string, v ...interface{}) { | |||
l.Log(skip+1, CRITICAL, format, v...) | |||
} | |||
// IsCritical returns true if the logger is CRITICAL | |||
func (l *LevelLoggerLogger) IsCritical() bool { | |||
return l.GetLevel() <= CRITICAL | |||
} | |||
// Fatal records fatal log and exit the process | |||
func (l *Logger) Fatal(format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) Fatal(format string, v ...interface{}) { | |||
l.Log(1, FATAL, format, v...) | |||
l.Close() | |||
os.Exit(1) | |||
} | |||
// FatalWithSkip records fatal log from "skip" calls back from this function and exits the process | |||
func (l *Logger) FatalWithSkip(skip int, format string, v ...interface{}) { | |||
func (l *LevelLoggerLogger) FatalWithSkip(skip int, format string, v ...interface{}) { | |||
l.Log(skip+1, FATAL, format, v...) | |||
l.Close() | |||
os.Exit(1) | |||
} | |||
// IsFatal returns true if the logger is FATAL | |||
func (l *LevelLoggerLogger) IsFatal() bool { | |||
return l.GetLevel() <= FATAL | |||
} |
@@ -0,0 +1,98 @@ | |||
// Copyright 2020 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package log | |||
import ( | |||
"fmt" | |||
"runtime" | |||
"strings" | |||
"time" | |||
) | |||
// MultiChannelledLogger is default logger in the Gitea application. | |||
// it can contain several providers and log message into all providers. | |||
type MultiChannelledLogger struct { | |||
LevelLoggerLogger | |||
*MultiChannelledLog | |||
bufferLength int64 | |||
} | |||
// newLogger initializes and returns a new logger. | |||
func newLogger(name string, buffer int64) *MultiChannelledLogger { | |||
l := &MultiChannelledLogger{ | |||
MultiChannelledLog: NewMultiChannelledLog(name, buffer), | |||
bufferLength: buffer, | |||
} | |||
l.LevelLogger = l | |||
return l | |||
} | |||
// SetLogger sets new logger instance with given logger provider and config. | |||
func (l *MultiChannelledLogger) SetLogger(name, provider, config string) error { | |||
eventLogger, err := NewChannelledLog(name, provider, config, l.bufferLength) | |||
if err != nil { | |||
return fmt.Errorf("Failed to create sublogger (%s): %v", name, err) | |||
} | |||
l.MultiChannelledLog.DelLogger(name) | |||
err = l.MultiChannelledLog.AddLogger(eventLogger) | |||
if err != nil { | |||
if IsErrDuplicateName(err) { | |||
return fmt.Errorf("Duplicate named sublogger %s %v", name, l.MultiChannelledLog.GetEventLoggerNames()) | |||
} | |||
return fmt.Errorf("Failed to add sublogger (%s): %v", name, err) | |||
} | |||
return nil | |||
} | |||
// DelLogger deletes a sublogger from this logger. | |||
func (l *MultiChannelledLogger) DelLogger(name string) (bool, error) { | |||
return l.MultiChannelledLog.DelLogger(name), nil | |||
} | |||
// Log msg at the provided level with the provided caller defined by skip (0 being the function that calls this function) | |||
func (l *MultiChannelledLogger) Log(skip int, level Level, format string, v ...interface{}) error { | |||
if l.GetLevel() > level { | |||
return nil | |||
} | |||
caller := "?()" | |||
pc, filename, line, ok := runtime.Caller(skip + 1) | |||
if ok { | |||
// Get caller function name. | |||
fn := runtime.FuncForPC(pc) | |||
if fn != nil { | |||
caller = fn.Name() + "()" | |||
} | |||
} | |||
msg := format | |||
if len(v) > 0 { | |||
msg = ColorSprintf(format, v...) | |||
} | |||
stack := "" | |||
if l.GetStacktraceLevel() <= level { | |||
stack = Stack(skip + 1) | |||
} | |||
return l.SendLog(level, caller, strings.TrimPrefix(filename, prefix), line, msg, stack) | |||
} | |||
// SendLog sends a log event at the provided level with the information given | |||
func (l *MultiChannelledLogger) SendLog(level Level, caller, filename string, line int, msg string, stack string) error { | |||
if l.GetLevel() > level { | |||
return nil | |||
} | |||
event := &Event{ | |||
level: level, | |||
caller: caller, | |||
filename: filename, | |||
line: line, | |||
msg: msg, | |||
time: time.Now(), | |||
stacktrace: stack, | |||
} | |||
l.LogEvent(event) | |||
return nil | |||
} |