欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實(shí)現(xiàn)callerSkip詳解

 更新時(shí)間:2023年09月08日 11:51:14   作者:莫大  
這篇文章主要為大家介紹了go?logger不侵入業(yè)務(wù)代碼使用slog替換zap并實(shí)現(xiàn)callerSkip詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

快速體驗(yàn)

以下是 項(xiàng)目中 已經(jīng)用slog替換 zap 后的 logger 使用方法,與替換前使用方式相同,無(wú)任何感知

package main
import "github.com/webws/go-moda/logger"
func main() {
    // 格式化打印 {"time":"2023-09-08T01:25:21.313463+08:00","level":"INFO","msg":"info hello slog","key":"value","file":"/Users/xxx/w/pro/go-moda/example/logger/main.go","line":6}
    logger.Infow("info hello slog", "key", "value")   // 打印json
    logger.Debugw("debug hello slog", "key", "value") // 不展示
    logger.SetLevel(logger.DebugLevel)                // 設(shè)置等級(jí)
    logger.Debugw("debug hello slog", "key", "value") // 設(shè)置了等級(jí)之后展示 debug
    // with
    newLog := logger.With("newkey", "newValue")
    newLog.Debugw("new hello slog") // 會(huì)打印 newkey:newValue
    logger.Debugw("old hello slog") // 不會(huì)打印 newkey:newValue
}

slog 基礎(chǔ)使用

Go 1.21版本中 將 golang.org/x/exp/slog 引入了go標(biāo)準(zhǔn)庫(kù) 路徑為 log/slog。

新項(xiàng)目的 如果不使用第三方包,可以直接用slog當(dāng)你的 logger

slog 簡(jiǎn)單示例

默認(rèn) 輸出級(jí)別是info以上,所以debug是打印不出來(lái).

import "log/slog"
func main() {
    slog.Info("finished", "key", "value")
    slog.Debug("finished", "key", "value")
}

輸出

2023/09/08 00:27:24 INFO finished key=value

slog 格式化

HandlerOptions Level:設(shè)置日志等級(jí) AddSource:打印文件相關(guān)信息

func main() {
    opts := &slog.HandlerOptions{AddSource: true, Level: slog.LevelInfo}
    logger := slog.New(slog.NewJSONHandler(os.Stdout, opts))
    logger.Info("finished", "key", "value")
}

輸出

{"time":"2023-09-08T00:34:22.035962+08:00","level":"INFO","source":{"function":"callvis/slog.TestLogJsonHandler","file":"/Users/websong/w/pro/go-note/slog/main_test.go","line":39},"msg":"finished","key":"value"}

slog 切換日志等級(jí)

看 slog源碼 HandlerOptions 的 Level 是一個(gè) interface,slog 自帶的 slog.LevelVar 實(shí)現(xiàn)了這個(gè) interface,也可以自己定義實(shí)現(xiàn) 下面是部分源碼

type Leveler interface {
    Level() Level
}
type LevelVar struct {
    val atomic.Int64
}
// Level returns v's level.
func (v *LevelVar) Level() Level {
    return Level(int(v.val.Load()))
}
// Set sets v's level to l.
func (v *LevelVar) Set(l Level) {
    v.val.Store(int64(l))
}

通過(guò) slog.LevelVar 設(shè)置debug等級(jí)后,第二次的debug日志是可以打印出來(lái)

func main() {
    levelVar := &slog.LevelVar{}
    levelVar.Set(slog.LevelInfo)
    opts := &slog.HandlerOptions{AddSource: true, Level: levelVar}
    logger := slog.New(slog.NewJSONHandler(os.Stdout, opts))
    logger.Info("finished", "key", "value")
    levelVar.Set(slog.LevelDebug)
    logger.Debug("finished", "key", "value")
}

想要實(shí)現(xiàn) 文章開(kāi)頭 通過(guò) logger.SetLevel(logger.DebugLevel) 快速切換等級(jí),可以選擇將slog.Logger 與 slog.LevelVar 封裝到同一結(jié)構(gòu),比如

