Marvin Preuss
d095180eb4
All checks were successful
continuous-integration/drone/push Build is passing
183 lines
4.1 KiB
Go
183 lines
4.1 KiB
Go
package log
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// assert interface compliance.
|
|
var _ Interface = (*Entry)(nil)
|
|
|
|
// Now returns the current time.
|
|
var Now = time.Now
|
|
|
|
// Entry represents a single log entry.
|
|
type Entry struct {
|
|
Logger *Logger `json:"-"`
|
|
Fields Fields `json:"fields"`
|
|
Level Level `json:"level"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Message string `json:"message"`
|
|
start time.Time
|
|
fields []Fields
|
|
}
|
|
|
|
// NewEntry returns a new entry for `log`.
|
|
func NewEntry(log *Logger) *Entry {
|
|
return &Entry{
|
|
Logger: log,
|
|
}
|
|
}
|
|
|
|
// WithFields returns a new entry with `fields` set.
|
|
func (e *Entry) WithFields(fields Fielder) *Entry {
|
|
f := []Fields{}
|
|
f = append(f, e.fields...)
|
|
f = append(f, fields.Fields())
|
|
return &Entry{
|
|
Logger: e.Logger,
|
|
fields: f,
|
|
}
|
|
}
|
|
|
|
// WithField returns a new entry with the `key` and `value` set.
|
|
func (e *Entry) WithField(key string, value interface{}) *Entry {
|
|
return e.WithFields(Fields{key: value})
|
|
}
|
|
|
|
// WithDuration returns a new entry with the "duration" field set
|
|
// to the given duration in milliseconds.
|
|
func (e *Entry) WithDuration(d time.Duration) *Entry {
|
|
return e.WithField("duration", d.Milliseconds())
|
|
}
|
|
|
|
// WithError returns a new entry with the "error" set to `err`.
|
|
//
|
|
// The given error may implement .Fielder, if it does the method
|
|
// will add all its `.Fields()` into the returned entry.
|
|
func (e *Entry) WithError(err error) *Entry {
|
|
if err == nil {
|
|
return e
|
|
}
|
|
|
|
ctx := e.WithField("error", err.Error())
|
|
|
|
if s, ok := err.(stackTracer); ok {
|
|
frame := s.StackTrace()[0]
|
|
|
|
name := fmt.Sprintf("%n", frame)
|
|
file := fmt.Sprintf("%+s", frame)
|
|
line := fmt.Sprintf("%d", frame)
|
|
|
|
parts := strings.Split(file, "\n\t")
|
|
if len(parts) > 1 {
|
|
file = parts[1]
|
|
}
|
|
|
|
ctx = ctx.WithField("source", fmt.Sprintf("%s: %s:%s", name, file, line))
|
|
}
|
|
|
|
if f, ok := err.(Fielder); ok {
|
|
ctx = ctx.WithFields(f.Fields())
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
// Debug level message.
|
|
func (e *Entry) Debug(msg string) {
|
|
e.Logger.log(DebugLevel, e, msg)
|
|
}
|
|
|
|
// Info level message.
|
|
func (e *Entry) Info(msg string) {
|
|
e.Logger.log(InfoLevel, e, msg)
|
|
}
|
|
|
|
// Warn level message.
|
|
func (e *Entry) Warn(msg string) {
|
|
e.Logger.log(WarnLevel, e, msg)
|
|
}
|
|
|
|
// Error level message.
|
|
func (e *Entry) Error(msg string) {
|
|
e.Logger.log(ErrorLevel, e, msg)
|
|
}
|
|
|
|
// Fatal level message, followed by an exit.
|
|
func (e *Entry) Fatal(msg string) {
|
|
e.Logger.log(FatalLevel, e, msg)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Debugf level formatted message.
|
|
func (e *Entry) Debugf(msg string, v ...interface{}) {
|
|
e.Debug(fmt.Sprintf(msg, v...))
|
|
}
|
|
|
|
// Infof level formatted message.
|
|
func (e *Entry) Infof(msg string, v ...interface{}) {
|
|
e.Info(fmt.Sprintf(msg, v...))
|
|
}
|
|
|
|
// Warnf level formatted message.
|
|
func (e *Entry) Warnf(msg string, v ...interface{}) {
|
|
e.Warn(fmt.Sprintf(msg, v...))
|
|
}
|
|
|
|
// Errorf level formatted message.
|
|
func (e *Entry) Errorf(msg string, v ...interface{}) {
|
|
e.Error(fmt.Sprintf(msg, v...))
|
|
}
|
|
|
|
// Fatalf level formatted message, followed by an exit.
|
|
func (e *Entry) Fatalf(msg string, v ...interface{}) {
|
|
e.Fatal(fmt.Sprintf(msg, v...))
|
|
}
|
|
|
|
// Trace returns a new entry with a Stop method to fire off
|
|
// a corresponding completion log, useful with defer.
|
|
func (e *Entry) Trace(msg string) *Entry {
|
|
e.Info(msg)
|
|
v := e.WithFields(e.Fields)
|
|
v.Message = msg
|
|
v.start = time.Now()
|
|
return v
|
|
}
|
|
|
|
// Stop should be used with Trace, to fire off the completion message. When
|
|
// an `err` is passed the "error" field is set, and the log level is error.
|
|
func (e *Entry) Stop(err *error) {
|
|
if err == nil || *err == nil {
|
|
e.WithDuration(time.Since(e.start)).Info(e.Message)
|
|
} else {
|
|
e.WithDuration(time.Since(e.start)).WithError(*err).Error(e.Message)
|
|
}
|
|
}
|
|
|
|
// mergedFields returns the fields list collapsed into a single map.
|
|
func (e *Entry) mergedFields() Fields {
|
|
f := Fields{}
|
|
|
|
for _, fields := range e.fields {
|
|
for k, v := range fields {
|
|
f[k] = v
|
|
}
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
// finalize returns a copy of the Entry with Fields merged.
|
|
func (e *Entry) finalize(level Level, msg string) *Entry {
|
|
return &Entry{
|
|
Logger: e.Logger,
|
|
Fields: e.mergedFields(),
|
|
Level: level,
|
|
Message: msg,
|
|
Timestamp: Now(),
|
|
}
|
|
}
|