全局日志记录的正确方法

Go 中的应用程序日志模式是什么?如果我有,比如说,5个需要记录的常规程序,我应该..。.

  • 创建一个单独的 log.Logger并传递它?
  • 传递一个指向那个 log.Logger的指针?
  • 每个 goroutine 或函数都应该创建一个日志记录器吗?
  • 我是否应该将日志记录器创建为一个全局变量?
72666 次浏览

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

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

  • 创建一个日志。记录器并传递它吗?

这是可能的。 伐木工可以从多个 goroutines 并发使用。

  • 传递一个指向该日志的指针。记录器?

木头。New 返回一个 *Logger,这通常表示您应该将对象作为指针传递。将其作为值传递将创建 struct 的一个副本(即 Logger 的一个副本) ,然后多个 goroutine 可能并发地写入同一个 我是作家。这可能是一个严重的问题,具体取决于编写器的实现。

  • 每个 goroutine 或函数都应该创建一个日志记录器吗?

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

  • 我是否应该将日志记录器创建为一个全局变量?

这取决于你的包裹。在前面的邮件服务示例中,为每个服务实例设置一个日志记录器可能是一个好主意,这样用户在使用 gmail 服务时可以记录失败,而不同于在使用本地 MTA (例如 sendmail)时发生的失败。

我知道这个问题有点老,但如果,像我一样,你的项目是由多个较小的文件,我投票给你的第四个选项-我已经创建了一个 logger.go,这是包的一部分主。这个 go 文件创建日志记录器,将其分配给一个文件,并将其提供给 main 的其余部分。注意,我还没有想出一个优雅的方式来结束 errorlog..。

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

我发现默认的日志包(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 端口)

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

Rlog 有很多特性:

  • 完全可配置的日期/时间戳
  • 同时输出到 stderr 或 stdout 以及 file。
  • 标准日志级别(Debug、 Info 等)以及可自由配置的多级日志。
  • 根据需要记录呼叫者信息(文件、行号、函数)。
  • 能够为不同的源文件设置不同的日志级别。

它非常小,没有外部依赖,除了标准的 Golang 图书馆,并且正在积极开发中。回购协议中提供了实例。

这是一个简单的记录器

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

可以考虑的日志记录模块之一是 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()
}