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)生。在這個例子中,當(dāng)記錄 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("任務(wù)結(jié)束", "agent", n)
}
輸出內(nèi)容如下:
2023/10/15 15:06:09 INFO 任務(wù)結(jié)束 agent.first=路多辛的博客 agent.last=路多辛的所思所想
包裝輸出函數(shù)
日志記錄函數(shù)使用調(diào)用堆棧上的反射來查找應(yīng)用程序中日志記錄調(diào)用的文件名和行號,這可能會導(dǎ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")
}到此這篇關(guān)于golang結(jié)構(gòu)化日志log/slog包之LogValuer的用法簡介的文章就介紹到這了,更多相關(guān)go slog內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Go語言創(chuàng)建error的幾種方式小結(jié)
Go語言函數(shù)(或方法)是支持多個返回值的,因此在Go語言的編程哲學(xué)中,函數(shù)的返回值的最后一個通常都是error類型,所以本文給大家介紹了使用Go語言創(chuàng)建error的幾種方式小結(jié),文中通過代碼示例講解的非常詳細,需要的朋友可以參考下2024-01-01
go redis實現(xiàn)滑動窗口限流的方式(redis版)
這篇文章主要介紹了go redis實現(xiàn)滑動窗口限流的方式(redis版),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
Golang實現(xiàn)smtp郵件發(fā)送的示例代碼
這篇文章主要為大家詳細介紹了Golang實現(xiàn)smtp郵件發(fā)送的相關(guān)知識,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
如何通過Golang的container/list實現(xiàn)LRU緩存算法
文章介紹了Go語言中container/list包實現(xiàn)的雙向鏈表,并探討了如何使用鏈表實現(xiàn)LRU緩存,LRU緩存通過維護一個雙向鏈表來管理數(shù)據(jù),確保在插入和刪除操作時能夠以O(shè)(1)的平均時間復(fù)雜度運行,提供了鏈表的操作和使用場景,并附帶了實現(xiàn)LRU緩存的代碼示例,感興趣的朋友一起看看吧2025-03-03

