Hubro
Hubro

Reputation: 59403

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

When using the log.Lshortfile flag, the logger prefixes all log lines with the file name and line number of the logger function call, for example:

myfile.go:14: Hello, world!

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

func info(pattern string, args ...interface{}) {
    myLogger.Printf(pattern + "\n", args...)
}

Every line emitted by this function is going to be prefixed with the line number of the Printf 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.

Is there any way around it?

Upvotes: 14

Views: 5823

Answers (2)

icza
icza

Reputation: 418417

Methods of log.Logger call the Logger.Output() method to send the message to the appropriate output. Logger.Output() allows you to pass the calldepth (the number of frames to skip).

Unfortunately methods of log.Logger contain the calldepth "wired in", so you can't provide an offset to skip the wrapper function's frame.

But a much better alternative is to call this Logger.Output() from your wrapper, so you don't have to bother with frames and lines yourself. Also note that you don't need to append a newline "\n", as the log.Logger type already does that if the message to be logged does not end with a newline.

So a better and shorter alternative:

var myLogger = log.New(os.Stdout, "[my]", log.Lshortfile)

func info(pattern string, args ...interface{}) {
    myLogger.Output(2, fmt.Sprintf(pattern, args...))
}

Testing it:

func main() {
    log.SetFlags(log.Lshortfile)
    log.Println("hello")
    info("world")
}

Output (try it on the Go Playground):

main.go:11: hello
[my]main.go:12: world

As you can see, info() prints the proper line number (+1 compared to the line number printed by log.Println() in the previous line).

Upvotes: 19

Hubro
Hubro

Reputation: 59403

I was going to include this in the question as my current workaround, but I suppose it is a valid answer. I'm hoping somebody can tell me of a logger config option I've missed that lets me adjust the depth the logger uses when it calls runtime.Caller.

A workaround is to remove the log.Lshortfile flag and implement the behavior manually:

func info(format string, args ...interface{}) {
    _, file, line, _ := runtime.Caller(1)

    prefix := fmt.Sprintf("%v:%v: ", path.Base(file), line)

    if logger != nil {
        logger.Printf(prefix+format+"\n", args...)
    }
}

Upvotes: 0

Related Questions