So I'm using a structured logging library (logrus), and I have a core
package used as a base for some other packages, lets call this package me/core
, then individual packages like me/foo-service
, me/bar-service
etc. that use this core library for common dependencies/utilities such as setup, configuration loading, and I also wanted to use it for standardized things like logging, so I want me/core
to be able to configure logging for the other packages, with Logrus you can do things like
import(
log "github.com/Sirupsen/logrus"
)
[...]
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.TextFormatter{FullTimestamp:true})
Then do;
log.Debug("Moo")
log.WithFields(log.Fields{"structured":"data"}).Debug("I have structure data")
getting an output like
> DEBU[2016-04-12T22:11:38+01:00] Moo
> DEBU[2016-04-12T22:11:38+01:00] I have structure data structured=data
So I want to configure this in my me/foo-service
package with something like
import(
"me/core/logging"
)
func main(){
logging.Setup()
}
Only for various reasons I'm running into issues. The primary issue seems to be that both me/core
, and me/foo-service
have a vendored version of the logrus
library, those log.Set*
commands modify the variable logrus.std
logger which holds the standard, global logger, but this is a seperate instance for both packages because me/core/vendor/.../logrus/std
and me/foo-service/vendor/.../logrus/std
are different objects.
First thing I tried was creating a variable in me/core/logging
that I could use in the parent, so something like
package logging
import(
log "github.com/Sirupsen/logrus"
)
var Log *log.Logger
func Setup(verbose bool){
Log = log.New()
if verbose{
Log.Level = log.DebugLevel
} else {
Log.Level = log.InfoLevel
}
Log.Formatter = &log.TextFormatter{FullTimestamp:true}
Log.Debug("Logging verbosely")
}
and that works for simple cases like this
package main
import(
"me/core/logging"
)
func main() {
logging.Setup(parsedOptions.Verbose)
logging.Log.Debug("Moo")
}
However trying to use the structured data Fields
causes problems, I can't use the local vendored logrus
fields like
logging.Log.WithFields(log.Fields{"data":"hi"}).Debug("test")
as I get
cannot use "me/foo-service/vendor/github.com/Sirupsen/logrus".Fields literal (type "me/foo-service/vendor/github.com/Sirupsen/logrus".Fields) as type "me/core/vendor/github.com/Sirupsen/logrus".Fields in argument to logging.Log.WithFields
And trying to mirror the field in me/core/logging
with something like
type Fields log.Fields
Doesn't work ether because it's still a different type
cannot use logging.Fields literal (type logging.Fields) as type "me/core/vendor/github.com/Sirupsen/logrus".Fields in argument to logging.Log.WithFields
I also can't think of any way to pass my local log.std
from me/foo-service
to me/core
as it's also a different type due to the vendored package.
My work around for the moment involves creating a mirror of every method, so in me/core/logging
I have a set up like
package logging
import(
log "github.com/Sirupsen/logrus"
)
var Log *log.Logger
type Fields map[string]interface{}
func Setup(verbose bool){
Log = log.New()
if verbose{
Log.Level = log.DebugLevel
} else {
Log.Level = log.InfoLevel
}
Log.Formatter = &log.TextFormatter{FullTimestamp:true}
Log.Debug("Logging verbosely")
}
func Debug(msg interface{}){
Log.Debug(msg)
}
func WithFields(fields Fields) *log.Entry{
lf := log.Fields{}
for k,v := range fields{
lf[k] = v
}
return Log.WithFields(lf)
}
But that would involve creating a mirror for every method which seems really inefficient.
So I'd like a suggestion for how I can make me/core/vendor/.../logrus/std
avaliable to me/foo-service
, or if I'm thinking about this in completely the wrong way a better way to do it.
You can only mirror WithFields
since the other ones accept built in types. Also since logging.Fields
and logrus.Fields
are the same type you could more simply do
func WithFields(f Fields) log.*Entry {
return Log.WithFields(log.Fields(f))
}
Then in your service, you could call
logging.Log.Debug("message")
logging.WithFields(logging.Fields{"k":"v"}).Debug("message")