golang实现slog日志格式化与自定义构建插件库slog-formatter的使用

Golang实现Slog日志格式化与自定义构建插件库slog-formatter的使用

简介

slog-formatter是一个为Go标准库slog提供常见格式化器和构建自定义格式化器的插件库。它可以帮助你:

  • 格式化日志属性(如时间、错误、HTTP请求等)
  • 构建自定义的日志格式化逻辑
  • 保护敏感信息(如PII、IP地址等)

安装

go get github.com/samber/slog-formatter

兼容性:Go >= 1.21

快速开始

以下示例展示了如何使用3个格式化器:匿名化数据、格式化错误和格式化用户:

import (
    slogformatter "github.com/samber/slog-formatter"
    "log/slog"
)

formatter1 := slogformatter.FormatByKey("very_private_data", func(v slog.Value) slog.Value {
    return slog.StringValue("***********")
})
formatter2 := slogformatter.ErrorFormatter("error")
formatter3 := slogformatter.FormatByType(func(u User) slog.Value {
    return slog.StringValue(fmt.Sprintf("%s %s", u.firstname, u.lastname))
})

logger := slog.New(
    slogformatter.NewFormatterHandler(formatter1, formatter2, formatter3)(
        slog.NewTextHandler(os.Stdout, nil),
    ),
)

err := fmt.Errorf("an error")
logger.Error("a message",
    slog.Any("very_private_data", "abcd"),
    slog.Any("user", user),
    slog.Any("err", err))

// 输出:
// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="a message" error.message="an error" error.type="*errors.errorString" user="John doe" very_private_data="********"

核心功能

处理器

  1. NewFormatterHandler: 主处理器,应用格式化器
  2. NewFormatterMiddleware: 与slog-multi中间件兼容
  3. RecoverHandlerError: 捕获处理链中的panic和错误

常见格式化器

  1. TimeFormatter: 将time.Time转换为可读字符串
  2. UnixTimestampFormatter: 将time.Time转换为Unix时间戳
  3. TimezoneConverter: 将time.Time转换为不同时区
  4. ErrorFormatter: 格式化Go错误
  5. HTTPRequestFormatter/HTTPResponseFormatter: 格式化HTTP请求/响应
  6. PIIFormatter: 隐藏个人身份信息(PII)
  7. IPAddressFormatter: 隐藏IP地址
  8. FlattenFormatterMiddleware: 递归扁平化属性

自定义格式化器

  1. Format: 对所有属性应用格式化器
  2. FormatByKind: 对匹配slog.Kind的属性应用格式化器
  3. FormatByType: 对匹配特定类型的属性应用格式化器
  4. FormatByKey: 对匹配键名的属性应用格式化器
  5. FormatByFieldType: 对匹配键名和类型的属性应用格式化器
  6. FormatByGroup: 对组内属性应用格式化器
  7. FormatByGroupKey: 对组内匹配键名的属性应用格式化器
  8. FormatByGroupKeyType: 对组内匹配键名和类型的属性应用格式化器

完整示例

1. 格式化时间和错误

logger := slog.New(
    slogformatter.NewFormatterHandler(
        slogformatter.TimeFormatter(time.DateTime, time.UTC),
        slogformatter.ErrorFormatter("error"),
    )(
        slog.NewJSONHandler(os.Stdout, nil),
    ),
)

err := fmt.Errorf("database connection failed")
logger.Error("operation failed", slog.Any("error", err))

2. 保护敏感信息

logger := slog.New(
    slogformatter.NewFormatterHandler(
        slogformatter.PIIFormatter("user"),
        slogformatter.IPAddressFormatter("ip"),
    )(
        slog.NewTextHandler(os.Stdout, nil),
    ),
)

logger.Info("user login", 
    slog.String("ip", "192.168.1.1"),
    slog.Group("user",
        slog.String("email", "user@example.com"),
        slog.String("password", "secret123"),
    ))

3. 自定义类型格式化

type Product struct {
    ID    string
    Name  string
    Price float64
}

logger := slog.New(
    slogformatter.NewFormatterHandler(
        slogformatter.FormatByType(func(p Product) slog.Value {
            return slog.GroupValue(
                slog.String("id", p.ID),
                slog.String("name", p.Name),
            )
        }),
    )(
        slog.NewJSONHandler(os.Stdout, nil),
    ),
)

product := Product{ID: "123", Name: "Laptop", Price: 999.99}
logger.Info("new product", slog.Any("item", product))

4. HTTP请求日志记录

logger := slog.New(
    slogformatter.NewFormatterHandler(
        slogformatter.HTTPRequestFormatter(true),
        slogformatter.HTTPResponseFormatter(true),
    )(
        slog.NewJSONHandler(os.Stdout, nil),
    ),
)

req, _ := http.NewRequest("GET", "https://api.example.com", nil)
res, _ := http.DefaultClient.Do(req)

logger.Info("API call",
    slog.Any("request", req),
    slog.Any("response", res))

注意事项

  1. 在某些情况下,考虑实现slog.LogValuer接口而不是使用此库
  2. 谨慎使用此库,日志处理可能非常消耗资源
  3. 在v2.0.0之前不会对导出API进行破坏性更改

slog-formatter提供了强大的日志格式化能力,可以帮助你创建更清晰、更安全的日志输出。根据你的具体需求选择合适的格式化器组合使用。


更多关于golang实现slog日志格式化与自定义构建插件库slog-formatter的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现slog日志格式化与自定义构建插件库slog-formatter的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang Slog日志格式化与自定义构建插件库

