最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

快速體驗

以下是 項目中 已經用slog替換 zap 后的 logger 使用方法,與替換前使用方式相同,無任何感知

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)                // 設置等級
    logger.Debugw("debug hello slog", "key", "value") // 設置了等級之后展示 debug
    // with
    newLog := logger.With("newkey", "newValue")
    newLog.Debugw("new hello slog") // 會打印 newkey:newValue
    logger.Debugw("old hello slog") // 不會打印 newkey:newValue
}

slog 基礎使用

Go 1.21版本中 將 golang.org/x/exp/slog 引入了go標準庫 路徑為 log/slog。

新項目的 如果不使用第三方包,可以直接用slog當你的 logger

slog 簡單示例

默認 輸出級別是info以上,所以debug是打印不出來.

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:設置日志等級 AddSource:打印文件相關信息

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 切換日志等級

看 slog源碼 HandlerOptions 的 Level 是一個 interface,slog 自帶的 slog.LevelVar 實現(xiàn)了這個 interface,也可以自己定義實現(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))
}

通過 slog.LevelVar 設置debug等級后,第二次的debug日志是可以打印出來

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")
}

想要實現(xiàn) 文章開頭 通過 logger.SetLevel(logger.DebugLevel) 快速切換等級,可以選擇將slog.Logger 與 slog.LevelVar 封裝到同一結構,比如

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

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

原有 logger zap實現(xiàn)

原有項目已經實現(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 實現(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 之類其他實現(xiàn)接口的方法 
}

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

package logger
// 全局 log,也可以單獨 NewLogger 獲取新的實例
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 之類

在項目中通過 如下使用 logger

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

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

logger interface 接口保持不變

slog 實現(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 會使用自定的方法給日志 增加字段 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 之類的
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 獲取調用方的文件和文件號
// slog 原生 暫不支持 callerSkip,使用此函數(shù)啃根會有性能問題,最好等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,也可以單獨 NewLogger 獲取新的實例
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 之類

一樣可以 通過 如下使用 logger,與使用zap時一樣

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

slog 實現(xiàn) callerSkip 功能

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

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

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

可能會有性能問題,希望slog能對外提供一個 callerSkip 參數(shù)

說明

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

如要查看logger實現(xiàn)細節(jié),可查看 在文章開頭 快速體驗 引用的包

也可以直接看下我這個 倉庫 go-moda 里使用 slog 和 zap 的封裝

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

相關文章

  • 詳解Golang編程中的常量與變量

    詳解Golang編程中的常量與變量

    這篇文章主要介紹了詳解Golang編程中的常量與變量,是Go語言入門學習中的基礎知識,需要的朋友可以參考下
    2015-10-10
  • golang獲取客戶端ip的實現(xiàn)

    golang獲取客戶端ip的實現(xiàn)

    本文主要介紹了golang獲取客戶端ip的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07
  • 基于golang中container/list包的用法說明

    基于golang中container/list包的用法說明

    這篇文章主要介紹了基于golang中container/list包的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 看看你的Go應用是否用了正確CPU核數(shù)

    看看你的Go應用是否用了正確CPU核數(shù)

    這篇文章主要為大家介紹了Go應用正確的CPU核數(shù)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 淺談Golang如何使用Viper進行配置管理

    淺談Golang如何使用Viper進行配置管理

    在Golang生態(tài)中,Viper是一個不錯的開源配置管理框架,這篇文章主要為大家介紹了Golang如何使用Viper進行配置管理,需要的可以參考一下
    2023-06-06
  • golang高并發(fā)的深入理解

    golang高并發(fā)的深入理解

    golang從語言級別上對并發(fā)提供了支持,而且在啟動并發(fā)的方式上直接添加了語言級的關鍵字。下面這篇文章主要給大家介紹了關于golang高并發(fā)的相關資料,需要的朋友可以參考下
    2019-03-03
  • Go并發(fā)編程sync.Cond的具體使用

    Go并發(fā)編程sync.Cond的具體使用

    Go 標準庫提供 Cond 原語的目的是,為等待 / 通知場景下的并發(fā)問題提供支持,本文主要介紹了Go并發(fā)編程sync.Cond的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2022-05-05
  • Go語言學習之操作MYSQL實現(xiàn)CRUD

    Go語言學習之操作MYSQL實現(xiàn)CRUD

    Go官方提供了database包,database包下有sql/driver。該包用來定義操作數(shù)據(jù)庫的接口,這保證了無論使用哪種數(shù)據(jù)庫,操作方式都是相同的。本文就來和大家聊聊Go語言如何操作MYSQL實現(xiàn)CRUD,希望對大家有所幫助
    2023-02-02
  • golang值接收者和指針接收者的區(qū)別介紹

    golang值接收者和指針接收者的區(qū)別介紹

    這篇文章主要介紹了golang值接收者和指針接收者的區(qū)別,它和函數(shù)的區(qū)別在于方法有一個接收者,給一個函數(shù)添加一個接收者,那么它就變成了方法,接收者可以是值接收者,也可以是指針接收者,本文通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • 深入理解Golang的反射reflect示例

    深入理解Golang的反射reflect示例

    本文主要介紹了Golang的反射reflect示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評論

盖州市| 彩票| 原平市| 莒南县| 凤阳县| 湖南省| 沙洋县| 曲沃县| 娱乐| 永昌县| 河池市| 和龙市| 金塔县| 洛扎县| 苏尼特右旗| 崇仁县| 银川市| 分宜县| 韶山市| 崇州市| 恩平市| 曲麻莱县| 镇赉县| 巨野县| 登封市| 上高县| 新郑市| 大同市| 安泽县| 阿拉善左旗| 嵊州市| 樟树市| 岱山县| 墨脱县| 义马市| 论坛| 新营市| 娱乐| 中方县| 武山县| 甘洛县|