首页 / Go 语言入门教程 / flag 与 log/slog

Go 语言入门教程

flag 与 log/slog

本教程共 80 篇 · 第 77 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程flaglogslog结构化日志命令行参数

77. flag 与 log/slog

本节目标:学会用 flag 解析命令行参数,用 log 打基础日志,用 Go 1.21 的 slog 做结构化日志。

flag 包解析参数

flag 是标准库的命令行参数解析工具:

package main

import (
	"flag"
	"fmt"
)

func main() {
	// 定义参数
	host := flag.String("host", "localhost", "服务器地址")
	port := flag.Int("port", 8080, "端口号")
	debug := flag.Bool("debug", false, "调试模式")

	// 解析
	flag.Parse()

	fmt.Printf("host=%s port=%d debug=%v\n", *host, *port, *debug)
}

运行:

go run main.go -host 0.0.0.0 -port 3000 -debug
# host=0.0.0.0 port=3000 debug=true

go run main.go --help
# 自动打印参数说明

flag.String 等函数返回的是指针,取值用 *host

绑定到变量

也可以先声明变量再绑定:

var port int
flag.IntVar(&port, "port", 8080, "端口号")

效果和 flag.Int 一样,区别是直接操作变量,不用解引用。

位置参数

flag.Parse() 后,flag.Args() 返回非 flag 的位置参数:

flag.Parse()
args := flag.Args() // []string
fmt.Println("位置参数:", args)
go run main.go -port 8080 file1.txt file2.txt
# args = [file1.txt file2.txt]
Note

Go 的 flag 必须放在位置参数前面。go run main.go file.txt -port 8080 里的 -port 不会被解析,因为遇到 file.txt 后 flag 解析就停了。

flag.Func 自定义类型

Go 1.21 起 flag.Func 支持自定义解析逻辑:

var nums []int
flag.Func("nums", "逗号分隔的数字", func(s string) error {
	for _, v := range strings.Split(s, ",") {
		n, err := strconv.Atoi(v)
		if err != nil {
			return err
		}
		nums = append(nums, n)
	}
	return nil
})
go run main.go -nums 1,2,3
# nums = [1 2 3]

log 基础日志

log 包提供简单的日志输出:

import "log"

log.Println("服务启动")           // 带时间戳
log.Printf("端口: %d", 8080)     // 格式化
log.Fatal("致命错误")             // 打印后 os.Exit(1)
log.Panic("崩溃了")               // 打印后 panic

输出:

2026/07/27 15:04:05 服务启动
2026/07/27 15:04:05 端口: 8080

自定义日志器:

logger := log.New(os.Stdout, "APP: ", log.Ldate|log.Ltime|log.Lshortfile)
logger.Println("自定义日志")
// APP: 2026/07/27 15:04:05 main.go:10: 自定义日志

log 简单够用,但不适合生产:没有日志级别、不支持结构化字段、不好被日志系统解析。

slog 结构化日志(Go 1.21+)

Go 1.21 引入 log/slog,支持结构化日志和日志级别:

import "log/slog"

slog.Info("用户登录", "user", "Tom", "ip", "1.2.3.4")
slog.Error("查询失败", "err", err, "query", sql)
slog.Debug("调试信息", "count", 42)

输出(默认文本格式):

time=2026-07-27T15:04:05.000+08:00 level=INFO msg=用户登录 user=Tom ip=1.2.3.4

结构化日志的好处:key-value 形式,日志系统能直接索引和筛选。

日志级别

slog 有四个级别:

slog.Debug("调试详情")   // 开发用,默认不输出
slog.Info("普通信息")
slog.Warn("警告")
slog.Error("错误")

默认级别是 Info,Debug 不输出。要开 Debug 得换一个配了级别的 handler:

slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
	Level: slog.LevelDebug,
})))

这样 slog.Debug 就会输出了。

JSON 格式输出

生产环境用 JSON 方便日志系统处理:

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)

slog.Info("用户登录", "user", "Tom")

输出:

{"time":"2026-07-27T15:04:05.000+08:00","level":"INFO","msg":"用户登录","user":"Tom"}

带上下文记录

slog.With 创建带固定字段的 logger:

logger := slog.With("service", "api", "version", "1.0")
logger.Info("请求处理", "path", "/users", "ms", 42)
logger.Info("响应发送", "status", 200)

每条日志都自动带上 service=api version=1.0,不用每次重复写。

context 集成

slog 支持从 context 取 trace ID 等信息:

// 把 logger 存进 context
ctx := context.WithValue(context.Background(), loggerKey, logger)

// 从 context 取 logger 记录
slog.InfoContext(ctx, "处理完成")

配合中间件可以自动给每个请求的日志加上 trace ID。

配置选项

handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
	Level:     slog.LevelDebug, // 最低输出级别
	AddSource: true,            // 带代码位置
})

logger := slog.New(handler)

log 和 slog 怎么选

场景推荐
小脚本、临时工具log 够用
服务端、生产程序slog
需要日志级别和结构化slog
需要接入 ELK/Lokislog JSON 输出
Tip

新项目直接上 sloglog 主要用在老代码或极简场景。

小结

  • flag.String/Int/Bool 定义参数,返回指针
  • flag.Parse()flag.Args() 取位置参数
  • log 简单,带时间戳,log.Fatal 会退出程序
  • Go 1.21 slog 结构化日志,key-value 形式
  • 四级别:Debug/Info/Warn/Error,默认 Info
  • JSON 格式适合生产,slog.With 附加固定字段
  • 新项目优先用 slog

下一节讲 strings、strconv、sort 包。