golang結(jié)構(gòu)化日志log/slog包之LogValuer的用法簡介
上一篇文章講解了 log/slog 包中的分組、上下文和屬性值類型,本文講解下 LogValuer 和日志記錄函數(shù)的正確包裝方法。
slog.LogValuer
如果想改變或者自定義一個類型的日志記錄行為,可以通過實現(xiàn) slog.LogValuer 接口來實現(xiàn),slog.LogValuer 接口的定義如下:
type LogValuer interface { LogValue() Value }
定義了一個 LogValue 方法,返回一個 Value 類型的對象。如果一個類型實現(xiàn)了 LogValuer 接口,那么從它的 LogValue 方法返回的 Value 將被用于日志記錄,可以用來控制該類型的值在日志中的顯示方式??磦€簡單示例:
package main import ( "log/slog" "os" ) type Token string // 實現(xiàn) slog.LogValuer 接口 // 避免泄露 token func (Token) LogValue() slog.Value { return slog.StringValue("******") } func main() { t := Token("shhhh!") logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) logger.Info("生成 token", "用戶", "路多辛的博客", "token", t) }
輸出內(nèi)容如下:
time=2023-10-15T15:06:58.253+08:00 level=INFO msg="生成 token" 用戶=路多辛的博客 token=******
從安全角度看,密碼或者token 等敏感信息是不能被記錄在日志里面的,可以使用自定義的并且實現(xiàn)了 LogValue 的類型來避免這種情況產(chǎn)生。在這個例子中,當記錄 token 日志時,token 會被轉(zhuǎn)換為“******”后記錄在日志里面。再看一個結(jié)合字段分組使用的示例:
package main import ( "log/slog" ) type Name struct { First, Last string } func (n Name) LogValue() slog.Value { return slog.GroupValue( slog.String("first", n.First), slog.String("last", n.Last)) } func main() { n := Name{"路多辛的博客", "路多辛的所思所想"} slog.Info("任務結(jié)束", "agent", n) }
輸出內(nèi)容如下:
2023/10/15 15:06:09 INFO 任務結(jié)束 agent.first=路多辛的博客 agent.last=路多辛的所思所想
包裝輸出函數(shù)
日志記錄函數(shù)使用調(diào)用堆棧上的反射來查找應用程序中日志記錄調(diào)用的文件名和行號,這可能會導致包裝 slog 的函數(shù)記錄錯誤的的源信息。舉個例子,如果在 mylog.go 中定義了一個日志記錄函數(shù) Infof,然后在 main.go 中調(diào)用了此函數(shù),這種情況下,日志會將把源文件記錄為 mylog.go 而不是 main.go。正確實現(xiàn) Infof 函數(shù)的方式是將獲取的源信息傳遞給 NewRecord 函數(shù),示例代碼如下:
package main import ( "context" "fmt" "log/slog" "os" "path/filepath" "runtime" "time" ) func Infof(logger *slog.Logger, format string, args ...any) { if !logger.Enabled(context.Background(), slog.LevelInfo) { return } var pcs [1]uintptr runtime.Callers(2, pcs[:]) // skip [Callers, Infof] r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(format, args...), pcs[0]) _ = logger.Handler().Handle(context.Background(), r) } func main() { replace := func(groups []string, a slog.Attr) slog.Attr { // Remove time. if a.Key == slog.TimeKey && len(groups) == 0 { return slog.Attr{} } // Remove the directory from the source's filename. if a.Key == slog.SourceKey { source := a.Value.Any().(*slog.Source) source.File = filepath.Base(source.File) } return a } logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, ReplaceAttr: replace})) Infof(logger, "message, %s", "formatted") }
到此這篇關于golang結(jié)構(gòu)化日志log/slog包之LogValuer的用法簡介的文章就介紹到這了,更多相關go slog內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go?并發(fā)編程協(xié)程及調(diào)度機制詳情
這篇文章主要介紹了Go并發(fā)編程協(xié)程及調(diào)度機制詳情,協(xié)程是Go語言最大的特色之一,goroutine的實現(xiàn)其實是通過協(xié)程,更多相關內(nèi)容需要的朋友可以參考一下2022-09-09linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境
相信每位開發(fā)者都知道選擇一門開發(fā)語言,免不了需要安裝配置開發(fā)環(huán)境,所以這篇文章給大家分享了linux下使用shell一鍵安裝配置GO語言開發(fā)環(huán)境的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-10-10淺析Go中函數(shù)的健壯性,panic異常處理和defer機制
這篇文章主要為大家詳細介紹了Go中函數(shù)的健壯性,panic異常處理和defer機制的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-10-10golang實現(xiàn)并發(fā)數(shù)控制的方法
下面小編就為大家分享一篇golang實現(xiàn)并發(fā)數(shù)控制的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12