log包
Go标准库中有log包,提供了简单的日志功能。
输出 | 格式输出 | 换行输出 | 解释 |
log.Print() | log.Printf() | log.Println() | 类似fmt.Print* |
log.Fatal() | log.Fatalf() | log.Fatalln() | 相当于log.Print* + os.Exit(1) |
log.Panic() | log.Panicf() | log.Panicln() | 相当于log.Print* + panic() |
日志输出需要使用日志记录器Logger。 log包提供了一个缺省的Logger即std。std是小写的,包外不可见,所以提供了Default()方法返回std给包外使用。
上表列出的方法底层都使用std.Output输出日志内容。而std本质上是使用了标准错误输出、无前缀、 LstdFlags标准标记的记录器Logger实例。
// 使用缺省Logger log.Print("abcde\n") log.Printf("%s\n", "abcd") log.Println("abc") log.Fatal("xyz") // 等价于 log.Print("xyz");os.Exit(1) log.Panicln("Failed") // 等价于 log.Println("Failed");panic()
自定义Logger
// 自定义Logger infoLogger := log.New(os.Stdout, "Info: ", log.LstdFlags|log.Lmsgprefix) infoLogger.Println("这是一个普通消息") // 使用stdout输出 errLogger := log.New(os.Stderr, "Error: ", log.LstdFlags) errLogger.Fatal("这是一个错误消息")
写日志文件
New方法签名 New(out io.Writer, prefix string, flag int) *Logger 中out参数提供Writer接口即可,那么就可以提供一个可写文件对象。
package main import ( "log" "os" ) func main() { f, err := os.OpenFile("log/test.log", // os.Open为只读打开,OpenFile按照指定方式打开文件 os.O_CREATE|os.O_RDONLY|os.O_APPEND, 0644) // Unix permission bits if err != nil { log.Panicf("%v %v", "文件打开失败:", err) } defer f.Close() logger := log.New(f, "[INFO]", log.Ldate|log.Ltime) logger.Println("开始初始化。。。。") }
zerolog
log模块太简陋了,实际使用并不方便。
- logrus有日志级别、Hook机制、日志格式输出,很好用
- zap是Uber的开源高性能日志库
- zerolog更注重开发体验,高性能、有日志级别、链式API,json格式日志记录,号称0内存分配
安装: go get -u github.com/rs/zerolog/log
级别
zerolog提供以下级别(从高到底)
- panic (zerolog.PanicLevel, 5)
- fatal (zerolog.FatalLevel, 4)
- error (zerolog.ErrorLevel, 3)
- warn (zerolog.WarnLevel, 2)
- info (zerolog.InfoLevel, 1)
- debug (zerolog.DebugLevel, 0)
- trace (zerolog.TraceLevel, -1)
级别有
- gLevel全局级别
- zerolog.SetGlobalLevel(级别数字或常量) 来设置全局级别
- zerolog.GlobalLevel() 获取当前全局级别
- 每个Logger的级别
- 消息的级别
package main import ( "fmt" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) func main() { fmt.Println("全局级别gLevel为", zerolog.GlobalLevel()) fmt.Println("缺省logger的级别为", log.Logger.GetLevel()) log1 := log.Level(zerolog.WarnLevel) // 创建一个子logger fmt.Println("log1级别为", log1.GetLevel()) fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~") log.Trace().Msg("缺省logger输出trace级别消息") // 输出 log.Info().Msg("缺省logger输出info级别消息") // 输出 log.Warn().Msg("缺省logger输出warn级别消息") // 输出 log.Error().Msg("缺省logger输出error级别消息") // 输出 log1.Debug().Msg("log1的Debug级别消息") // 不能输出 log1.Warn().Msg("log1的Warn级别消息") // 输出 log1.Error().Msg("log1的Error级别消息") // 输出 }
因为有消息级别和Logger级别,因此使用缺省logger,全部可以输出日志消息,而log1使22行、23行输出了日志
而缺省Logger的级别是trace,任何消息级别都大于等于log1的级别,因此都可以输出。
package main import ( "fmt" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) func main() { zerolog.SetGlobalLevel(zerolog.ErrorLevel) // 调高全局级别 fmt.Println("全局级别gLevel为", zerolog.GlobalLevel()) fmt.Println("缺省logger的级别为", log.Logger.GetLevel()) log1 := log.Level(zerolog.WarnLevel) // 创建一个子logger fmt.Println("log1级别为", log1.GetLevel()) fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~") log.Trace().Msg("缺省logger输出trace级别消息") // 不能输出 log.Info().Msg("缺省logger输出info级别消息") // 不能输出 log.Warn().Msg("缺省logger输出warn级别消息") // 不能输出 log.Error().Msg("缺省logger输出error级别消息") // 输出 log1.Debug().Msg("log1的Debug级别消息") // 不能输出 log1.Warn().Msg("log1的Warn级别消息") // 不能输出 log1.Error().Msg("log1的Error级别消息") // 输出 }
缺省logger和log1都只有error级别的输出,说明将gLevel调整到error级别后,所有logger输出消息都必 须大于等于gLevel。
zerolog.SetGlobalLevel()设置的是全局变量gLevel,它影响所有Logger。
日志消息是否能够输出,应当满足下面的要求 消息级别 >= max(gLevel, 当前logger级别)
上下文
zerolog是以Json对象格式输出的,还可以自定义一些键值对字段增加到上下文中以输出。
zerolog.SetGlobalLevel(zerolog.InfoLevel) log.Info().Bool("Success", false).Str("Reason", "File Not Found").Msg("文件没 找到") log.Info().Str("Name", "Tom").Floats32("Scores", []float32{87.5, 90, 59}).Send() // Send is equivalent to calling Msg("")
错误日志
package main import ( "errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" // 全局logger ) func main() { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix // 自定义time字段时间的格 式,TimeFormatUnix时间戳 // zerolog.ErrorFieldName = "err" // 修改日志Json中的缺省字段名error // 错误日志 err := errors.New("自定义的错误") log.Error(). // 错误级别消息 Err(err). // err字段,错误消息内容 Send() // 有错误消息了,message可以省略 log.Fatal(). // fatal级别 Err(err). Send() }
全局Logger
// 全局Logger定义如下 var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
可以覆盖全局Logger
// 全局日期格式 zerolog.TimeFieldFormat = zerolog.TimeFormatUnix zerolog.TimeFieldFormat = "2006/01/02 15:04:05 -0700" // With()创建一个全局Logger的子logger log.Logger = log.With().Str("Name", "Cabel").Logger() // 覆盖了全局Logger log.Info().Send() // {"level":"info","Name":"Cabel","time":1223947070}
自定义Logger
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix logger := log.With(). // With()返回基于全局Logger的子logger Str("School", "Mg"). Caller(). // 增加日志调用的位置信息字段 Logger() // 返回Logger logger.Info().Send() // {"level":"info","School":"Mg","time":1223947070} log.Info().Send() // {"level":"info","time":1223947070} 全局Logger
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix logger := zerolog.New(os.Stdout). // 不基于全局Logger,重新构造了一个Logger With().Str("School", "Mg"). Caller(). // 调用者信息:增加日志函数调用的位置信息字段 Logger(). // 返回Logger Level(zerolog.ErrorLevel) // 重新定义Logger级别为3 error,返回Logger fmt.Println(logger.GetLevel()) logger.Info().Send() // {"level":"info","School":"Mg","time":1223947070} logger.Error().Send() log.Info().Send() // {"level":"info","time":1223947070} 全局Logger
写日志文件
package main import ( "os" "github.com/rs/zerolog" "github.com/rs/zerolog/log" // 全局logger ) func main() { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix f, err := os.OpenFile("o:/my.log", os.O_CREATE|os.O_APPEND, os.ModePerm) if err != nil { log.Panic().Err(err).Send() // 内部调用panic } defer f.Close() multi := zerolog.MultiLevelWriter(f, os.Stdout) // 多分支写 // Timestamp()为这个全新的Logger增加时间戳输出 logger := zerolog.New(multi).With().Timestamp().Logger() logger.Info().Msg("日志兵分两路,去控制台stdout,还去日志文件") }
如果只输出到文件可以使用 zerolog.New(f).With().Timestamp().Logger()