# Go 語言之自定義 zap 日誌 [zap 日誌](https://github.com/uber-go/zap):https://github.com/uber-go/zap ## 一、日誌寫入文件 - `zap.NewProduction`、`zap.NewDevelopment` 是預設配 ...
Go 語言之自定義 zap 日誌
zap 日誌:https://github.com/uber-go/zap
一、日誌寫入文件
zap.NewProduction
、zap.NewDevelopment
是預設配置好的。zap.New
可自定義配置
zap.New
源碼
這是構造Logger最靈活的方式,但也是最冗長的方式。
對於典型的用例,高度固執己見的預設(NewProduction、NewDevelopment和NewExample)或Config結構體更方便。
// New constructs a new Logger from the provided zapcore.Core and Options. If
// the passed zapcore.Core is nil, it falls back to using a no-op
// implementation.
//
// This is the most flexible way to construct a Logger, but also the most
// verbose. For typical use cases, the highly-opinionated presets
// (NewProduction, NewDevelopment, and NewExample) or the Config struct are
// more convenient.
//
// For sample code, see the package-level AdvancedConfiguration example.
func New(core zapcore.Core, options ...Option) *Logger {
if core == nil {
return NewNop()
}
log := &Logger{
core: core,
errorOutput: zapcore.Lock(os.Stderr),
addStack: zapcore.FatalLevel + 1,
clock: zapcore.DefaultClock,
}
return log.WithOptions(options...)
}
zapcore.Core 源碼
// Core is a minimal, fast logger interface. It's designed for library authors
// to wrap in a more user-friendly API.
type Core interface {
LevelEnabler
// With adds structured context to the Core.
With([]Field) Core
// Check determines whether the supplied Entry should be logged (using the
// embedded LevelEnabler and possibly some extra logic). If the entry
// should be logged, the Core adds itself to the CheckedEntry and returns
// the result.
//
// Callers must use Check before calling Write.
Check(Entry, *CheckedEntry) *CheckedEntry
// Write serializes the Entry and any Fields supplied at the log site and
// writes them to their destination.
//
// If called, Write should always log the Entry and Fields; it should not
// replicate the logic of Check.
Write(Entry, []Field) error
// Sync flushes buffered logs (if any).
Sync() error
}
zapcore.AddSync(file)
源碼解析
func AddSync(w io.Writer) WriteSyncer {
switch w := w.(type) {
case WriteSyncer:
return w
default:
return writerWrapper{w}
}
}
type writerWrapper struct {
io.Writer
}
func (w writerWrapper) Sync() error {
return nil
}
type WriteSyncer interface {
io.Writer
Sync() error
}
日誌級別
// A Level is a logging priority. Higher levels are more important.
type Level int8
const (
// DebugLevel logs are typically voluminous, and are usually disabled in
// production.
DebugLevel Level = iota - 1
// InfoLevel is the default logging priority.
InfoLevel
// WarnLevel logs are more important than Info, but don't need individual
// human review.
WarnLevel
// ErrorLevel logs are high-priority. If an application is running smoothly,
// it shouldn't generate any error-level logs.
ErrorLevel
// DPanicLevel logs are particularly important errors. In development the
// logger panics after writing the message.
DPanicLevel
// PanicLevel logs a message, then panics.
PanicLevel
// FatalLevel logs a message, then calls os.Exit(1).
FatalLevel
_minLevel = DebugLevel
_maxLevel = FatalLevel
// InvalidLevel is an invalid value for Level.
//
// Core implementations may panic if they see messages of this level.
InvalidLevel = _maxLevel + 1
)
實操
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"os"
)
// 定義一個全局 logger 實例
// Logger提供快速、分級、結構化的日誌記錄。所有方法對於併發使用都是安全的。
// Logger是為每一微秒和每一個分配都很重要的上下文設計的,
// 因此它的API有意傾向於性能和類型安全,而不是簡便性。
// 對於大多數應用程式,SugaredLogger在性能和人體工程學之間取得了更好的平衡。
var logger *zap.Logger
// SugaredLogger將基本的Logger功能封裝在一個較慢但不那麼冗長的API中。任何Logger都可以通過其Sugar方法轉換為sugardlogger。
//與Logger不同,SugaredLogger並不堅持結構化日誌記錄。對於每個日誌級別,它公開了四個方法:
// - methods named after the log level for log.Print-style logging
// - methods ending in "w" for loosely-typed structured logging
// - methods ending in "f" for log.Printf-style logging
// - methods ending in "ln" for log.Println-style logging
// For example, the methods for InfoLevel are:
//
// Info(...any) Print-style logging
// Infow(...any) Structured logging (read as "info with")
// Infof(string, ...any) Printf-style logging
// Infoln(...any) Println-style logging
var sugarLogger *zap.SugaredLogger
func main() {
// 初始化
InitLogger()
// Sync調用底層Core的Sync方法,刷新所有緩衝的日誌條目。應用程式在退出之前應該註意調用Sync。
// 在程式退出之前,把緩衝區里的日誌刷到磁碟上
defer logger.Sync()
simpleHttpGet("www.baidu.com")
simpleHttpGet("http://www.baidu.com")
}
func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
// NewCore創建一個向WriteSyncer寫入日誌的Core。
// A WriteSyncer is an io.Writer that can also flush any buffered data. Note
// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.
// LevelEnabler決定在記錄消息時是否啟用給定的日誌級別。
// Each concrete Level value implements a static LevelEnabler which returns
// true for itself and all higher logging levels. For example WarnLevel.Enabled()
// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and
// FatalLevel, but return false for InfoLevel and DebugLevel.
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
// New constructs a new Logger from the provided zapcore.Core and Options. If
// the passed zapcore.Core is nil, it falls back to using a no-op
// implementation.
logger = zap.New(core)
// Sugar封裝了Logger,以提供更符合人體工程學的API,但速度略慢。糖化一個Logger的成本非常低,
// 因此一個應用程式同時使用Loggers和SugaredLoggers是合理的,在性能敏感代碼的邊界上在它們之間進行轉換。
sugarLogger = logger.Sugar()
}
func getEncoder() zapcore.Encoder {
// NewJSONEncoder創建了一個快速、低分配的JSON編碼器。編碼器適當地轉義所有欄位鍵和值。
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
// production environments.
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
func getLogWriter() zapcore.WriteSyncer {
// Create創建或截斷指定文件。如果文件已經存在,它將被截斷。如果該文件不存在,則以模式0666(在umask之前)創建。
// 如果成功,返回的File上的方法可以用於IO;關聯的文件描述符模式為O_RDWR。如果有一個錯誤,它的類型將是PathError。
file, _ := os.Create("./test.log")
// AddSync converts an io.Writer to a WriteSyncer. It attempts to be
// intelligent: if the concrete type of the io.Writer implements WriteSyncer,
// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
return zapcore.AddSync(file)
}
func simpleHttpGet(url string) {
// Get向指定的URL發出Get命令。如果響應是以下重定向代碼之一,則Get跟隨重定向,最多可重定向10個:
// 301 (Moved Permanently)
// 302 (Found)
// 303 (See Other)
// 307 (Temporary Redirect)
// 308 (Permanent Redirect)
// Get is a wrapper around DefaultClient.Get.
// 使用NewRequest和DefaultClient.Do來發出帶有自定義頭的請求。
resp, err := http.Get(url)
if err != nil {
// Error在ErrorLevel記錄消息。該消息包括在日誌站點傳遞的任何欄位,以及日誌記錄器上積累的任何欄位。
//logger.Error(
// 錯誤使用fmt。以Sprint方式構造和記錄消息。
sugarLogger.Error(
"Error fetching url..",
zap.String("url", url), // 字元串用給定的鍵和值構造一個欄位。
zap.Error(err)) // // Error is shorthand for the common idiom NamedError("error", err).
} else {
// Info以infollevel記錄消息。該消息包括在日誌站點傳遞的任何欄位,以及日誌記錄器上積累的任何欄位。
//logger.Info("Success..",
// Info使用fmt。以Sprint方式構造和記錄消息。
sugarLogger.Info("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
運行
Code/go/zap_demo via