type SlogLogger struct {
    logger *slog.Logger
    level  *slog.LevelVar
}

下文 slog 替換 zap 有詳細(xì)代碼體現(xiàn)

原有 logger zap實(shí)現(xiàn)

原有項(xiàng)目已經(jīng)實(shí)現(xiàn)了一套logger,使用zap log 以下代碼都是在 logger 包下

原zap代碼

logger interface LoggerInterface

package logger
type LoggerInterface interface {
    Debugw(msg string, keysAndValues ...interface{})
    Infow(msg string, keysAndValues ...interface{})
    Errorw(msg string, keysAndValues ...interface{})
    Fatalw(msg string, keysAndValues ...interface{})
    SetLevel(level Level)
    With(keyValues ...interface{}) LoggerInterface
}

zap log 實(shí)現(xiàn) LoggerInterface

type ZapSugaredLogger struct {
    logger    *zap.SugaredLogger
    zapConfig *zap.Config
}
func buildZapLog(level Level) LoggerInterface {
    encoderConfig := zapcore.EncoderConfig{
        TimeKey:        "ts",
        LevelKey:       "level",
        NameKey:        "logger",
        CallerKey:      "caller",
        MessageKey:     "msg",
        StacktraceKey:  "stacktrace",
        LineEnding:     zapcore.DefaultLineEnding,
        EncodeDuration: zapcore.SecondsDurationEncoder,
        EncodeTime:     zapcore.ISO8601TimeEncoder,
        EncodeLevel:    zapcore.LowercaseLevelEncoder,
        EncodeCaller:   zapcore.ShortCallerEncoder,
    }
    zapConfig := &zap.Config{
        Level:             zap.NewAtomicLevelAt(zapcore.Level(level)),
        Development:       true,
        DisableCaller:     false,
        DisableStacktrace: true,
        Sampling:          &zap.SamplingConfig{Initial: 100, Thereafter: 100},
        Encoding:          "json",
        EncoderConfig:     encoderConfig,
        OutputPaths:       []string{"stderr"},
        ErrorOutputPaths:  []string{"stderr"},
    }
    l, err := zapConfig.Build(zap.AddCallerSkip(2))
    if err != nil {
        fmt.Printf("zap build logger fail err=%v", err)
        return nil
    }
    return &ZapSugaredLogger{
        logger:    l.Sugar(),
        zapConfig: zapConfig,
    }
    func (l *ZapSugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
    l.logger.Debugw(msg, keysAndValues...)
    }
    func (l *ZapSugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
        l.logger.Errorw(msg, keysAndValues...)
    }
    // ...省略info 之類(lèi)其他實(shí)現(xiàn)接口的方法 
}

全局初始化logger,因代碼量太大,以下是偽代碼,主要提供思路

package logger
// 全局 log,也可以單獨(dú) NewLogger 獲取新的實(shí)例
var globalog = newlogger(DebugLevel)
func newlogger(level Level) *Logger {
    l := &Logger{logger: buildZapLog(level)}
    return l
}
func Infow(msg string, keysAndValues ...interface{}) {
    globalog.logger.Infow(msg, keysAndValues...)
}
// ...省略其他全局方法,比如DebugW 之類(lèi)

在項(xiàng)目中通過(guò) 如下使用 logger

import "github.com/webws/go-moda/logger"
func main() {
    logger.Infow("hello", "key", "value")   // 打印json
}

slog 不侵入業(yè)務(wù) 替換zap

logger interface 接口保持不變

slog 實(shí)現(xiàn) 代碼

