玩转高性能日志库ZAP (7)-标识

1.前言 在Java中我们常常会这样使用日志 import org.slf4j.Logger; import org.slf4j.LoggerFactory; class Main { final static Logger log = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { log.info("info, hello {}", "slf4j"); log.error("error"); } } 输出 6:30:03.350 [main] INFO Main - info, hello slf4j 16:30:03.356 [main] ERROR Main - error 这里在每条日志中都有类名,可以很容易从文件中过滤出这个类相关的日志。 2.zap中使用name标识 zap中的玩法 package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "time" ) func main() { config := zap.NewProductionConfig() config.Encoding = "console" config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05") logger, _ := config.Build() defer logger.Sync() // 输出该条日志在代码文件中的行号 logger = logger.WithOptions(zap.AddCaller()) logger.Info("failed to fetch URL", zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) go func() { // worker1 // 携带name标识 logger1 := logger.Named("worker1") for { time.Sleep(time.Millisecond * 200) logger1.Info("hello1") } }() go func() { // worker2 logger2 := logger.Named("worker2") for { time.Sleep(time.Millisecond * 200) logger2.Info("hello2") } }() time.Sleep(3 * time.Second) } 输出 ...

October 9, 2024 · 2 min

玩转高性能日志库ZAP (6)-采样

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | 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条 ...

June 10, 2020 · 2 min

玩转高性能日志库ZAP(5)-异步写日志

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在复杂的业务逻辑中,一个请求就有可能带来10 ~ 30条日志的写入。打日志造成的开销很大,是无法被忽略的一环。怎样才能提高日志的写入速度? 2. 实验 首先来看几个实验 完整程序见 vearne/golab/zaplog 中的log.go log2.go log3.go 2.1 打印到文件 打印到指定文件中,并做归档 package zaplog import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) func InitLogger() *zap.Logger { // 动态调整日志级别 alevel := zap.NewAtomicLevel() hook := lumberjack.Logger{ Filename: "/tmp/tt1.log", MaxSize: 1024, // megabytes MaxBackups: 3, MaxAge: 7, //days Compress: true, // disabled by default } w := zapcore.AddSync(&hook) alevel.SetLevel(zap.InfoLevel) encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder core := zapcore.NewCore( zapcore.NewConsoleEncoder(encoderConfig), w, alevel, ) return zap.New(core) } 2.2 打印到终端 这应该是官方推荐的方式,特别是对于使用docker容器的场景,应用开发者的配置非常简单,无需指定日志文件路径,由容器管理服务采集容器的标准输出并归档。 ...

January 2, 2020 · 3 min

玩转高性能日志库ZAP(3)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引言 前几天有朋友问我,zap库是否支持同时打印到多个目标地址,比如1份打印到文件,1份到控制台,1份打印到kafka中。这是所有日志库都会支持的功能,zap当然也不例外。 示例 package main import ( "io/ioutil" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack" "os" ) func main(){ // First, define our level-handling logic. // 仅打印Error级别以上的日志 highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.ErrorLevel }) // 打印所有级别的日志 lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.DebugLevel }) hook := lumberjack.Logger{ Filename: "/tmp/abc.log", MaxSize: 1024, // megabytes MaxBackups: 3, MaxAge: 7, //days Compress: true, // disabled by default } topicErrors := zapcore.AddSync(ioutil.Discard) fileWriter := zapcore.AddSync(&hook) // High-priority output should also go to standard error, and low-priority // output should also go to standard out. consoleDebugging := zapcore.Lock(os.Stdout) // Optimize the Kafka output for machine consumption and the console output // for human operators. kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) // Join the outputs, encoders, and level-handling functions into // zapcore.Cores, then tee the four cores together. core := zapcore.NewTee( // 打印在kafka topic中(伪造的case) zapcore.NewCore(kafkaEncoder, topicErrors, highPriority), // 打印在控制台 zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority), // 打印在文件中 zapcore.NewCore(consoleEncoder, fileWriter, highPriority), ) // From a zapcore.Core, it's easy to construct a Logger. logger := zap.New(core) defer logger.Sync() logger.Info("constructed a info logger", zap.Int("test", 1)) logger.Error("constructed a error logger", zap.Int("test", 2)) } 输出结果 文件中 ...

September 17, 2018 · 2 min

玩转高性能日志库ZAP (2)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 uber开源的高性能日志库zap, 除了性能远超logrus之外,还有很多诱人的功能,比如支持日志采样、支持通过HTTP服务动态调整日志级别。不过他原生不支持文件归档,如果要支持文件按大小或者时间归档,必须要使用第三方库, 根据官方资料参考资料1,官方推荐的是 natefinch/lumberjack 示例 package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) // logpath 日志文件路径 // loglevel 日志级别 func initLogger(logpath string, loglevel string) *zap.Logger { hook := lumberjack.Logger{ Filename: logpath, // 日志文件路径 MaxSize: 1024, // megabytes MaxBackups: 3, // 最多保留3个备份 MaxAge: 7, //days Compress: true, // 是否压缩 disabled by default } w := zapcore.AddSync(&hook) var level zapcore.Level switch loglevel { case "debug": level = zap.DebugLevel case "info": level = zap.InfoLevel case "error": level = zap.ErrorLevel default: level = zap.InfoLevel } encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder core := zapcore.NewCore( zapcore.NewConsoleEncoder(encoderConfig), w, level, ) logger := zap.New(core) logger.Info("DefaultLogger init success") return logger } func main() { logger := initLogger("/tmp/all.log", "info") logger.Info("test log", zap.Int("line", 47)) } 文件的清理策略如下 Cleaning Up Old Log Files Whenever a new logfile gets created, old log files may be deleted. The most recent files according to the encoded timestamp will be retained, up to a number equal to MaxBackups (or all of them if MaxBackups is 0). Any files with an encoded timestamp older than MaxAge days are deleted, regardless of MaxBackups. Note that the time encoded in the timestamp is the rotation time, which may differ from the last time that file was written to. ...

September 12, 2018 · 2 min

玩转高性能日志库zap (1)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 uber开源的高性能日志库zap, 除了性能远超logrus之外,还有很多诱人的功能,比如支持数据采样、支持通过HTTP服务动态调整日志级别 参考资料1虽然提到了可以通过HTTP服务动态调整日志级别,但是没有给出可用的代码实现,这里给出一个样例。 动态调整日志级别 非常简单直接上代码 package main import ( "fmt" "go.uber.org/zap" "net/http" "time" ) func main() { alevel := zap.NewAtomicLevel() http.HandleFunc("/handle/level", alevel.ServeHTTP) go func() { if err := http.ListenAndServe(":9090", nil); err != nil { panic(err) } }() // 默认是Info级别 logcfg := zap.NewProductionConfig() logcfg.Level = alevel logger, err := logcfg.Build() if err != nil { fmt.Println("err", err) } defer logger.Sync() for i := 0; i < 1000; i++ { time.Sleep(1 * time.Second) logger.Debug("debug log", zap.String("level", alevel.String())) logger.Info("Info log", zap.String("level", alevel.String())) } } 查看日志级别 ...

September 12, 2018 · 1 min