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

Overhead for Golang's logger if set to discard

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

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

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

Q1。如果我仍然将记录器设置为丢弃,是否会影响性能?

Q2。对于最佳实践,从 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
  1. 我运行每次10M次。
  2. 2~3秒10M日志到io.Discard比我想象的要快..我想我不用担心速度。
  3. os.Stdout 我不是故意要用的; (我最初关心的是保留代码 ioutil.Discard 与删除代码),但由于 os.Stdout 没有缓冲,所以速度很慢。
  4. 顺便说一下,printf() 比我想象的要快很多。几乎与 println()
  5. 相同

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

基准

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

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

来自 ioutil 的相关代码显示将要发生的事情 "under the hood":

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

记录最佳实践

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

你会注意到 Go 在标准库中只提供 PrintError 级别的语句,所以你需要一个 external package to benefit from something like this (these can also help structuring your logs in a more machine readable/search friendly JSON format too). Dave Cheney has justified the reasoning behind this decision in a blog post

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