如果设置为丢弃,Golang 记录器的开销

Posted

技术标签:

【中文标题】如果设置为丢弃,Golang 记录器的开销【英文标题】:Overhead for Golang's logger if set to discard 【发布时间】:2019-09-10 13:46:00 【问题描述】:

我有一个 HTTP 处理程序,它有 40 个设置为 os.Stdout 的记录器。

它对我来说很好,因为我是目前唯一的测试者。 但是,到了制作的时候,恐怕开销太大了。

当前记录器设置为 os.Stdoutos.Stderr。 但一旦进入生产阶段,os.Stdout 将设置为 ioutil.discard

第一季度。如果我仍然设置要丢弃的记录器,它会影响性能吗?

第二季度。对于最佳实践,从 HTTP 处理程序中完全删除记录器会更好吗?

---- 更新----

package main

import (
    "time"
    "fmt"
    "log"
    "io/ioutil"
    "io"
)

func testPrintf(w io.Writer, note string, cnt int) 
    l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ 
        l.Printf("%s - %d", "test", i)
    
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n","Printf ", note, t2.Sub(t1))


func testPrintln(w io.Writer, note string, cnt int) 
    l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ 
        l.Println("test" + string(i))
    
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n","Println", note, t2.Sub(t1))


func testDoNothing(w io.Writer, note string, cnt int) 
    //l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ 
        _ = "test" + string(i) // evaluated but didn't do any.
    
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n", "DoNothing", note, t2.Sub(t1))


func main() 
    cnt := 10000000 // ten million
    testPrintf(ioutil.Discard, "discard.Attempt.1", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.1", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.1", cnt)
    fmt.Println("\n")
    testPrintf(ioutil.Discard, "discard.Attempt.2", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.2", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.2", cnt)
    fmt.Println("\n")
    testPrintf(ioutil.Discard, "discard.Attempt.3", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.3", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.3", cnt)

--- 结果---

Printf           discard.Attempt.1, 2.663697209s
Println          discard.Attempt.1, 2.4289759s
DoNothing        discard.Attempt.1, 190.480694ms

Printf           discard.Attempt.2, 2.493506245s
Println          discard.Attempt.2, 2.426081786s
DoNothing        discard.Attempt.2, 182.899574ms

Printf           discard.Attempt.3, 2.480853275s
Println          discard.Attempt.3, 2.481552836s
DoNothing        discard.Attempt.3, 180.916608ms
    我每次跑了 1000 万次。 10M log to io 2~3 sec.Discard比我想象的要快..我想我不必担心速度。 os.Stdout 我无意使用; (我最初担心的是使用 ioutil.Discard 保留代码与删除代码),但由于 os.Stdout 没有缓冲,所以速度很慢。 顺便说一句,printf() 比我想象的要快得多。几乎和 println() 一样

我不是每天都写 Go 代码,所以这个测试可能不准确。如果您从测试中看到误导性信息,请在此处发表评论以供其他人注意。谢谢。

【问题讨论】:

对于第一季度,您可以编写一个基准测试。然后你可以看到记录器有多慢。 对于第二季度,最好手动选择要记录的情况/错误,并且只记录那些(如果有的话) 如果你想知道性能影响,你需要在你的代码中测量它。评估日志参数占据您时间的可能性非常小。 您可以在 50 纳秒/日志条目下记录日志。例如github.com/larytet/binlog 【参考方案1】:

基准测试

虽然我的直觉说将日志放到 Discard 对您的代码的影响很小,但我建议您设置一个基准来测试您的记录器对性能的影响。

幸运的是,Go 通过在单元测试中编写 func BenchmarkXxxxx(*testing.B) 函数并运行 go test -bench 使这变得非常容易。更多信息可以找到in the documentation。对于您的基准测试,我建议编写两个测试,一个使用os.Stdout,另一个使用ioutil.Discard - 确保两个测试的输入相同。

来自ioutil 的相关代码显示“幕后”会发生什么:

// ioutil.go
type devNull int

func (devNull) Write(p []byte) (int, error) 
    return len(p), nil


func (devNull) WriteString(s string) (int, error) 
    return len(s), nil

记录最佳实践

一种记录方法是常见的“基于级别的记录”,您可以在其中选择希望记录的消息的严重性(DEBUG/INFO/WARN/ERROR 等),然后选择哪些级别在部署应用程序时显示(例如,DEBUG 及以上在开发中,WARN 及以上在生产中)。

您会注意到 Go 仅在标准库中提供 PrintError 级别语句,因此您将需要一个 external package 才能从这样的事情中受益(这些也有助于在更多机器中构建您的日志可读/搜索友好的 JSON 格式)。戴夫·切尼 (Dave Cheney) 证明了这一决定背后的理由 in a blog post。

在您的情况下,40 条日志语句听起来很多,但这可能取决于有多少是简单的 Print 日志;处理程序的大小和复杂性等。Print 日志记录作为开发中的临时措施非常有用,Error 如果您没有向客户端显示错误,则有助于在故障期间进行诊断。

【讨论】:

谢谢;我会让它变得简单并尝试;我会在这里更新结果以防以后有人遇到同样的问题..

以上是关于如果设置为丢弃,Golang 记录器的开销的主要内容,如果未能解决你的问题,请参考以下文章

mysql UPDATE 语句 - 相同值的开销?

MySQL/Mariadb 每条记录的开销?

记录一下关于golang版本升级的那些问题

记录一下关于golang版本升级的那些问题

cmd丢弃旧的副本啥意思

golang学习记录:二进制文件判断实现与思路