flag 与 log/slog
本教程共 80 篇 · 第 77 篇 · 更新于 2026-07-27 · 约 8 分钟阅读
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]
NoteGo 的 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/Loki | slog JSON 输出 |
Tip新项目直接上
slog。log主要用在老代码或极简场景。
小结
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 包。