
在 Go 中,直接解引用 nil 指针会引发 panic;本文介绍一种简洁、通用且无需显式 if/else 判断的 nil 安全日志方案——利用 json.Marshal 自动处理 nil 指针,并支持单个变量、多值组合及嵌套结构体的统一打印。
在 go 中,直接解引用 nil 指针会引发 panic;本文介绍一种简洁、通用且无需显式 if/else 判断的 nil 安全日志方案——利用 `json.marshal` 自动处理 nil 指针,并支持单个变量、多值组合及嵌套结构体的统一打印。
Go 语言中,对 nil 指针执行 *ptr 操作会导致运行时 panic,因此在日志输出前必须进行 nil 检查。虽然可借助 if ptr != nil { fmt.Println(*ptr) } else { fmt.Println("nil") } 实现,但重复编写此类逻辑既冗余又易出错。更优雅的替代方案是利用 Go 标准库的 encoding/json:json.Marshal 对 nil 指针自动序列化为 null,对非 nil 指针则递归序列化其指向的值,整个过程安全、无 panic,且天然支持任意嵌套结构。
以下是一个实用的封装函数示例:
import (
"encoding/json"
"log"
)
func printJSON(i ...interface{}) {
if b, err := json.Marshal(i); err == nil {
log.Println(string(b))
} else {
log.Printf("JSON marshal error: %v", err)
}
}
配合结构体使用效果更佳:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
type Line struct {
Point1 *string
Point2 *string
Name string
}
func main() {
p := "hi"
point2 := &p
line := Line{Point1: nil, Point2: point2, Name: "demo"}
// 单个指针或混合类型(自动处理 nil)
printJSON(line.Point1, line.Point2, line.Name)
// 输出: [null,"hi","demo"]
// 整个结构体(nil 字段转为 null,非 nil 正常展开)
printJSON(line)
// 输出: {"Point1":null,"Point2":"hi","Name":"demo"}
}
⚠️ 注意事项:
-
json.Marshal会忽略未导出字段(首字母小写),确保需日志的字段为导出字段(大写开头); - 时间、通道、函数等不支持 JSON 序列化的类型会返回错误,建议仅用于调试日志场景;
- 若需更轻量的字符串表示(如
"nil"而非null),仍需手动判断,但json方案在多数调试与可观测性场景中已足够清晰、可靠且零维护成本。
综上,json.Marshal 不仅规避了显式 nil 检查的繁琐,还提供了结构化、可读性强、兼容复杂数据类型的统一日志能力,是 Go 工程中推荐的 nil 安全调试实践。










