Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://vearne.cc

前言

uber开源的高性能日志库zap, 除了性能远超logrus之外,还有很多诱人的功能,比如支持日志采样、支持通过HTTP服务动态调整日志级别。本文简单聊一下日志采样。

使用说明

Sampling:Sampling实现了日志的流控功能,或者叫采样配置,主要有两个配置参数,Initial和Thereafter,实现的效果是在1s的时间单位内,如果某个日志级别下同样内容的日志输出数量超过了Initial的数量,那么超过之后,每隔Thereafter的数量,才会再输出一次。是一个对日志输出的保护功能。

注意 这里画个重点

  • 仅对”同样内容” 的日志做采样
  • 默认1s的时间单位内

示例

package main

import (
    "go.uber.org/zap"
)

func main() {
    config := zap.NewProductionConfig()
    // 默认值:Initial:100 Thereafter:100
    config.Sampling = &zap.SamplingConfig{
        Initial:    5, // 从第6条数据开始
        Thereafter: 3, // 每3条打印一条
    }
    // 可以置为nil 来关闭采样
    //config.Sampling = nil
    config.Encoding = "console"
    logger, _ := config.Build()
    defer logger.Sync()
    // 打印的消息要**重复**才会被执行采样动作
    for i := 0; i < 100; i++ {
        logger.Info("hello")
    }
}

输出

仅输出36条日志,而不是100条

核心代码

代码版本 v1.10.0

// Info logs a message at InfoLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (log *Logger) Info(msg string, fields ...Field) {
    if ce := log.check(InfoLevel, msg); ce != nil {
        ce.Write(fields...)
    }
}
func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
    if !s.Enabled(ent.Level) {
        return ce
    }

    counter := s.counts.get(ent.Level, ent.Message)
    // 内部的counter每个 s.tick 会被重置,默认为1秒
    n := counter.IncCheckReset(ent.Time, s.tick)
    //  first等同于Initial
    //  thereafter等同于Thereafter
    if n > s.first && (n-s.first)%s.thereafter != 0 {
        return ce
    }
    return s.Core.Check(ent, ce)
}

有兴趣的还可以看萌叔的另一个代码示例
test_zap2.go

总结

通过日志采样,降低了高并发下打印日志的开销,能够有效提升提服务的吞吐能力。


打赏我

微信支付码

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据