package logger
import (
    "log/slog"
    "os"
    "runtime"
)
var _ LoggerInterface = (*SlogLogger)(nil)
type SlogLogger struct {
    logger *slog.Logger
    level  *slog.LevelVar
    // true 代表使用slog打印文件路徑,false 會(huì)使用自定的方法給日志 增加字段 file line
    addSource bool
}
// newSlog
func newSlog(level Level, addSource bool) LoggerInterface {
    levelVar := &slog.LevelVar{}
    levelVar.Set(slog.LevelInfo)
    opts := &slog.HandlerOptions{AddSource: addSource, Level: levelVar}
    logger := slog.New(slog.NewJSONHandler(os.Stdout, opts))
    return &SlogLogger{
        logger: logger,
        level:  levelVar,
    }
}
func (l *SlogLogger) Fatalw(msg string, keysAndValues ...interface{}) {
    keysAndValues = l.ApppendFileLine(keysAndValues...)
    l.logger.Error(msg, keysAndValues...)
    os.Exit(1)
}
func (l *SlogLogger) Infow(msg string, keysAndValues ...interface{}) {
    keysAndValues = l.ApppendFileLine(keysAndValues...)
    l.logger.Info(msg, keysAndValues...)
}
// 省略繼承接口的其他方法 DebugW 之類(lèi)的
func (l *SlogLogger) SetLevel(level Level) {
    zapLevelToSlogLevel(level)
    l.level.Set(slog.Level(zapLevelToSlogLevel(level)))
}
// 
func (l *SlogLogger) With(keyValues ...interface{}) LoggerInterface {
    newLog := l.logger.With(keyValues...)
    return &SlogLogger{
        logger: newLog,
        level:  l.level,
    }
}
// ApppendFileLine 獲取調(diào)用方的文件和文件號(hào)
// slog 原生 暫不支持 callerSkip,使用此函數(shù)啃根會(huì)有性能問(wèn)題,最好等slog提供 CallerSkip 的參數(shù)
func (l *SlogLogger) ApppendFileLine(keyValues ...interface{}) []interface{} {
    l.addSource = false
    if !l.addSource {
        var pc uintptr
        var pcs [1]uintptr
        // skip [runtime.Callers, this function, this function's caller]
        runtime.Callers(4, pcs[:])
        pc = pcs[0]
        fs := runtime.CallersFrames([]uintptr{pc})
        f, _ := fs.Next()
        keyValues = append(keyValues, "file", f.File, "line", f.Line)
        return keyValues
    }
    return keyValues
}

全局初始化logger,以下偽代碼

package logger
// 全局 log,也可以單獨(dú) NewLogger 獲取新的實(shí)例
var globalog = newlogger(DebugLevel)
func newlogger(level Level) *Logger {
    l := &Logger{logger: newSlog(level, false)}
    return l
}
func Infow(msg string, keysAndValues ...interface{}) {
    globalog.logger.Infow(msg, keysAndValues...)
}
// ...省略其他全局方法,比如DebugW 之類(lèi)

一樣可以 通過(guò) 如下使用 logger,與使用zap時(shí)一樣

import "github.com/webws/go-moda/logger"
func main() {
    logger.Infow("hello", "key", "value")   // 打印json
}

slog 實(shí)現(xiàn) callerSkip 功能

slog 的 addsource 參數(shù) 會(huì)打印文件名和行號(hào),但 并不能像 zap 那樣支持 callerSkip,也就是說(shuō) 如果將 slog 封裝在 logger 目錄的log.go 文件下,使用logger進(jìn)行打印,展示的文件會(huì)一只是log.go

看了 slog 的源碼, 使用了 runtime.Callers 在內(nèi)部實(shí)現(xiàn)了 callerSkip 功能,但是沒(méi)有對(duì)外暴露 callerSkip 參數(shù)

可以看我上面代碼 自己封裝了一個(gè)方法: ApppendFileLine, 使用 runtime.Callers 獲取到 文件名 和 行號(hào),增加 file 和 line 的key value到日志

可能會(huì)有性能問(wèn)題,希望slog能對(duì)外提供一個(gè) callerSkip 參數(shù)

說(shuō)明

