Golang logrus - how to do a centralized configuration?
Asked Answered
D

2

35

I am using logrus in a Go app. I believe this question is applicable to any other logging package (which doesn't offer external file based configuration) as well.

logrus provides functions to setup various configuration, e.g. SetOutput, SetLevel etc.

Like any other application I need to do logging from multiple source files/packages, it seems you need to setup these options in each file with logrus.

Is there any way to setup these options once somewhere in a central place to be shared all over the application. That way if I have to make logging level change I can do it in one place and applies to all the components of the app.

Docile answered 15/5, 2015 at 10:48 Comment(0)
I
46

You don't need to set these options in each file with Logrus.

You can import Logrus as log:

import log "github.com/Sirupsen/logrus"

Then functions like log.SetOutput() are just functions and modify the global logger and apply to any file that includes this import.

You can create a package global log variable:

var log = logrus.New()

Then functions like log.SetOutput() are methods and modify your package global. This is awkward IMO if you have multiple packages in your program, because each of them has a different logger with different settings (but maybe that's good for some use cases). I also don't like this approach because it confuses goimports (which will want to insert log into your imports list).

Or you can create your own wrapper (which is what I do). I have my own log package with its own logger var:

var logger = logrus.New()

Then I make top-level functions to wrap Logrus:

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

func Debug(args ...interface{}) {
    logger.Debug(args...)
}

This is slightly tedious, but allows me to add functions specific to my program:

func WithConn(conn net.Conn) *logrus.Entry {
    var addr string = "unknown"
    if conn != nil {
        addr = conn.RemoteAddr().String()
    }
    return logger.WithField("addr", addr)
}

func WithRequest(req *http.Request) *logrus.Entry {
    return logger.WithFields(RequestFields(req))
}

So I can then do things like:

log.WithConn(c).Info("Connected")

(I plan in the future to wrap logrus.Entry into my own type so that I can chain these better; currently I can't call log.WithConn(c).WithRequest(r).Error(...) because I can't add WithRequest() to logrus.Entry.)

Immortelle answered 15/5, 2015 at 13:55 Comment(4)
Thanks for a well detailed explanation! Previously I have also used my own log wrappers with other languages but a lot of times found them unnecessary and an overhead. I think it would make log provider replacement easier though.Docile
As Golang beginner I really appreciate the introduction including information about the "globalness" of the logging. Did not know this.And
This is kind a necro post, but I just implemented something similar and the file and func fields are the file/func locations of my override where i call logger.Info() etc... It makes sense since the actual callsite for the log statement is now inside my "wrapper" package, but wanted to ask if that happened to you and if you'd found a way around it.Klaus
I miss SLF4J already. Why is Go so behind.Skittish
J
1

This is the solution that I arrived at for my application that allows adding of fields to the logging context. It does have a small performance impact due to the copying of context base fields.

package logging

import (
    log "github.com/Sirupsen/logrus"
)

func NewContextLogger(c log.Fields) func(f log.Fields) *log.Entry {
    return func(f log.Fields) *log.Entry {
        for k, v := range c {
            f[k] = v
        }
        return log.WithFields(f)
    }
}

package main

import (
    "logging"
)

func main {
    app.Logger = logging.NewContextLogger(log.Fields{
        "module":    "app",
        "id":   event.Id,
    })
    app.Logger(log.Fields{
        "startTime": event.StartTime,
        "endTime":   event.EndTime,
        "title":     event.Name,
    }).Info("Starting process")
}
Jurgen answered 24/10, 2017 at 23:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.