package logrus
import (
"bytes"
"context"
"fmt"
"os"
"reflect"
"runtime"
"strings"
"sync"
"time"
)
var (
logrusPackage string
minimumCallerDepth int
callerInitOnce sync .Once
)
const (
maximumCallerDepth int = 25
knownLogrusFrames int = 4
)
func init () {
minimumCallerDepth = 1
}
var ErrorKey = "error"
type Entry struct {
Logger *Logger
Data Fields
Time time .Time
Level Level
Caller *runtime .Frame
Message string
Buffer *bytes .Buffer
Context context .Context
err string
}
func NewEntry (logger *Logger ) *Entry {
return &Entry {
Logger : logger ,
Data : make (Fields , 6 ),
}
}
func (entry *Entry ) Dup () *Entry {
data := make (Fields , len (entry .Data ))
for k , v := range entry .Data {
data [k ] = v
}
return &Entry {Logger : entry .Logger , Data : data , Time : entry .Time , Context : entry .Context , err : entry .err }
}
func (entry *Entry ) Bytes () ([]byte , error ) {
return entry .Logger .Formatter .Format (entry )
}
func (entry *Entry ) String () (string , error ) {
serialized , err := entry .Bytes ()
if err != nil {
return "" , err
}
str := string (serialized )
return str , nil
}
func (entry *Entry ) WithError (err error ) *Entry {
return entry .WithField (ErrorKey , err )
}
func (entry *Entry ) WithContext (ctx context .Context ) *Entry {
dataCopy := make (Fields , len (entry .Data ))
for k , v := range entry .Data {
dataCopy [k ] = v
}
return &Entry {Logger : entry .Logger , Data : dataCopy , Time : entry .Time , err : entry .err , Context : ctx }
}
func (entry *Entry ) WithField (key string , value interface {}) *Entry {
return entry .WithFields (Fields {key : value })
}
func (entry *Entry ) WithFields (fields Fields ) *Entry {
data := make (Fields , len (entry .Data )+len (fields ))
for k , v := range entry .Data {
data [k ] = v
}
fieldErr := entry .err
for k , v := range fields {
isErrField := false
if t := reflect .TypeOf (v ); t != nil {
switch {
case t .Kind () == reflect .Func , t .Kind () == reflect .Ptr && t .Elem ().Kind () == reflect .Func :
isErrField = true
}
}
if isErrField {
tmp := fmt .Sprintf ("can not add field %q" , k )
if fieldErr != "" {
fieldErr = entry .err + ", " + tmp
} else {
fieldErr = tmp
}
} else {
data [k ] = v
}
}
return &Entry {Logger : entry .Logger , Data : data , Time : entry .Time , err : fieldErr , Context : entry .Context }
}
func (entry *Entry ) WithTime (t time .Time ) *Entry {
dataCopy := make (Fields , len (entry .Data ))
for k , v := range entry .Data {
dataCopy [k ] = v
}
return &Entry {Logger : entry .Logger , Data : dataCopy , Time : t , err : entry .err , Context : entry .Context }
}
func getPackageName (f string ) string {
for {
lastPeriod := strings .LastIndex (f , "." )
lastSlash := strings .LastIndex (f , "/" )
if lastPeriod > lastSlash {
f = f [:lastPeriod ]
} else {
break
}
}
return f
}
func getCaller () *runtime .Frame {
callerInitOnce .Do (func () {
pcs := make ([]uintptr , maximumCallerDepth )
_ = runtime .Callers (0 , pcs )
for i := 0 ; i < maximumCallerDepth ; i ++ {
funcName := runtime .FuncForPC (pcs [i ]).Name ()
if strings .Contains (funcName , "getCaller" ) {
logrusPackage = getPackageName (funcName )
break
}
}
minimumCallerDepth = knownLogrusFrames
})
pcs := make ([]uintptr , maximumCallerDepth )
depth := runtime .Callers (minimumCallerDepth , pcs )
frames := runtime .CallersFrames (pcs [:depth ])
for f , again := frames .Next (); again ; f , again = frames .Next () {
pkg := getPackageName (f .Function )
if pkg != logrusPackage {
return &f
}
}
return nil
}
func (entry Entry ) HasCaller () (has bool ) {
return entry .Logger != nil &&
entry .Logger .ReportCaller &&
entry .Caller != nil
}
func (entry *Entry ) log (level Level , msg string ) {
var buffer *bytes .Buffer
newEntry := entry .Dup ()
if newEntry .Time .IsZero () {
newEntry .Time = time .Now ()
}
newEntry .Level = level
newEntry .Message = msg
newEntry .Logger .mu .Lock ()
reportCaller := newEntry .Logger .ReportCaller
newEntry .Logger .mu .Unlock ()
if reportCaller {
newEntry .Caller = getCaller ()
}
newEntry .fireHooks ()
buffer = getBuffer ()
defer func () {
newEntry .Buffer = nil
putBuffer (buffer )
}()
buffer .Reset ()
newEntry .Buffer = buffer
newEntry .write ()
newEntry .Buffer = nil
if level <= PanicLevel {
panic (newEntry )
}
}
func (entry *Entry ) fireHooks () {
var tmpHooks LevelHooks
entry .Logger .mu .Lock ()
tmpHooks = make (LevelHooks , len (entry .Logger .Hooks ))
for k , v := range entry .Logger .Hooks {
tmpHooks [k ] = v
}
entry .Logger .mu .Unlock ()
err := tmpHooks .Fire (entry .Level , entry )
if err != nil {
fmt .Fprintf (os .Stderr , "Failed to fire hook: %v\n" , err )
}
}
func (entry *Entry ) write () {
serialized , err := entry .Logger .Formatter .Format (entry )
if err != nil {
fmt .Fprintf (os .Stderr , "Failed to obtain reader, %v\n" , err )
return
}
entry .Logger .mu .Lock ()
defer entry .Logger .mu .Unlock ()
if _ , err := entry .Logger .Out .Write (serialized ); err != nil {
fmt .Fprintf (os .Stderr , "Failed to write to log, %v\n" , err )
}
}
func (entry *Entry ) Log (level Level , args ...interface {}) {
if entry .Logger .IsLevelEnabled (level ) {
entry .log (level , fmt .Sprint (args ...))
}
}
func (entry *Entry ) Trace (args ...interface {}) {
entry .Log (TraceLevel , args ...)
}
func (entry *Entry ) Debug (args ...interface {}) {
entry .Log (DebugLevel , args ...)
}
func (entry *Entry ) Print (args ...interface {}) {
entry .Info (args ...)
}
func (entry *Entry ) Info (args ...interface {}) {
entry .Log (InfoLevel , args ...)
}
func (entry *Entry ) Warn (args ...interface {}) {
entry .Log (WarnLevel , args ...)
}
func (entry *Entry ) Warning (args ...interface {}) {
entry .Warn (args ...)
}
func (entry *Entry ) Error (args ...interface {}) {
entry .Log (ErrorLevel , args ...)
}
func (entry *Entry ) Fatal (args ...interface {}) {
entry .Log (FatalLevel , args ...)
entry .Logger .Exit (1 )
}
func (entry *Entry ) Panic (args ...interface {}) {
entry .Log (PanicLevel , args ...)
}
func (entry *Entry ) Logf (level Level , format string , args ...interface {}) {
if entry .Logger .IsLevelEnabled (level ) {
entry .Log (level , fmt .Sprintf (format , args ...))
}
}
func (entry *Entry ) Tracef (format string , args ...interface {}) {
entry .Logf (TraceLevel , format , args ...)
}
func (entry *Entry ) Debugf (format string , args ...interface {}) {
entry .Logf (DebugLevel , format , args ...)
}
func (entry *Entry ) Infof (format string , args ...interface {}) {
entry .Logf (InfoLevel , format , args ...)
}
func (entry *Entry ) Printf (format string , args ...interface {}) {
entry .Infof (format , args ...)
}
func (entry *Entry ) Warnf (format string , args ...interface {}) {
entry .Logf (WarnLevel , format , args ...)
}
func (entry *Entry ) Warningf (format string , args ...interface {}) {
entry .Warnf (format , args ...)
}
func (entry *Entry ) Errorf (format string , args ...interface {}) {
entry .Logf (ErrorLevel , format , args ...)
}
func (entry *Entry ) Fatalf (format string , args ...interface {}) {
entry .Logf (FatalLevel , format , args ...)
entry .Logger .Exit (1 )
}
func (entry *Entry ) Panicf (format string , args ...interface {}) {
entry .Logf (PanicLevel , format , args ...)
}
func (entry *Entry ) Logln (level Level , args ...interface {}) {
if entry .Logger .IsLevelEnabled (level ) {
entry .Log (level , entry .sprintlnn (args ...))
}
}
func (entry *Entry ) Traceln (args ...interface {}) {
entry .Logln (TraceLevel , args ...)
}
func (entry *Entry ) Debugln (args ...interface {}) {
entry .Logln (DebugLevel , args ...)
}
func (entry *Entry ) Infoln (args ...interface {}) {
entry .Logln (InfoLevel , args ...)
}
func (entry *Entry ) Println (args ...interface {}) {
entry .Infoln (args ...)
}
func (entry *Entry ) Warnln (args ...interface {}) {
entry .Logln (WarnLevel , args ...)
}
func (entry *Entry ) Warningln (args ...interface {}) {
entry .Warnln (args ...)
}
func (entry *Entry ) Errorln (args ...interface {}) {
entry .Logln (ErrorLevel , args ...)
}
func (entry *Entry ) Fatalln (args ...interface {}) {
entry .Logln (FatalLevel , args ...)
entry .Logger .Exit (1 )
}
func (entry *Entry ) Panicln (args ...interface {}) {
entry .Logln (PanicLevel , args ...)
}
func (entry *Entry ) sprintlnn (args ...interface {}) string {
msg := fmt .Sprintln (args ...)
return msg [:len (msg )-1 ]
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .