← 中文资源

简体中文版本

Slog 结构化 JSON 日志

用标准库 slog 输出结构化日志:JSON 处理器分级输出、With 绑定上下文生成子记录器、属性分组组织嵌套字段。

阅读英文原文

示例说明

printf 风格日志是人类可读、机器难解析的两难。slog 把属性键值化:JSONHandler 输出可直接索引的行,With 派生的子记录器自动携带绑定字段——支付处理器绑定订单号与金额后,内部所有日志天然带上业务上下文。Group 属性把相关字段组织成嵌套对象,风险评分这类复合信息不再平铺。

技术要点

  • With 派生携带绑定上下文的记录器
  • Group 把相关字段组织成嵌套对象
  • Context 系列方法继承追踪上下文

代码解读

根记录器绑定服务与区域两个维度;PaymentHandler 的 With 在每次结算时追加订单与金额。超额走 ErrorContext 附带拒绝原因与 risk 分组;正常路径两条 Info。SetDefault 之后包级函数也共享绑定上下文。handler2 演示按级别分流到标准错误流的形态。

使用建议

级别阈值在生产调 info 以上控制量。上下文字段防高基数,订单号可以用户 ID 不行。自定义 Handler 可实现敏感字段脱敏。

代码示例

package main

import (
	"context"
	"errors"
	"log/slog"
	"os"
)

type PaymentHandler struct {
	logger *slog.Logger
}

func (h *PaymentHandler) Settle(ctx context.Context, orderID string, amount int) error {
	logger := h.logger.With(
		slog.String("order_id", orderID),
		slog.Int("amount_cents", amount),
	)

	logger.InfoContext(ctx, "settlement started")

	if amount > 1_000_000 {
		logger.ErrorContext(ctx, "settlement rejected",
			slog.String("reason", "single-charge limit"),
			slog.Group("risk",
				slog.Int("score", 91),
				slog.String("band", "high"),
			),
		)
		return errAmountTooHigh
	}

	logger.InfoContext(ctx, "settlement completed")
	return nil
}

var errAmountTooHigh = errors.New("amount exceeds single-charge limit")

func main() {
	handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})

	logger := slog.New(handler).With(
		slog.String("service", "billing"),
		slog.String("region", "cn-north"),
	)

	payments := &PaymentHandler{logger: logger}
	_ = payments.Settle(context.Background(), "ord-42", 250_000)
	_ = payments.Settle(context.Background(), "ord-43", 2_500_000)

	slog.SetDefault(logger)
	slog.Info("default logger now carries bound context")
}