【问题标题】:Logging globally (across packages)全局记录(跨包)
【发布时间】:2015-04-09 12:35:48
【问题描述】:

我现在花了很多时间搜索和阅读有关该主题的帖子,但还没有设法完全回答我的问题 - 或者我只是需要对现有答案进行更多说明。

我读过this 的帖子,它与我的标题相同,但它是关于跨 go 例程而不是包进行日志记录。

我要解决的是跨主应用程序及其使用的任何包进行日志记录。我需要一个可以记录到多个位置的记录器(可以使用 io.MultiWriter 完成)并且可以执行 log.Error() 和 log.Debug() 之类的操作

我知道有一些包可以做到这一点,而且我知道如何自己实现这些东西。

我无法理解的是如何正确使用我的包。

一种方法当然是在 main 中创建记录器,然后将其传递给所有需要记录的函数。但这似乎很尴尬。

我的理想解决方案是使用 log 包中的内置全局记录器之类的记录器,但具有上述附加功能。

我主要希望它用于包中的可选调试日志记录,因此如果需要,我可以在生产版本中打开它。

这样做的正确方法是什么?

【问题讨论】:

  • 为什么不在每个包中使用全局记录器变量并在主包中创建/设置它们?
  • 也许你可以试试github.com/golang/glog。虽然它没有 Debug,但它有 Info、Warning、Error 和 Fatal。它还允许详细级别的日志记录。嗯,但它也不允许设置作家。那好吧!值得评论

标签: logging go


【解决方案1】:

正确的方法是您认为理想的方法。只需创建一个包(最好遵循 Go 的约定 https://golang.org/doc/code.html)并将您的日志设为全局:

package mylog

// Define your custom logger type.
type logger struct { /* Whatever you want */ }

// Optionally make it a interface.
type Logger interface { /* Your functions */ }

// And just go global.
var defaultLogger *Logger

func init(){
   defaultLogger = new(logger)
}

func Debug(params ...string){
   // Have some fun.
}

// ...

我还建议在文档中描述您的项目使用该日志记录功能。

【讨论】:

    【解决方案2】:

    我正在发布对我有用的解决方案!。我刚刚创建了自己的包,并使用了 init 函数。

    package logging
    
    import (
        "io"
        logging "log"
        "os"
    
        "github.com/Sirupsen/logrus"
    )
    
    var (
        log *logrus.Logger
    )
    
    func init() {
        f, err := os.OpenFile("logs/application.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    
        if err != nil {
            logging.Fatalf("error opening file: %v", err)
        }
    
        log = logrus.New()
    
        //log.Formatter = &logrus.JSONFormatter{}
    
        log.SetReportCaller(true)
    
        mw := io.MultiWriter(os.Stdout, f)
        log.SetOutput(mw)
    }
    
    // Info ...
    func Info(format string, v ...interface{}) {
        log.Infof(format, v...)
    }
    
    // Warn ...
    func Warn(format string, v ...interface{}) {
        log.Warnf(format, v...)
    }
    
    // Error ...
    func Error(format string, v ...interface{}) {
        log.Errorf(format, v...)
    }
    
    var (
    
        // ConfigError ...
        ConfigError = "%v type=config.error"
    
        // HTTPError ...
        HTTPError = "%v type=http.error"
    
        // HTTPWarn ...
        HTTPWarn = "%v type=http.warn"
    
        // HTTPInfo ...
        HTTPInfo = "%v type=http.info"
    )
    

    在任何包上,只需导入我的包并执行(信息、警告、错误)函数

    package main
    
    import (
    
        log "logging"
    )
    
    func main() {
        log.Error(log.ConfigError, "Testing the error")
    }
    

    日志条目将保存在屏幕上,并将保存在文件中。

    【讨论】:

    • 为什么使用 github.com/Sirupsen/logrus 而不是 go 的常规日志记录包?
    【解决方案3】:

    我在这里为任何对类似解决方案感兴趣的人发布我的答案。

    我所做的是复制标准日志包 (src/log/log.go) 并扩展它。获得一个已经完成标准记录器所做的所有事情以及您希望它做的任何其他事情的全局记录器再简单不过了!在这种情况下,支持分级日志记录。

    我必须做的唯一修改:

    type Logger struct {
        mu     sync.Mutex // ensures atomic writes; protects the following fields
        prefix string     // prefix to write at beginning of each line
        flag   int        // properties
        out    io.Writer  // destination for output
        buf    []byte     // for accumulating text to write
        level  int        // One of DEBUG, ERROR, INFO
    }
    

    只添加了最后一行。日志包设置了一个全局变量 std,然后可以使用该变量从包中的任何函数访问结构字段。

    接下来我为不同的日志级别添加了常量:

    const (
        DEBUG = 1 << iota
        INFO
        ERROR
    )
    

    接下来我添加了我的函数:

    (注意:ct 是 https://github.com/seago/go-colortext 包,它允许在 Windows 上为控制台文本着色。所以这里的错误都以红色打印)

    func Error(v ...interface{}) {
        if std.level <= ERROR {
            ct.ChangeColor(ct.Red, true, ct.None, false)
            s := fmt.Sprintf("ERROR: %v", v...)
            std.Output(2, s)
            ct.ResetColor()
        }
    }
    
    func Info(format string, v ...interface{}) {
        if std.level <= INFO {
            s := fmt.Sprintf("INFO: "+format, v...)
            std.Output(2, s)
        }
    }
    
    func Debug(v ...interface{}) {
        if std.level <= DEBUG {
            s := fmt.Sprintf("DEBUG: %v", v...)
            std.Output(2, s)
        }
    }
    
    func SetLogLevel(lvl int) {
        std.level = lvl
    }
    

    就是这样!有了它,我现在可以通过简单地导入修改后的包而不是标准日志包来使用它并注销:

    import (
        "errors"
        "tryme/log" 
    )
    
    func main() {
        log.SetLogLevel(log.INFO)
        log.Info("This is a test Info")
        err := errors.New("This is a test error!!!")
        log.Error(err)
        log.Debug("Testing debugging") // won't be printed with log.INFO
    }
    

    这当然只是一个演示,可以通过更多日志级别、输出格式等轻松扩展。

    您可以使用标准日志包提供的所有功能,例如 SetOutput 写入文件或 MultiWriter 写入文件和控制台等。

    【讨论】:

    • 你为什么要复制整个记录器而不是仅仅将Logger 嵌入到你自己的新结构中,然后还要实现ErrorInfo 等?全部复制似乎有点矫枉过正......
    • @ScallioXT - 因为我想要一个全局记录器。如果我创建自己的类型并嵌入标准记录器,那么我必须包装日志包的所有函数(那些不是方法的函数)才能全局访问它们。这比我上面所做的要多得多,但我不知道我还能怎么做。也许我错过了一些东西。如果是这样,请指出正确的方向。
    • 我认为只嵌入log.Logger 会更容易,但我刚刚尝试过,您遇到了麻烦,log.Logger 的字段未导出,因此您无法引用它们。要解决这个问题,您必须跳很多圈,这确实不值得。就我看来,像现在这样复制它似乎是最好的解决方案。
    【解决方案4】:

    补充一点,如果你想对多个 Go 应用程序进行日志记录,你可以使用 RPC 并创建一个日志记录服务。这将是一个独立的应用程序,为其他应用程序提供服务。 Golang has its own package for it

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-25
      相关资源
      最近更新 更多