在 Go 语言里,打印信息最常用的两个包是 fmt 和 log。
fmt 更偏向通用格式化输出,可以输出到控制台、字符串、文件或其他 io.Writer。
log 更偏向日志输出,默认会带时间信息,并且提供了打印后退出程序、打印后触发 panic 等能力。
一、fmt 包是什么
fmt 是 Go 标准库中的格式化输入输出包。
使用前需要导入:
import "fmt"
它最常见的作用是:
fmt.Println("hello")
fmt.Printf("name=%s, age=%d\n", "Tom", 18)
二、fmt 的主要打印函数
fmt 提供了三大类常用打印函数:
-
输出到标准输出:
Print、Println、Printf -
输出到字符串:
Sprint、Sprintln、Sprintf -
输出到指定位置:
Fprint、Fprintln、Fprintf
三、Print、Println、Printf
1. fmt.Print
Print 会直接打印内容,不会自动换行。
package main
import "fmt"
func main() {
fmt.Print("hello")
fmt.Print("world")
}
输出:
helloworld
如果想要空格,需要自己写:
fmt.Print("hello ")
fmt.Print("world")
2. fmt.Println
Println 会打印内容,并在最后自动换行。
多个参数之间会自动加空格。
package main
import "fmt"
func main() {
fmt.Println("hello")
fmt.Println("name:", "Tom", "age:", 18)
}
输出:
hello
name: Tom age: 18
3. fmt.Printf
Printf 按照指定格式打印内容。
它不会自动换行,如果需要换行,要手动写 \n。
package main
import "fmt"
func main() {
name := "Tom"
age := 18
fmt.Printf("name=%s, age=%d\n", name, age)
}
输出:
name=Tom, age=18
其中:
%s // 字符串
%d // 十进制整数
\n // 换行
四、Sprint、Sprintln、Sprintf
这三个函数不会直接打印到控制台,而是返回一个字符串。
1. fmt.Sprint
package main
import "fmt"
func main() {
msg := fmt.Sprint("hello", "world")
fmt.Println(msg)
}
输出:
helloworld
2. fmt.Sprintln
Sprintln 会在参数之间加空格,并在末尾加换行。
package main
import "fmt"
func main() {
msg := fmt.Sprintln("name:", "Tom", "age:", 18)
fmt.Print(msg)
}
输出:
name: Tom age: 18
3. fmt.Sprintf
Sprintf 按格式生成字符串,开发中非常常用。
package main
import "fmt"
func main() {
name := "Tom"
age := 18
msg := fmt.Sprintf("name=%s, age=%d", name, age)
fmt.Println(msg)
}
输出:
name=Tom, age=18
常见用途是拼接错误信息、生成提示文案、构造路径等。
五、Fprint、Fprintln、Fprintf
这三个函数会把内容写到指定的 io.Writer 中。
常见的 io.Writer 有:
os.Stdout // 标准输出
os.Stderr // 标准错误输出
文件对象
bytes.Buffer
1. 打印到标准错误输出
package main
import (
"fmt"
"os"
)
func main() {
err := "file not found"
fmt.Fprintln(os.Stderr, "error:", err)
}
os.Stderr 通常用来输出错误信息。
命令行程序里一般建议:
fmt.Println("normal output") // 普通输出
fmt.Fprintln(os.Stderr, "error message") // 错误输出
2. 写入文件
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("create file failed:", err)
return
}
defer file.Close()
fmt.Fprintln(file, "hello file")
fmt.Fprintf(file, "name=%s, age=%d\n", "Tom", 18)
}
六、fmt.Printf 的常用格式化参数
Printf、Sprintf、Fprintf 都会用到格式化参数,也叫占位符。
格式一般是:
fmt.Printf("格式字符串", 参数1, 参数2, 参数3)
例如:
fmt.Printf("name=%s, age=%d\n", "Tom", 18)
这里:
%s 对应 "Tom"
%d 对应 18
七、通用格式化参数
1. %v:默认格式
%v 表示按照默认格式打印值。
fmt.Printf("%v\n", 123)
fmt.Printf("%v\n", "hello")
fmt.Printf("%v\n", true)
输出:
123
hello
true
%v 很通用,打印错误、整数、字符串、结构体都可以。
2. %+v:打印结构体时带字段名
package main
import "fmt"
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "Tom", Age: 18}
fmt.Printf("%v\n", u)
fmt.Printf("%+v\n", u)
}
输出:
{Tom 18}
{Name:Tom Age:18}
实际开发中,调试结构体最常用:
fmt.Printf("user=%+v\n", u)
3. %#v:打印 Go 语法形式
fmt.Printf("%#v\n", u)
输出:
main.User{Name:"Tom", Age:18}
%#v 更适合调试,可以看到类型和字段信息。
4. %T:打印类型
fmt.Printf("%T\n", 123)
fmt.Printf("%T\n", "hello")
fmt.Printf("%T\n", u)
输出:
int
string
main.User
5. %%:打印百分号
fmt.Printf("progress: %d%%\n", 80)
输出:
progress: 80%
八、布尔值
%t
%t 用来打印布尔值。
ok := true
fmt.Printf("ok=%t\n", ok)
输出:
ok=true
九、整数
1. %d:十进制整数
age := 18
fmt.Printf("age=%d\n", age)
输出:
age=18
2. %b:二进制
fmt.Printf("%b\n", 10)
输出:
1010
3. %o:八进制
fmt.Printf("%o\n", 10)
输出:
12
4. %x 和 %X:十六进制
fmt.Printf("%x\n", 255)
fmt.Printf("%X\n", 255)
输出:
ff
FF
5. %c:Unicode 字符
fmt.Printf("%c\n", 65)
fmt.Printf("%c\n", '中')
输出:
A
中
十、浮点数
1. %f:普通小数
price := 12.3456
fmt.Printf("%f\n", price)
输出:
12.345600
默认保留 6 位小数。
2. %.2f:保留 2 位小数
fmt.Printf("%.2f\n", price)
输出:
12.35
3. %e 和 %E:科学计数法
fmt.Printf("%e\n", 123456.789)
fmt.Printf("%E\n", 123456.789)
输出类似:
1.234568e+05
1.234568E+05
4. %g:根据情况自动选择格式
fmt.Printf("%g\n", 123456.789)
fmt.Printf("%g\n", 0.000012345)
%g 会根据数值大小自动选择普通小数或科学计数法。
十一、字符串和字节
1. %s:字符串
name := "Tom"
fmt.Printf("name=%s\n", name)
输出:
name=Tom
2. %q:带引号的字符串
fmt.Printf("%q\n", "hello\nworld")
输出:
"hello\nworld"
%q 对调试字符串很有用,因为它能看出换行、制表符等特殊字符。
3. %x:字符串或字节切片的十六进制
fmt.Printf("%x\n", "Go")
fmt.Printf("% x\n", []byte("Go"))
输出:
476f
47 6f
% x 中间有一个空格,表示每个字节之间也用空格分隔。
十二、指针
%p
%p 用来打印指针地址。
age := 18
fmt.Printf("%p\n", &age)
输出类似:
0xc0000120c0
地址每次运行可能不同。
十三、宽度和精度
格式化参数还可以控制宽度和精度。
1. 宽度
fmt.Printf("|%6d|\n", 123)
fmt.Printf("|%-6d|\n", 123)
输出:
| 123|
|123 |
含义:
%6d // 宽度至少 6,默认右对齐
%-6d // 宽度至少 6,左对齐
2. 补零
fmt.Printf("|%06d|\n", 123)
输出:
|000123|
3. 小数精度
fmt.Printf("%.2f\n", 3.14159)
fmt.Printf("%8.2f\n", 3.14159)
输出:
3.14
3.14
含义:
%.2f // 保留 2 位小数
%8.2f // 总宽度至少 8,保留 2 位小数
十四、错误 err 的打印
Go 里的错误类型是 error,通常变量名叫 err。
file, err := os.Open("missing.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
1. fmt.Println(err)
fmt.Println(err)
会直接打印错误信息。
2. fmt.Printf("%v", err)
fmt.Printf("error: %v\n", err)
%v 表示默认格式。对错误来说,通常就是调用它的 Error() 方法。
3. fmt.Printf("%s", err)
fmt.Printf("error: %s\n", err)
%s 表示字符串格式。因为 error 有 Error() string 方法,所以通常也能打印错误文本。
实际开发中更推荐:
fmt.Printf("error: %v\n", err)
原因是 %v 更通用。
4. fmt.Errorf
fmt.Errorf 不是直接打印,而是创建一个错误。
return fmt.Errorf("open file failed: %v", err)
如果要包装原始错误,推荐使用 %w:
return fmt.Errorf("open file failed: %w", err)
这样后续可以用 errors.Is 或 errors.As 判断错误链。
if errors.Is(err, os.ErrNotExist) {
fmt.Println("file does not exist")
}
十五、完整 fmt 示例
package main
import (
"errors"
"fmt"
"os"
)
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "Tom", Age: 18}
fmt.Println("Println:", u)
fmt.Printf("default: %v\n", u)
fmt.Printf("with fields: %+v\n", u)
fmt.Printf("go syntax: %#v\n", u)
fmt.Printf("type: %T\n", u)
fmt.Printf("string: %s\n", "hello")
fmt.Printf("quoted string: %q\n", "hello\nworld")
fmt.Printf("int: %d\n", 18)
fmt.Printf("binary: %b\n", 10)
fmt.Printf("hex: %x\n", 255)
fmt.Printf("float: %.2f\n", 3.14159)
fmt.Printf("percent: %d%%\n", 80)
_, err := os.Open("missing.txt")
if err != nil {
fmt.Printf("error: %v\n", err)
}
err = fmt.Errorf("wrap error: %w", errors.New("original error"))
fmt.Println(err)
}
十六、log 包是什么
log 是 Go 标准库提供的日志包。
使用前需要导入:
import "log"
和 fmt 相比,log 更适合记录程序运行信息。
默认情况下,log 会输出到标准错误 os.Stderr,并且带日期和时间。
package main
import "log"
func main() {
log.Println("server started")
}
输出类似:
2026/06/30 12:00:00 server started
十七、log 的常用打印函数
log 常用函数主要有三组:
-
Print、Println、Printf -
Fatal、Fatalln、Fatalf -
Panic、Panicln、Panicf
十八、log.Print、log.Println、log.Printf
这组函数只是打印日志,不会退出程序。
1. log.Print
package main
import "log"
func main() {
log.Print("hello")
log.Print("world")
}
2. log.Println
package main
import "log"
func main() {
log.Println("hello")
log.Println("name:", "Tom", "age:", 18)
}
3. log.Printf
package main
import "log"
func main() {
name := "Tom"
age := 18
log.Printf("name=%s, age=%d", name, age)
}
log.Printf 和 fmt.Printf 的格式化规则基本一样,也使用 %s、%d、%v 等占位符。
十九、log.Fatal、log.Fatalln、log.Fatalf
这组函数会先打印日志,然后调用:
os.Exit(1)
也就是说,程序会立即退出。
1. log.Fatal
package main
import "log"
func main() {
log.Fatal("program failed")
log.Println("this line will not run")
}
2. log.Fatalf
package main
import "log"
func main() {
err := connectDB()
if err != nil {
log.Fatalf("connect db failed: %v", err)
}
}
func connectDB() error {
return fmt.Errorf("connection refused")
}
注意:上面代码需要同时导入:
import (
"fmt"
"log"
)
Fatal 适合用于程序启动阶段的不可恢复错误,比如配置读取失败、端口监听失败、数据库连接失败等。
不建议在普通业务函数里随便使用 log.Fatal,因为它会直接结束整个程序。
二十、log.Panic、log.Panicln、log.Panicf
这组函数会先打印日志,然后触发 panic。
package main
import "log"
func main() {
log.Panic("something terrible happened")
}
等价于:
log.Print("something terrible happened")
panic("something terrible happened")
panic 一般用于程序无法继续运行的严重错误,不适合普通错误处理。
二十一、设置 log 输出格式
1. log.SetFlags
默认日志会带日期和时间。
可以通过 log.SetFlags 修改日志前缀格式。
package main
import "log"
func main() {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("hello")
}
输出类似:
2026/06/30 12:00:00 main.go:7: hello
常用 flag:
log.Ldate // 日期,例如 2026/06/30
log.Ltime // 时间,例如 12:00:00
log.Lmicroseconds // 微秒
log.Llongfile // 完整文件名和行号
log.Lshortfile // 短文件名和行号
log.LUTC // 使用 UTC 时间
log.Lmsgprefix // 把 prefix 放在日志信息前
log.LstdFlags // 标准格式,等于 Ldate | Ltime
2. log.SetPrefix
SetPrefix 可以设置日志前缀。
package main
import "log"
func main() {
log.SetPrefix("[myapp] ")
log.Println("server started")
}
输出类似:
[myapp] 2026/06/30 12:00:00 server started
3. log.SetOutput
SetOutput 可以修改日志输出位置。
例如输出到文件:
package main
import (
"log"
"os"
)
func main() {
file, err := os.Create("app.log")
if err != nil {
log.Fatal(err)
}
defer file.Close()
log.SetOutput(file)
log.Println("server started")
log.Println("server stopped")
}
运行后日志会写入 app.log。
二十二、自定义 Logger
除了使用全局的 log.Println,也可以创建自己的 logger。
package main
import (
"log"
"os"
)
func main() {
infoLogger := log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime)
errorLogger := log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile)
infoLogger.Println("server started")
errorLogger.Println("file not found")
}
log.New 的参数含义:
log.New(输出位置, 日志前缀, 日志格式)
也就是:
log.New(io.Writer, prefix string, flag int)
二十三、fmt 和 log 怎么选
适合用 fmt 的场景
fmt.Println("hello")
fmt.Printf("name=%s\n", name)
fmt.Fprintln(os.Stderr, "error:", err)
适合:
-
学习和临时调试
-
命令行程序输出结果
-
格式化生成字符串
-
写入任意
io.Writer
适合用 log 的场景
log.Println("server started")
log.Printf("request id=%s cost=%dms", requestID, cost)
log.Fatal(err)
适合:
-
记录程序运行日志
-
服务端程序输出运行状态
-
需要日期、时间、文件行号
-
程序启动失败后直接退出
二十四、常用速查表
fmt 函数
fmt.Print // 打印,不换行
fmt.Println // 打印,自动换行,参数之间加空格
fmt.Printf // 按格式打印
fmt.Sprint // 返回字符串,不换行
fmt.Sprintln // 返回字符串,自动加空格和换行
fmt.Sprintf // 按格式返回字符串
fmt.Fprint // 写入指定 io.Writer,不换行
fmt.Fprintln // 写入指定 io.Writer,自动换行
fmt.Fprintf // 按格式写入指定 io.Writer
fmt 占位符
%v // 默认格式
%+v // 打印结构体时带字段名
%#v // Go 语法形式
%T // 类型
%% // 百分号
%t // 布尔值
%d // 十进制整数
%b // 二进制整数
%o // 八进制整数
%x // 十六进制,小写
%X // 十六进制,大写
%c // Unicode 字符
%f // 浮点数
%.2f // 保留 2 位小数
%e // 科学计数法,小写 e
%E // 科学计数法,大写 E
%g // 自动选择浮点格式
%s // 字符串
%q // 带引号字符串
%p // 指针地址
log 函数
log.Print // 打印日志
log.Println // 打印日志并换行
log.Printf // 按格式打印日志
log.Fatal // 打印日志后 os.Exit(1)
log.Fatalln // 打印日志后 os.Exit(1)
log.Fatalf // 按格式打印日志后 os.Exit(1)
log.Panic // 打印日志后 panic
log.Panicln // 打印日志后 panic
log.Panicf // 按格式打印日志后 panic
二十五、总结
fmt 解决的是"怎么格式化输出"的问题。
log 解决的是"怎么记录日志"的问题。
日常开发中可以简单记住:
fmt.Println(value) // 简单打印
fmt.Printf("%+v\n", structVal) // 调试结构体
fmt.Sprintf("id=%d", id) // 生成字符串
fmt.Fprintln(os.Stderr, err) // 输出错误到 stderr
log.Println("started") // 打日志
log.Printf("id=%d", id) // 格式化日志
log.Fatal(err) // 打印错误并退出程序
如果只是学习、调试、命令行输出,优先用 fmt。
如果是服务运行日志、错误记录、程序启动失败退出,优先用 log。