首页 > 解决方案 > Go sdk 记录器如何以及何时刷新?

问题描述

我正在尝试确定默认/sdk 记录器 log.PrintYYY() 函数是否在某个时间点、退出、恐慌等被刷新。我不确定是否需要找到一种方法来刷新写入器记录器已连接,尤其是在使用 SetOutput(...) 设置输出写入器时。当然,writer 接口没有 flush() 方法,所以不确定如何完成。

Go sdk 记录器如何以及何时刷新?

标签: gologgingflush

解决方案


log不负责刷新底层io.Writer。包可能会log执行类型断言以查看当前io.Writer是否有Flush()方法,如果有则调用它,但不能保证如果多个io.Writers 被“链接”,数据最终会被刷新到最底层。

log在我看来,包装不刷新的主要原因是性能。我们使用缓冲写入器,因此我们不必在每次写入单个字节(或字节片)时都到达底层,但我们可以缓存最近写入的数据,并且当我们达到某个大小(或某个time),我们可以一次高效地编写“批处理”。

如果log包在每个日志语句之后刷新,那将使缓冲的 IO 无用。在小型应用程序的情况下这可能无关紧要,但如果您有一个高流量的 Web 服务器,在每个日志语句之后发出一个刷新(每个请求处理中可能有很多)会导致严重的性能缺陷。

那么是的,如果应用程序被终止,最后的日志语句可能不会到达底层。正确的解决方案是优雅地关闭:实现信号处理,当您的应用程序即将终止时,正确刷新并关闭io.Writer您使用的记录器的底层。有关详细信息,请参阅:

是否可以以“延迟”方式捕获 Ctrl+C 信号并运行清理功能?

Go 中的 finally() 是否与 init() 正好相反?

在 Go 中收到 SIGINT 时是否调用延迟函数?

如果——为了简单起见——你仍然需要一个在每个日志语句之后刷新的记录器,你可以轻松实现。这是因为该log.Logger类型保证io.Writer通过一次Writer.Write()调用将每个日志消息传递到目标:

每个日志记录操作都会调用 Writer 的 Write 方法。一个 Logger 可以同时从多个 goroutine 中使用;它保证序列化对 Writer 的访问。

所以基本上你需要做的就是创建一个“包装器” io.Writer,它的方法在将调用“转发”到其底层编写器Write()之后执行刷新。Write()

这就是它的样子:

type myWriter struct {
    io.Writer
}

func (m *myWriter) Write(p []byte) (n int, err error) {
    n, err = m.Writer.Write(p)

    if flusher, ok := m.Writer.(interface{ Flush() }); ok {
        flusher.Flush()
    } else if syncer := m.Writer.(interface{ Sync() error }); ok {
        // Preserve original error
        if err2 := syncer.Sync(); err2 != nil && err == nil {
            err = err2
        }
    }
    return
}

此实现检查Flush()method 和os.File'sSync()方法,并在它们“存在”时调用。

这就是如何使用它,以便日志语句始终刷新:

f, err := os.Create("log.txt")
if err != nil {
    panic(err)
}
defer f.Close()

log.SetOutput(&myWriter{Writer: f})

log.Println("hi")

查看相关问题:

Go:创建 io.Writer 接口以记录到 mongodb 数据库

net/http 设置自定义记录器


推荐阅读