Slog日志格式化基础

Go 1.21引入了新的结构化日志库slog,它提供了高性能的结构化日志记录功能。下面是一个基本的使用示例:

package main

import (
    "log/slog"
    "os"
)

func main() {
    // 基本文本输出
    textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil))
    textLogger.Info("hello world", "count", 3)
    
    // JSON格式输出
    jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    jsonLogger.Info("hello world", "count", 3)
}

自定义日志格式化

要实现自定义格式化,我们需要创建自己的Handler:

package main

import (
    "context"
    "fmt"
    "io"
    "log/slog"
    "os"
    "time"
)

type CustomHandler struct {
    handler slog.Handler
    writer  io.Writer
}

func (h *CustomHandler) Enabled(ctx context.Context, level slog.Level) bool {
    return h.handler.Enabled(ctx, level)
}

func (h *CustomHandler) Handle(ctx context.Context, r slog.Record) error {
    // 自定义格式
    timeStr := r.Time.Format(time.DateTime)
    msg := fmt.Sprintf("[%s] %s: %s", timeStr, r.Level, r.Message)
    
    // 添加属性
    r.Attrs(func(attr slog.Attr) bool {
        msg += fmt.Sprintf(" %s=%v", attr.Key, attr.Value)
        return true
    })
    
    msg += "\n"
    
    _, err := h.writer.Write([]byte(msg))
    return err
}

func (h *CustomHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
    return &CustomHandler{handler: h.handler.WithAttrs(attrs), writer: h.writer}
}

func (h *CustomHandler) WithGroup(name string) slog.Handler {
    return &CustomHandler{handler: h.handler.WithGroup(name), writer: h.writer}
}

func NewCustomHandler(w io.Writer) *CustomHandler {
    return &CustomHandler{
        writer: w,
    }
}

func main() {
    logger := slog.New(NewCustomHandler(os.Stdout))
    logger.Info("custom formatted message", "key1", "value1", "key2", 123)
}

使用slog-formatter插件库

slog-formatter是一个第三方库,提供了更多灵活的格式化选项。首先安装:

go get github.com/samber/slog-formatter

使用示例:

package main

import (
    "os"
    "time"

    "github.com/samber/slog-formatter"
    "log/slog"
)

func main() {
    // 1. 基础格式化器
    logger := slog.New(
        formatter.NewFormatterHandler(
            formatter.TimeFormatter(time.DateTime),
            formatter.LevelFormatter("LEVEL"),
            formatter.MessageFormatter("MESSAGE"),
            formatter.AttrsFormatter("ATTRS"),
        )(
            slog.NewJSONHandler(os.Stdout, nil),
        ),
    )
    
    logger.Info("hello world", "count", 3)
    
    // 2. 彩色输出
    logger = slog.New(
        formatter.NewColorFormatter(
            os.Stdout,
            formatter.ColorFormatterOpts{
                Time:    formatter.ColorTime(time.DateTime),
                Level:   formatter.ColorLevel(),
                Message: formatter.ColorMessage(),
            },
        ),
    )
    
    logger.Info("colored message", "key", "value")
    
    // 3. 自定义模板
    logger = slog.New(
        formatter.NewTemplateFormatter(
            os.Stdout,
            "{{.Time}} | {{.Level}} | {{.Message}} | {{range .Attrs}}{{.Key}}={{.Value}} {{end}}\n",
        ),
    )
    
    logger.Warn("template message", "user", "Alice", "age", 30)
}

高级自定义插件开发

如果你想开发自己的slog插件,可以遵循以下模式:

package myformatter

import (
    "context"
    "log/slog"
)

type MyFormatter struct {
    next slog.Handler
    // 添加你的配置字段
    prefix string
}

func NewMyFormatter(next slog.Handler, prefix string) *MyFormatter {
    return &MyFormatter{
        next:   next,
        prefix: prefix,
    }
}

func (f *MyFormatter) Enabled(ctx context.Context, level slog.Level) bool {
    return f.next.Enabled(ctx, level)
}

func (f *MyFormatter) Handle(ctx context.Context, r slog.Record) error {
    // 修改记录
    r.Message = f.prefix + r.Message
    
    // 可以添加或修改属性
    r.AddAttrs(slog.String("formatted_by", "my_formatter"))
    
    return f.next.Handle(ctx, r)
}

func (f *MyFormatter) WithAttrs(attrs []slog.Attr) slog.Handler {
    return NewMyFormatter(f.next.WithAttrs(attrs), f.prefix)
}

func (f *MyFormatter) WithGroup(name string) slog.Handler {
    return NewMyFormatter(f.next.WithGroup(name), f.prefix)
}

使用示例:

package main

import (
    "log/slog"
    "os"
    "your/package/path/myformatter"
)

func main() {
    logger := slog.New(
        myformatter.NewMyFormatter(
            slog.NewJSONHandler(os.Stdout, nil),
            "[PREFIX] ",
        ),
    )
    
    logger.Info("custom formatted message")
}

总结

  1. Go的slog库提供了强大的结构化日志功能
  2. 可以通过实现slog.Handler接口来自定义日志格式
  3. 第三方库如slog-formatter提供了更多现成的格式化选项
  4. 开发自定义插件需要实现Handler接口并包装现有的Handler

选择哪种方式取决于你的需求:简单自定义可以直接实现Handler,复杂需求可以使用现有插件库,特定需求可以开发自己的插件。

回到顶部