软件包之间共享库时的集中日志记录配置

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")