[AI生成] 基于slog日志打印

slog是go内置的日志标准库。
支持初始化全局日志设置并打印json日志。

// Package main —— slog global logger demo:
//   - JSON format output
//   - Beijing time (CST, UTC+8)
//   - Source line number (relative path)
//   - Request ID (req_id) field
//
// Run: go run ./slog-demo/
package main

import (
	"log/slog"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Beijing time location (CST, UTC+8)
var beijingLoc = time.FixedZone("CST", 8*3600)

// Current working directory — used to shorten source.file to a relative path
var wd, _ = os.Getwd()

// init configures the global slog.Logger at package load time
func init() {
	opts := &slog.HandlerOptions{
		Level:     slog.LevelDebug, // Show all levels including Debug
		AddSource: true,            // Record the source location (file + line)
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			// 1) Convert time to Beijing time
			if a.Key == slog.TimeKey {
				a.Value = slog.StringValue(a.Value.Time().In(beijingLoc).Format("2006-01-02 15:04:05.000"))
			}

			// 2) "msg" → "message"
			if a.Key == slog.MessageKey {
				a.Key = "message"
			}

			// 3) "level" keeps the default field name (slog default: "level"

			// 4) source.file: convert absolute path to relative path
			if a.Key == slog.SourceKey {
				s := a.Value.Any().(*slog.Source)
				file := s.File

				if wd != "" {
					if rel, err := filepath.Rel(wd, file); err == nil {
						file = rel
					}
				}

				// Fallback: keep only the last 2 path components
				if strings.Contains(file, string(filepath.Separator)) {
					parts := strings.Split(file, string(filepath.Separator))
					if len(parts) >= 2 {
						file = strings.Join(parts[len(parts)-2:], "/")
					}
				}

				file = filepath.ToSlash(file)

				return slog.Attr{
					Key: a.Key,
					Value: slog.GroupValue(
						slog.String("file", file),
						slog.Int("line", s.Line),
					),
				}
			}

			return a
		},
	}

	slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, opts)))
	slog.Info("global slog.Logger initialized",
		slog.String("timezone", "CST (UTC+8)"),
		slog.String("format", "JSON"),
	)
}

// ============================================================
// Business scenario: simulate a login request handler
// ============================================================

// handleLogin simulates a login request.
// For each request, create a child logger with req_id — every log in this
// request will automatically carry the req_id field.
func handleLogin(reqID, username, ip string) {
	log := slog.With(slog.String("req_id", reqID))

	log.Info("start processing login request",
		slog.String("username", username),
		slog.String("client_ip", ip),
	)

	log.Debug("querying users table",
		slog.String("table", "users"),
		slog.String("query", "SELECT * FROM users WHERE username = ?"),
	)

	log.Info("login succeeded",
		slog.String("username", username),
		slog.Int("user_id", 10086),
	)
}

// ============================================================
// Main
// ============================================================
func main() {
	slog.Info("application started")

	handleLogin("req-001", "alice", "192.168.1.10")
	handleLogin("req-002", "bob", "192.168.1.20")

	slog.Info("application exited normally")
}

image

posted on 2026-06-21 10:32  王景迁  阅读(2)  评论(0)    收藏  举报

导航