golang结构化日志log/slog包之LogValuer的用法简介

 更新时间:2023年10月18日 08:41:17   作者:路多辛  
这篇文章主要为大家详细介绍了golang结构化日志log/slog包中 LogValuer 和日志记录函数的正确包装方法,感兴趣的小伙伴可以跟随小编一起了解一下

上一篇文章讲解了 log/slog 包中的分组、上下文和属性值类型,本文讲解下 LogValuer 和日志记录函数的正确包装方法。

slog.LogValuer

如果想改变或者自定义一个类型的日志记录行为,可以通过实现 slog.LogValuer 接口来实现,slog.LogValuer 接口的定义如下:

type LogValuer interface {
    LogValue() Value
}

定义了一个 LogValue 方法,返回一个 Value 类型的对象。如果一个类型实现了 LogValuer 接口,那么从它的 LogValue 方法返回的 Value 将被用于日志记录,可以用来控制该类型的值在日志中的显示方式。看个简单示例:

package main
 
import (
    "log/slog"
    "os"
)
 
type Token string
 
// 实现 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)
}

输出内容如下:

time=2023-10-15T15:06:58.253+08:00 level=INFO msg="生成 token" 用户=路多辛的博客 token=******

从安全角度看,密码或者token 等敏感信息是不能被记录在日志里面的,可以使用自定义的并且实现了 LogValue 的类型来避免这种情况产生。在这个例子中,当记录 token 日志时,token 会被转换为“******”后记录在日志里面。再看一个结合字段分组使用的示例:

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("任务结束", "agent", n)
}

输出内容如下:

2023/10/15 15:06:09 INFO 任务结束 agent.first=路多辛的博客 agent.last=路多辛的所思所想

包装输出函数

日志记录函数使用调用堆栈上的反射来查找应用程序中日志记录调用的文件名和行号,这可能会导致包装 slog 的函数记录错误的的源信息。举个例子,如果在 mylog.go 中定义了一个日志记录函数 Infof,然后在 main.go 中调用了此函数,这种情况下,日志会将把源文件记录为 mylog.go 而不是 main.go。正确实现 Infof 函数的方式是将获取的源信息传递给 NewRecord 函数,示例代码如下:

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结构化日志log/slog包之LogValuer的用法简介的文章就介绍到这了,更多相关go slog内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go语言解决并发问题小结

    go语言解决并发问题小结

    并发是GO最基本的功能了,但是在传统的PHP中是比较困难的,如果不借助其它一些扩展的话,是做不到并发的,这篇文章主要介绍了go语言如何解决并发问题,需要的朋友可以参考下
    2024-05-05
  • golang中的单引号转义问题

    golang中的单引号转义问题

    这篇文章主要介绍了golang中的单引号转义问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • goland中导包报红和go mod问题

    goland中导包报红和go mod问题

    这篇文章主要介绍了goland中导包报红和go mod问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 使用Go语言创建error的几种方式小结

    使用Go语言创建error的几种方式小结

    Go语言函数(或方法)是支持多个返回值的,因此在Go语言的编程哲学中,函数的返回值的最后一个通常都是error类型,所以本文给大家介绍了使用Go语言创建error的几种方式小结,文中通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-01-01
  • Go之集合slice的实现

    Go之集合slice的实现

    本文主要介绍了Go之集合slice的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • golangci-lint安装与Goland集成问题

    golangci-lint安装与Goland集成问题

    这篇文章主要介绍了golangci-lint安装与Goland集成,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • go redis实现滑动窗口限流的方式(redis版)

    go redis实现滑动窗口限流的方式(redis版)

    这篇文章主要介绍了go redis实现滑动窗口限流的方式(redis版),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Golang实现smtp邮件发送的示例代码

    Golang实现smtp邮件发送的示例代码

    这篇文章主要为大家详细介绍了Golang实现smtp邮件发送的相关知识,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • 如何通过Golang的container/list实现LRU缓存算法

    如何通过Golang的container/list实现LRU缓存算法

    文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向链表来管理数据,确保在插入和删除操作时能够以O(1)的平均时间复杂度运行,提供了链表的操作和使用场景,并附带了实现LRU缓存的代码示例,感兴趣的朋友一起看看吧
    2025-03-03
  • Golang中堆排序的实现

    Golang中堆排序的实现

    堆是一棵基于数组实现的特殊的完全二叉树,本文主要介绍了Golang中堆排序的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04

最新评论