gologgingcallstack

Is it possible to wrap logrus.Logger functions without losing the line number prefix?


When using the wrapped logrus function/logger, the logger prefixes all log lines with the file name and line number of the logger function call, for example:

INFO[0000]logging.go:39 myfolder/logging.Info()

If I wrap the log function like this, for instance: package logging

import (
    "fmt"
    "github.com/sirupsen/logrus"
    "os"
    "path"
    "runtime"
)

var (
    log *logrus.Logger
)

func init() {

    log = logrus.New()
    log.SetReportCaller(true)
    log.Formatter = &logrus.TextFormatter{
        CallerPrettyfier: func(f *runtime.Frame) (string, string) {
            filename := path.Base(f.File)
            return fmt.Sprintf("%s()", f.Function), fmt.Sprintf("%s:%d", filename, f.Line)
        },
    }
}

func Info(args ...interface{}) {
        log.Info(args...)
}

Every line emitted by this function is going to be prefixed with the line number of the logging function call. That is as expected, but the desired behavior is for each line to be prefixed with the line number of the line where Info is called.

The Desired output should be :

INFO[0000]myfile.go:39 myfolder/myfile.myfunction()

Is there any way around it?


Solution

  • It is not possible to do it in the logrus. I had a similar requirement and ended up doing the following which worked for us.

    package mylog
    
    import (
        "fmt"
        "github.com/Sirupsen/logrus"
        "runtime"
        "strings"
    )
    
    var logger = logrus.New()
    
    func SetLogFormatter(formatter logrus.Formatter) {
        logger.Formatter = formatter
    }
    
    // Info logs a message at level Info on the standard logger.
    func Info(args ...interface{}) {
        if logger.Level >= logrus.InfoLevel {
            entry := logger.WithFields(logrus.Fields{})
            entry.Data["file"] = fileInfo(2)
            entry.Info(args...)
        }
    }
    
    func fileInfo(skip int) string {
        _, file, line, ok := runtime.Caller(skip)
        if !ok {
            file = "<???>"
            line = 1
        } else {
            slash := strings.LastIndex(file, "/")
            if slash >= 0 {
                file = file[slash+1:]
            }
        }
        return fmt.Sprintf("%s:%d", file, line)
    }
    

    See if this or some variation of this works for your use case. I have removed the application-specific code from the code snippet above.