正确的Golang全局日志记录方法


118

Go中应用程序登录的模式是什么?如果我有5个我需要登录的goroutine,我应该...

  • 创建一个单曲log.Logger并传递它?
  • 传递指向该指针的指针log.Logger
  • 每个例程或函数都应创建一个记录器吗?
  • 我应该将记录器创建为全局变量吗?

Answers:


59
  • 创建一个log.Logger并传递给他?

那是可能的。甲log.Logger可以同时从多个够程中使用。

  • 传递指向该日志的指针。

log.New返回a *Logger,通常表明您应该将对象作为指针传递。将其作为值传递将创建该结构的副本(即Logger的副本),然后多个goroutine可能会同时写入同一io.Writer。根据作者的实现,这可能是一个严重的问题。

  • 每个例程或函数都应创建一个记录器吗?

我不会为每个函数或goroutine创建单独的记录器。Goroutine(和函数)用于执行非常轻量级的任务,这些任务不足以维护单独的记录器。为项目的每个较大组件创建一个记录器可能是一个好主意。例如,如果您的项目使用SMTP服务发送邮件,那么为邮件服务创建单独的记录器听起来是个好主意,因此您可以分别过滤和关闭输出。

  • 我应该将记录器创建为全局变量吗?

那取决于你的包裹。在前面的邮件服务示例中,为每个服务实例都配备一个记录器可能是一个好主意,以便用户可以在使用gmail邮件服务时记录故障,而与使用本地MTA时发生的故障(例如sendmail)不同。 )。


37

对于简单的情况,在日志包中定义了一个全局记录器log.Logger。可以通过配置全局记录器log.SetFlags

然后,可以只调用日志包的顶层函数,例如log.Printflog.Fatalf,它们使用该全局实例。


以为您可以设置不能使用自定义记录器的标志。
0xcaff 2014年

@caffinatedmonkey实际上,如果自定义记录器实现了io.Writer接口,并且可以通过更改默认记录器的输出,则可以使用它们SetOutput()
congusbongus

16

这是一个简单的记录器

package customlogger

import (
    "log"
    "os"
    "sync"
)

type logger struct {
    filename string
    *log.Logger
}

var logger *logger
var once sync.Once

// start loggeando
func GetInstance() *logger {
    once.Do(func() {
        logger = createLogger("mylogger.log")
    })
    return logger
}

func createLogger(fname string) *logger {
    file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)

    return &logger{
        filename: fname,
        Logger:   log.New(file, "My app Name ", log.Lshortfile),
    }
}

你可以这样使用

package main

import (
    "customlogger"
    "fmt"
    "net/http"
)

func main() {
    logger := customlogger.GetInstance()
    logger.Println("Starting")

    http.HandleFunc("/", sroot)
    http.ListenAndServe(":8080", nil)
}

func sroot(w http.ResponseWriter, r *http.Request) {
    logger := customlogger.GetInstance()

    fmt.Fprintf(w, "welcome")
    logger.Println("Starting")
}

10

我知道这个问题有点老了,但是,如果像我一样,您的项目是由多个较小的文件组成的,则我为您的第4个选项投票-我创建了一个logger.go包main的一部分。这个go文件创建记录器,将其分配给文件,并将其提供给main的其余部分。注意我还没有想办法关闭错误日志...

package main

import (
    "fmt"
    "log"
    "os"
)

var errorlog *os.File
var logger *log.Logger

func init() {
    errorlog, err := os.OpenFile(logfile,  os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("error opening file: %v", err)
        os.Exit(1)
    }

    logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}

8
对于优雅收盘时,你很可能defer errorlog.Close()在执行结束,或者更好地保证其关闭,设置信号处理程序使用Go的信号包 golang.org/pkg/os/signal
安芬尼·

4

这是一个比较老的问题,但是我建议使用http://github.com/romana/rlog(由我们开发)。它是通过环境变量配置的,在导入rlog时将创建并初始化logger对象。因此,无需传递记录器。

rlog具有很多功能:

  • 完全可配置的日期/时间戳
  • 同时输出到stderr或stdout以及文件。
  • 标准日志级别(调试,信息等)以及可自由配置的多层日志。
  • 按需记录呼叫者信息(文件,行号,功能)。
  • 能够为不同的源文件设置不同的日志级别。

它很小,除了标准的Golang库外没有任何外部依赖关系,并且正在积极开发中。回购中提供了示例。


3
感谢您披露与您推荐产品的隶属关系!感激不尽。
罗伯特·哥伦比亚

2

我发现默认日志包(https://golang.org/pkg/log/)有所限制。例如,不支持信息日志和调试日志。
经过一番摸索后,决定使用https://github.com/golang/glog。这似乎是https://github.com/google/glog的端口,并为日志记录提供了相当大的灵活性。例如,在本地运行应用程序时,您可能需要DEBUG级别的日志,但可能只想在生产环境中以INFO / ERROR级别运行。完整功能/指南的列表位于https://google-glog.googlecode.com/svn/trunk/doc/glog.html(适用于c ++模块,但大部分会转换为golang端口)


0

您可以考虑的日志记录模块之一是klog。它支持“ V”记录,从而可以灵活地记录特定级别

klog是glog的分支,克服了以下缺点

  • glog提出了很多“陷阱”,并介绍了容器化环境中的挑战,所有这些都没有得到很好的记录。
  • glog不能提供一种简单的方法来测试日志,这会损害使用它的软件的稳定性
  • glog是基于C ++的,而klog是纯golang的实现

样例实施

package main

import (
    "flag"

    "k8s.io/klog"


)

type myError struct {
    str string
}

func (e myError) Error() string {
    return e.str
}

func main() {
    klog.InitFlags(nil)
    flag.Set("v", "1")
    flag.Parse()

    klog.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
    klog.V(3).Info("nice to meet you")
    klog.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
    klog.Error(myError{"an error occurred"}, "goodbye", "code", -1)
    klog.Flush()
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.