Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/go_modules/github.com/sirupsen/…
Browse files Browse the repository at this point in the history
…logrus-1.9.3
  • Loading branch information
alfrunes committed Jul 12, 2023
2 parents 3919b3c + 85a5f59 commit 2834acf
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 26 deletions.
157 changes: 139 additions & 18 deletions log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ package log

import (
"context"
"fmt"
"io"
"os"
"path"
"runtime"
"strconv"
"strings"
"time"

"github.com/sirupsen/logrus"
)
Expand All @@ -44,6 +49,20 @@ var (
Log = logrus.New()
)

const (
envLogFormat = "LOG_FORMAT"
envLogLevel = "LOG_LEVEL"
envLogDisableCaller = "LOG_DISABLE_CALLER_CONTEXT"

logFormatJSON = "json"
logFormatJSONAlt = "ndjson"

logFieldCaller = "caller"
logFieldCallerFmt = "%s@%s:%d"

pkgSirupsen = "github.com/sirupsen/logrus"
)

type loggerContextKeyType int

const (
Expand All @@ -59,17 +78,89 @@ type ContextLogger interface {

// init initializes the global logger to sane defaults.
func init() {
Log.Formatter = &logrus.TextFormatter{
FullTimestamp: true,
var opts Options
switch strings.ToLower(os.Getenv(envLogFormat)) {
case logFormatJSON, logFormatJSONAlt:
opts.Format = FormatJSON
default:
opts.Format = FormatConsole
}
opts.Level = Level(logrus.InfoLevel)
if lvl := os.Getenv(envLogLevel); lvl != "" {
logLevel, err := logrus.ParseLevel(lvl)
if err == nil {
opts.Level = Level(logLevel)
}
}
Log.Level = logrus.InfoLevel
Log.Hooks.Add(ContextHook{})
opts.TimestampFormat = time.RFC3339
opts.DisableCaller, _ = strconv.ParseBool(os.Getenv(envLogDisableCaller))
Configure(opts)

Log.ExitFunc = func(int) {}
}

type Level logrus.Level

const (
LevelPanic = Level(logrus.PanicLevel)
LevelFatal = Level(logrus.FatalLevel)
LevelError = Level(logrus.ErrorLevel)
LevelWarn = Level(logrus.WarnLevel)
LevelInfo = Level(logrus.InfoLevel)
LevelDebug = Level(logrus.DebugLevel)
LevelTrace = Level(logrus.TraceLevel)
)

type Format int

const (
FormatConsole Format = iota
FormatJSON
)

type Options struct {
TimestampFormat string

Level Level

DisableCaller bool

Format Format

Output io.Writer
}

func Configure(opts Options) {
Log = logrus.New()

if opts.Output != nil {
Log.SetOutput(opts.Output)
}
Log.SetLevel(logrus.Level(opts.Level))

if !opts.DisableCaller {
Log.AddHook(ContextHook{})
}

var formatter logrus.Formatter

switch opts.Format {
case FormatConsole:
formatter = &logrus.TextFormatter{
FullTimestamp: true,
TimestampFormat: opts.TimestampFormat,
}
case FormatJSON:
formatter = &logrus.JSONFormatter{
TimestampFormat: opts.TimestampFormat,
}
}
Log.Formatter = formatter
}

// Setup allows to override the global logger setup.
func Setup(debug bool) {
if debug == true {
if debug {
Log.Level = logrus.DebugLevel
}
}
Expand Down Expand Up @@ -121,27 +212,57 @@ func (hook ContextHook) Levels() []logrus.Level {
return logrus.AllLevels
}

func fmtCaller(frame runtime.Frame) string {
return fmt.Sprintf(
logFieldCallerFmt,
path.Base(frame.Function),
path.Base(frame.File),
frame.Line,
)
}

func (hook ContextHook) Fire(entry *logrus.Entry) error {
//'skip' = 6 is the default call stack skip, which
//works ootb when Error(), Warn(), etc. are called
//for Errorf(), Warnf(), etc. - we have to skip 1 lvl up
for skip := 6; skip < 8; skip++ {
if pc, file, line, ok := runtime.Caller(skip); ok {
funcName := runtime.FuncForPC(pc).Name()

//detect if we're still in logrus (formatting funcs)
if !strings.Contains(funcName, "github.com/sirupsen/logrus") {
entry.Data["file"] = path.Base(file)
entry.Data["func"] = path.Base(funcName)
entry.Data["line"] = line
const (
minCallDepth = 6 // logrus.Logger.Log
maxCallDepth = 8 // logrus.Logger.<Level>f
)
var pcs [1 + maxCallDepth - minCallDepth]uintptr
if _, ok := entry.Data[logFieldCaller]; !ok {
// We don't know how deep we are in the callstack since the hook can be fired
// at different levels. Search between depth 6 -> 8.
i := runtime.Callers(minCallDepth, pcs[:])
frames := runtime.CallersFrames(pcs[:i])
var caller *runtime.Frame
for frame, _ := frames.Next(); frame.PC != 0; frame, _ = frames.Next() {
if !strings.HasPrefix(frame.Function, pkgSirupsen) {
caller = &frame
break
}
}
if caller != nil {
entry.Data[logFieldCaller] = fmtCaller(*caller)
}
}

return nil
}

// WithCallerContext returns a new logger with caller set to the parent caller
// context. The skipParents select how many caller contexts to skip, a value of
// 0 sets the context to the caller of this function.
func (l *Logger) WithCallerContext(skipParents int) *Logger {
const calleeDepth = 2
var pc [1]uintptr
newEntry := l
i := runtime.Callers(calleeDepth+skipParents, pc[:])
frame, _ := runtime.CallersFrames(pc[:i]).
Next()
if frame.Func != nil {
newEntry = &Logger{Entry: l.Dup()}
newEntry.Data[logFieldCaller] = fmtCaller(frame)
}
return newEntry
}

// Grab an instance of Logger that may have been passed in context.Context.
// Returns the logger or creates a new instance if none was found in ctx. Since
// Logger is based on logrus.Entry, if logger instance from context is any of
Expand Down
25 changes: 17 additions & 8 deletions rest_utils/response_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,56 +27,65 @@ import (
// return selected http code + error message directly taken from error
// log error
func RestErrWithLog(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int) {
RestErrWithLogMsg(w, r, l, e, code, "")
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, "", logrus.ErrorLevel)
}

// return http 500, with an "internal error" message
// log full error
func RestErrWithLogInternal(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error) {
msg := "internal error"
RestErrWithLogMsg(w, r, l, e, http.StatusInternalServerError, msg)
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, http.StatusInternalServerError, msg, logrus.ErrorLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as debug
func RestErrWithDebugMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.DebugLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as info
func RestErrWithInfoMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.InfoLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as warning
func RestErrWithWarningMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.WarnLevel)
}

// same as RestErrWithErrorMsg - for backward compatibility purpose
// return an error code with an overriden message (to avoid exposing the details)
// log full error as error
func RestErrWithLogMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.ErrorLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as error
func RestErrWithErrorMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.ErrorLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as fatal
func RestErrWithFatalMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.FatalLevel)
}

// return an error code with an overriden message (to avoid exposing the details)
// log full error as panic
func RestErrWithPanicMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) {
l = l.WithCallerContext(1)
restErrWithLogMsg(w, r, l, e, code, msg, logrus.PanicLevel)
}

Expand All @@ -101,16 +110,16 @@ func restErrWithLogMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger,
}
switch logLevel {
case logrus.DebugLevel:
l.F(log.Ctx{}).Debug(e.Error())
l.Debug(e.Error())
case logrus.InfoLevel:
l.F(log.Ctx{}).Info(e.Error())
l.Info(e.Error())
case logrus.WarnLevel:
l.F(log.Ctx{}).Warn(e.Error())
l.Warn(e.Error())
case logrus.ErrorLevel:
l.F(log.Ctx{}).Error(e.Error())
l.Error(e.Error())
case logrus.FatalLevel:
l.F(log.Ctx{}).Fatal(e.Error())
l.Fatal(e.Error())
case logrus.PanicLevel:
l.F(log.Ctx{}).Panic(e.Error())
l.Panic(e.Error())
}
}

0 comments on commit 2834acf

Please sign in to comment.