文章中貼的代碼不多,主要提供思路,雖然省略了一些方法和 全局logger的實(shí)現(xiàn)方式

如要查看logger實(shí)現(xiàn)細(xì)節(jié),可查看 在文章開(kāi)頭 快速體驗(yàn) 引用的包

也可以直接看下我這個(gè) 倉(cāng)庫(kù) go-moda 里使用 slog 和 zap 的封裝

以上就是go logger不侵入業(yè)務(wù)代碼使用slog替換zap并實(shí)現(xiàn)callerSkip詳解的詳細(xì)內(nèi)容,更多關(guān)于go logger slog替換zap的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Goland 關(guān)閉自動(dòng)移除未使用的包操作

    Goland 關(guān)閉自動(dòng)移除未使用的包操作

    這篇文章主要介紹了Goland 關(guān)閉自動(dòng)移除未使用的包操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Golang使用gorm實(shí)現(xiàn)分頁(yè)功能的示例代碼

    Golang使用gorm實(shí)現(xiàn)分頁(yè)功能的示例代碼

    在提供列表接口時(shí)一般要用到分頁(yè),對(duì)于存儲(chǔ)在某些數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行分頁(yè)起來(lái)非常的方便,下文給出一個(gè)通過(guò)gorm進(jìn)行分頁(yè)并通過(guò)http返回?cái)?shù)據(jù)的例子,感興趣的小伙幫跟著小編一起來(lái)看看吧
    2024-10-10
  • golang壓縮與解壓縮文件的示例代碼

    golang壓縮與解壓縮文件的示例代碼

    這篇文章主要給大家介紹了golang壓縮與解壓縮文件,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-02-02
  • Go語(yǔ)言的os包中常用函數(shù)初步歸納

    Go語(yǔ)言的os包中常用函數(shù)初步歸納

    這篇文章主要介紹了Go語(yǔ)言的os包中常用函數(shù)初步歸納,用于一些和系統(tǒng)交互功能的實(shí)現(xiàn),需要的朋友可以參考下
    2015-10-10
  • 使用Golang的singleflight防止緩存擊穿的方法

    使用Golang的singleflight防止緩存擊穿的方法

    這篇文章主要介紹了使用Golang的singleflight防止緩存擊穿的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Go語(yǔ)言利用ffmpeg轉(zhuǎn)hls實(shí)現(xiàn)簡(jiǎn)單視頻直播

    Go語(yǔ)言利用ffmpeg轉(zhuǎn)hls實(shí)現(xiàn)簡(jiǎn)單視頻直播

    這篇文章主要為大家介紹了Go語(yǔ)言利用ffmpeg轉(zhuǎn)hls實(shí)現(xiàn)簡(jiǎn)單視頻直播,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • Go開(kāi)發(fā)中有哪幾種無(wú)法恢復(fù)的致命場(chǎng)景分析

    Go開(kāi)發(fā)中有哪幾種無(wú)法恢復(fù)的致命場(chǎng)景分析

    這篇文章主要為大家介紹了Go有哪幾種無(wú)法恢復(fù)的致命場(chǎng)景示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Golang利用自定義模板發(fā)送郵件的方法詳解

    Golang利用自定義模板發(fā)送郵件的方法詳解

    這篇文章主要給大家介紹了關(guān)于Golang利用自定義模板發(fā)送郵件的方法,文中通過(guò)示例代碼將實(shí)現(xiàn)的方法介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的全過(guò)程

    Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的全過(guò)程

    Go語(yǔ)言是Google的開(kāi)源編程語(yǔ)言,廣泛應(yīng)用于云計(jì)算、分布式系統(tǒng)開(kāi)發(fā)等領(lǐng)域,在Linux上也有大量的應(yīng)用場(chǎng)景,這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • GO使用Mutex確保并發(fā)程序正確性詳解

    GO使用Mutex確保并發(fā)程序正確性詳解

    這篇文章主要為大家介紹了GO使用Mutex確保并發(fā)程序正確性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評(píng)論