【问题标题】:How and when is the Go sdk logger flushed?Go sdk 记录器如何以及何时刷新?
【发布时间】:2019-03-26 21:54:44
【问题描述】:

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

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

【问题讨论】:

  • 考虑到它没有采用Flush方法的接口,所以它不控制何时刷新。它依赖于作者。
  • 什么是“Go sdk”?
  • @Volker SDK(软件开发工具包)google.com/search?q=sdk,Go 是语言,SDK 是标准库。
  • Go 的标准库是 Go 的标准库而不是 SDK。

标签: go logging flush


【解决方案1】:

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

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

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

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

Is it possible to capture a Ctrl+C signal and run a cleanup function, in a "defer" fashion?

Is there something like finally() in Go just opposite to what init()?

Are deferred functions called when SIGINT is received in Go?

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

每个日志记录操作都会调用 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() 方法和os.FileSync() 方法,并在它们“存在”时调用。

这就是它的使用方法,以便日志语句始终刷新:

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

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

log.Println("hi")

查看相关问题:

Go: Create io.Writer inteface for logging to mongodb database

net/http set custom logger

【讨论】:

    【解决方案2】:

    Logger 不应该知道如何刷新数据。您必须刷新您在创建记录器时指定的输出编写器(如果它具有这种能力)。

    参见github discussion的示例

    package main
    
    import (
        "bufio"
        "flag"
        "log"
        "os"
        "strings"
    )
    
    func main() {
        var flush bool
        flag.BoolVar(&flush, "flush", false, "if set, will flush the buffered io before exiting")
        flag.Parse()
    
        br := bufio.NewWriter(os.Stdout)
        logger := log.New(br, "", log.Ldate)
        logger.Printf("%s\n", strings.Repeat("This is a test\n", 5))
        if flush {
            br.Flush()
        }
        logger.Fatalf("exiting now!")
    }
    

    您可以阅读与您的问题相关的整个讨论on github

    或者,您可以查看具有flush 的第 3 方记录器。查看zap logger 有方法logger.Sync()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      • 2023-04-02
      • 2012-01-07
      • 2014-06-12
      • 1970-01-01
      相关资源
      最近更新 更多