1. Log trong CLI: chan doan va hop dong dau ra
Khi ban viet mot tien ich console (vi du, todo hoc tap cua chung ta), ban thuong muon “cho chac an” in tat ca moi thu qua fmt.Println. Dieu do hoan toan binh thuong... cho den khi todo list cua ban phai dua ra mot bang trong stdout, va ban bat ngo nem vao do chuoi "DEBUG: open file ok". Script bi hong, test bi hong, nguoi dung khong vui.
Mo hinh dung la nay: ket qua cua lenh la mot hop dong (thuong la stdout), con log la chan doan (thuong la stderr). Goi log ve co ban duoc thiet ke cho muc dich chan doan: logger mac dinh ghi vao loi chuan (stderr) va them ngay/gio vao moi dong.
Co the hinh dung hai “ong” sau:
flowchart LR
App[Ung dung CLI todo]
STDOUT["stdout (ket qua lenh)"]
STDERR["stderr (loi/log)"]
User[Nguoi dung/script]
App --> STDOUT --> User
App --> STDERR --> User
Neu ban mot lan chap nhan mo hinh nay, nhieu loi la ky kieu “tai sao dau ra JSON cua toi khong parse duoc” se tu bien mat.
log don gian nhat: Print, Printf, Println
Bay gio hay nhe nhang lam quen voi goi log ma khong can “cau hinh tat ca moi thu”. Thuong chi can danh dau cac diem quan trong trong doi song chuong trinh: khoi dong, tham so, thanh cong, loi.
Vi du toi thieu (theo phong cach cua chung ta — tien ich todo khoi dong va ghi ra che do dang chay):
package main
import "log"
func main() {
log.Println("todo started") // 2009/01/23 01:23:23 todo started
log.Printf("mode=%s", "dev") // 2009/01/23 01:23:23 mode=dev
}
Cac quan sat quan trong (khong phai “danh sach quy tac”, ma la ly le thong thuong):
Logger tu them xuong dong neu ban quen. No co gang in “mot ban ghi tren moi dong”, de log co the doc bang mat va xu ly bang cong cu. Diem thu hai — logger mac dinh da co ngay va gio duoc bat san, va dieu do thuong huu ich hon ban nghi vao luc viet code: “chinh xac la luc nao dieu nay xay ra?” — cau hoi ma ban se bat dau tu hoi minh lien tuc.
log ghi vao dau va vi sao gan nhu luon la stderr
Neu ban viet mot lenh CLI, mac dinh ban gan nhu luon co hai loai dau ra.
Loai thu nhat — cai nguoi dung “dat hang”: danh sach nhiem vu, ket qua JSON, chuoi "OK". Cai nay can duoc in vao stdout, vi no co the duoc chuyen huong vao file, pipe sang lenh khac, va v.v.
Loai thu hai — cai ban can (voi tu cach nha phat trien) de hieu dieu dang xay ra: "opened file", "read 15 tasks", "could not parse date", "disk write error". Do chinh la log. Goi log mac dinh noi rang logger tieu chuan ghi vao standard error.
Hay minh hoa bang mot doan nho, trong do ket qua theo hop dong di vao stdout, con chan doan — vao stderr:
package main
import (
"fmt"
"log"
)
func main() {
fmt.Println("OK") // stdout: OK
log.Println("printed OK to user") // stderr: 2009/01/23 ... printed OK to user
}
Neu ban tung gap tinh huong “toi da luu dau ra cua lenh vao file, va trong do co rac tu cac dong debug” — chuc mung, ban da hieu vi sao can tach dau ra nhu vay.
2. Cau hinh logger: SetOutput, SetPrefix, SetFlags
Khi log nhieu hon, chung bat dau giong nhu cuoc tro chuyen giua hai nguoi quen mat chu de. Vi vay logger co the duoc cau hinh nhe: ghi vao dau, them tien to gi, in nhung truong ky thuat nao (ngay/gio/tep/dong).
Hay cau hinh no “nhu trong CLI thuc te”, noi chung ta muon:
- ghi vao stderr (thuc ra mac dinh da nhu vay, nhung cu de ro rang),
- co tien to "todo " (de phan biet log cua ung dung voi log cua moi truong),
- in ngay/gio tieu chuan (do la log.LstdFlags).
package main
import (
"log"
"os"
)
func main() {
log.SetOutput(os.Stderr)
log.SetPrefix("todo ")
log.SetFlags(log.LstdFlags)
log.Println("started") // todo 2009/01/23 01:23:23 started
}
Ve thuc te, chung ta da “dinh nhan” len moi dong: bay gio no kho nham lan voi cai gi khac hon.
Dang log: cac co cua logger
Trong log, dinh dang phan dau duoc dieu khien bang cac co. Ban co the se nho het “ca mot vuon thu” nay theo thoi gian, nhung y tuong nen nam lay ngay luc nay: cac co chi la nhung cong tac bat them thong tin meta vao dau dong.
Mot bang nho (rat tien de dung nhu so tay nhanh):
| Co | No them gi | Khi nao huu ich |
|---|---|---|
|
ngay | khi log duoc ghi trong nhieu ngay va ban khong muon doan “hom nao thi co” |
|
gio | gan nhu luon |
|
micro giay | khi ban debug hieu nang, nhung co the tao nhieu nhieu |
|
|
khi can tim nhanh vi tri trong code |
|
duong dan day du + dong | khi du an lon va co nhieu tep |
|
|
mac dinh, gan nhu luc nao cung on |
Ve mat hinh thuc, goi log mo ta rang logger tieu chuan in ngay va gio va ghi vao stderr.
Neu bat Lshortfile, ban se nhan duoc goi y “dong nao da goi no”. No huu ich... cho den khi ban bat no o moi noi va nhan duoc mot log giong nhu bai van “Chien tranh va Hoa binh”, chi co them duong dan.
log.New: logger rieng cho tung thanh phan
Khi ung dung lon len, se xuat hien mong muon tu nhien la tach the gioi thanh cac “phan”: CLI parser, storage, business logic. Ngay ca khi ban van chi co mot module, ban da phan biet trong dau giua “noi chung ta doc file” va “noi chung ta ve dau ra cho nguoi dung”.
Trong log co kieu Logger, ma ban co the tao thu cong qua log.New(out, prefix, flags). Dieu nay cho phep tao mot logger cho tung phan he (vi du storage layer) va gan cho no mot tien to rieng.
Hay tuong tuong chung ta co cac ham doc task tu file. Ta se tao mot logger rieng storageLog cho chung:
package main
import (
"log"
"os"
)
func main() {
storageLog := log.New(os.Stderr, "todo storage ", log.LstdFlags)
storageLog.Println("open tasks file") // todo storage 2009/01/23 01:23:23 open tasks file
}
Vi sao cach nay tien? Vi sau nay khi ban thay trong stderr chuoi “todo storage ...”, ban se hieu ngay: day khong phai dau ra cua lenh, ma la lop noi bo. Va ban cung se khong phai them lap lai "storage: ..." bang tay nua — logger lam dieu do thay ban.
3. Vi sao log.Fatal khong phai la “xu ly loi” vat ly
Bay gio se den luc ma nhieu nguoi moi hoc (va ca mot so nguoi co kinh nghiem) noi: “O, vay chi can log.Fatal(err) la xong, khoi phai lo nua!”
Ve ky thuat — co the. Ve thuc te — gan nhu luon khong nen.
Van de la Fatal khong phai la “log loi dep mat”. Fatal la log + thoat khoi tien trinh ngay lap tuc.
Tai lieu log noi rat ro: Fatal tuong duong Print roi goi os.Exit(1). Con os.Exit ket thuc chuong trinh ngay lap tuc, va cac ham deferred se khong duoc thuc thi.
Chinh lien ket nay la nguyen nhan cua mot nua so “loi la” lien quan den Fatal.
Minh hoa van de: defer se khong chay
Hay lam mot vi du ngan, trong do chung ta ky vong defer nhu mot “nguoi don dep”, nhung log.Fatal lai pha het:
package main
import (
"log"
)
func main() {
defer log.Println("cleanup: should run, but...") // se khong kip chay
log.Fatal("fatal: cannot continue")
}
Ve mat logic, ban se mong doi: “thoi chuong trinh se ket thuc, nhung defer chua? no phai duoc thuc thi chu.” Nhung Fatal di vao os.Exit(1), va no ket thuc tien trinh ma khong can defer.
De doi chieu: neu do la panic, thi cac defer se duoc chay trong luc bung stack — day la co che co ban cua panic/defer/recover. Nhung Fatal khong phai panic. Va chinh vi the no moi “nguy hiem nhung tien”.
No dac biet danh vao tai nguyen o cho nay
Trong todo cua chung ta (va trong bat ky ung dung nao), co nhung tai nguyen can dong mo can than:
- tep tin (neu khong, mot phan du lieu se khong duoc ghi, descriptor se khong duoc giai phong),
- cac writer co bo dem (neu khong, se khong co Flush),
- mutex/lock (neu co),
- tep tam thoi (neu ban lam ghi an toan qua temp+rename).
Va rat thuong, viec dong/giai phong dung nam trong defer. Neu ban dung log.Fatal o giua stack, ban cat dut ca co che can than do.
4. Vi sao log.Fatal co hai trong code thu vien
Y tuong quan trong nhat cua bai giang: thu vien khong co quyen quyet dinh rang tien trinh phai chet ngay bay gio.
Hay tuong tuong rang todo cua ban se lon dan theo thoi gian (hoac don gian la ban tach mot phan logic vao mot package) va ai do se dung code cua ban nhu mot phu thuoc:
- trong mot CLI khac,
- trong test,
- trong mot service chay dai han (ma khong nen chet chi vi mot loi doc file),
- trong mot ung dung GUI (noi quy tac UX hoan toan khac).
Neu ben trong “thu vien” ban dung log.Fatal, ban se:
- Lam hong dieu khien dong chay: thay vi return err ban lai lam kieu “cam on tat ca, moi nguoi co the ve nha”. Fatal goi os.Exit(1).
- Lam hong viec don dep tai nguyen: os.Exit khong chay cac ham deferred.
- Lam hong kha nang test: test khong the “kiem tra loi” duoc, vi tien trinh test da ket thuc.
- Lam hong hop dong trach nhiem: thu vien phai thong bao loi, con ung dung phai quyet dinh se lam gi voi no.
Day la su ke thua truc tiep tu dieu chung ta da thuc hanh truoc do: “code ben trong tra ve loi, code cap tren quyet dinh phan ung”.
Vi du xau: thu vien giet tien trinh
Gia su ban viet mot ham doc task va nghi rang “neu khong mo duoc file thi day la loi tat yeu”:
package storage
import (
"log"
"os"
)
func MustReadAll(path string) []byte {
b, err := os.ReadFile(path)
if err != nil {
log.Fatal(err) // ❌ se giet toan bo tien trinh
}
return b
}
Ham nay trong co ve “tien”, nhung thuc te no lam cho thu vien cua ban tro nen doc hai: khong the tai su dung an toan.
Vi du tot: tra ve error va log o lop tren
Tot hon la tra loi ve tren:
package storage
import (
"fmt"
"os"
)
func ReadAll(path string) ([]byte, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read %q: %w", path, err)
}
return b, nil
}
Con bay gio, trong main (hoac trong xu ly lenh CLI), ban se quyet dinh: co nen log hay khong, hien thi gi cho nguoi dung, va dat ma ket thuc nao. Log tien loi nhat o noi ban ra quyet dinh “tiep tuc hay thoat”.
5. Cach them log vao todo ma khong lam hong dau ra
Hay xem mot khuon mau can than: “khoi tao logger → chay → chi log loi mot lan”.
Gia su chung ta co ham run() error, lam mot viec huu ich:
package main
import (
"log"
"os"
)
func run() error {
// day se la logic cua cac lenh: add/list/done
return nil
}
func main() {
log.SetOutput(os.Stderr)
log.SetPrefix("todo ")
log.SetFlags(log.LstdFlags)
if err := run(); err != nil {
log.Printf("command failed: %v", err)
}
}
Diem quan trong o day: run khong “fatal” cai gi ca. No tra ve loi. Con main se quyet dinh phai lam gi. Dieu do lam code de du doan hon.
Dung, o day co the phan bien: “Nhung main khong ket thuc ngay lap tuc!” — dung, va dieu do hoan toan binh thuong. Ban co the ket thuc bang cach return tu main. Neu ban can exit code — do la mot chu de rieng cua hop dong CLI, nhung ke ca trong truong hop do, nguoi ta thuong chi dung os.Exit(code) o muc cao nhat, hieu ro he qua cua no. Vi os.Exit khong goi defer.
6. log.Fatal so voi panic so voi return err
Rat de nham lan giua “dung chuong trinh mot cach cuong bap” va “thong bao loi”. Vay nen hay giu mot mo hinh nhan thuc don gian.
return err — la “toi bao cho cap tren rang co gi do sai, hay tu quyet dinh”.
panic — la “da xay ra dieu gi do khong ngo toi muc khong the tiep tuc”. Khi do, cac ham defer trong goroutine hien tai van chay trong qua trinh bung stack (day la co che quan trong cua panic va recover).
log.Fatal — la “bay gio toi se in thong diep va ket thuc tien trinh bang os.Exit(1)”. Con os.Exit ket thuc ngay lap tuc va khong chay defer.
Noi cach khac, Fatal khong phai la “print manh hon”. No thuoc mot lop hanh dong khac: “in ra + gat cong tac”.
7. Loi thuong gap khi lam viec voi log
Loi 1: log vao stdout va lam hong hop dong dau ra.
Nguoi moi thuong lam fmt.Println("DEBUG ...") ben canh dau ra huu ich ma khong nhan ra van de cho den khi ai do thu pipe ket qua sang lenh khac. Voi logger log, dau ra mac dinh la stderr, va do chinh la dieu can cho chan doan trong CLI.
Loi 2: dung log.Fatal “o dau do ben trong” (giua stack).
No co ve tien, nhung hau qua khong de chiu: Fatal goi os.Exit(1). Va os.Exit ket thuc tien trinh ngay lap tuc va khong thuc hien cac ham deferred. Ket qua la “dong file dam bao bang defer” tro thanh mot loi hi vong vao may man.
Loi 3: ap dung log.Fatal trong code thu vien.
Thu vien phai tra ve loi, khong phai quyet dinh so phan cua tien trinh. Neu khong, ban se lam hong kha nang tai su dung, test va kha nang “giam toc mem”. Ve mat ky thuat, log.Fatal la thoat khoi tien trinh, tuc la thu vien bat ngo co quyen luc tren ung dung.
Loi 4: bat dinh dang qua “on ao” va sau do khong con doc log nua.
Neu ban bat Llongfile | Lmicroseconds chi vi “vi sao lai khong”, log se dai va kho doc. Ket qua nghich ly: co log, nhung khong ai dung no. Hay giu dang du thong tin, nhung dung bien moi dong thanh mot cuon tieu thuyet.
Loi 5: co gang “chua” loi bang log thay vi xu ly.
Doi khi lap trinh vien viet log.Printf("err=%v"), err roi tiep tuc nhu khong co gi xay ra, trong khi day la mot loi ma sau do can thoat hoac tra no len tren. Log la viec ghi nhan su kien, khong phai sua loi. Neu loi lam ban khong the tiep tuc, hanh dong dung la return error (hoac xu ly no), con log chi la thu cap.
GO TO FULL VERSION