Ketika program berjalan di server tanpa layar, log menjadi jendela utama untuk melihat apa yang terjadi di dalamnya. Go menyediakan dua lapis pencatatan: package log klasik yang menulis baris teks berawalan waktu, dan log/slog sejak Go 1.21 yang mencatat kejadian terstruktur dengan level dan pasangan atribut. Chapter ini memakai keduanya, lalu menutup dengan handler slog yang menghasilkan output identik di setiap eksekusi.
Package log Klasik #
log.Println dan log.Printf menulis satu baris ke stderr dengan awalan tanggal dan jam — perilaku bawaan flag log.LstdFlags pada logger default (log.Default). Format log.Printf mengikuti pola fmt.Printf:
package main
import "log"
func main() {
log.Println("aplikasi dimulai")
log.Printf("pengguna %s masuk", "[email protected]")
}Output yang Dihasilkan:
2026/10/13 10:09:59 aplikasi dimulai
2026/10/13 10:09:59 pengguna [email protected] masuklog.SetFlags mengubah awalan, misalnya menambah log.Lshortfile agar setiap baris mencantumkan berkas dan baris pemanggilan. log.Fatal mencatat pesan lalu menghentikan program dengan os.Exit(1) — gunakan hanya untuk galat yang tidak mungkin dipulihkan.
Logger Default slog #
slog mencatat kejadian sebagai data: satu pesan, satu level, dan pasangan atribut kunci-nilai. Fungsi top-level slog.Info, slog.Warn, dan slog.Error memakai logger default yang menulis ke stderr dengan format serupa log klasik ditambah level dan atribut:
package main
import "log/slog"
func main() {
slog.Info("server siap", "port", 8080)
slog.Warn("koneksi lambat", "ulang", 3)
}Output yang Dihasilkan:
2026/10/13 10:10:02 INFO server siap port=8080
2026/10/13 10:10:02 WARN koneksi lambat ulang=3Handler Text dan JSON #
Logger slog dibangun dari handler yang menentukan format keluaran. slog.NewTextHandler menulis satu baris berformat kunci=nilai, sedangkan slog.NewJSONHandler menulis satu objek JSON per baris — format umum untuk pipeline log modern. Keduanya menerima *slog.HandlerOptions:
logger := slog.New(slog.NewTextHandler(os.Stdout, opts))Level Log #
Setiap kejadian memiliki level yang menunjukkan tingkat kepentingannya:
| Level | Makna kejadian |
|---|---|
slog.LevelDebug | detail internal saat menyelidiki masalah |
slog.LevelInfo | kejadian normal yang layak dicatat |
slog.LevelWarn | kondisi mencurigakan yang belum gagal |
slog.LevelError | kegagalan yang memengaruhi operasi |
Handler default mencatat mulai Info, sehingga Debug dibuang. Opsi Level: slog.LevelDebug pada HandlerOptions menampilkan level Debug; untuk mengubah batas saat program berjalan, gunakan slog.LevelVar.
With, WithGroup, dan SetDefault #
logger.With("layanan", "pelanggan") menghasilkan logger turunan yang menyisipkan atribut tetap pada setiap barisnya, menghapus pengulangan penulisan atribut yang sama. logger.WithGroup("http") memberi awalan grup pada atribut turunannya, misalnya http.status=200. slog.SetDefault(logger) menjadikan logger kustom sebagai logger default, sehingga panggilan top-level seperti slog.Info memakai handler tersebut di seluruh program.
Membuang Atribut time #
Setiap baris slog menyertakan atribut time yang dibaca jam mesin. HandlerOptions.ReplaceAttr memanggil fungsi kita untuk setiap atribut sebelum ditulis; mengembalikan slog.Attr{} menghapus atribut tersebut. Program utuh chapter memakai opsi ini agar contoh dapat direproduksi — pada program produksi, biarkan atribut time tetap tercatat.
Program Utuh #
Berkas slog-demo.go merangkai seluruh materi chapter:
package main
import (
"log/slog"
"os"
)
// buangTime membuat opsi handler yang menghapus atribut time dari setiap
// baris log, sehingga output contoh ini identik di setiap eksekusi.
func buangTime() *slog.HandlerOptions {
return &slog.HandlerOptions{
Level: slog.LevelDebug,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey && len(groups) == 0 {
return slog.Attr{}
}
return a
},
}
}
func main() {
// TextHandler menulis log satu baris dengan format kunci=nilai.
logger := slog.New(slog.NewTextHandler(os.Stdout, buangTime()))
logger.Debug("memuat konfigurasi", "berkas", "app.toml")
logger.Info("server siap", "port", 8080, "versi", "1.2.0")
logger.Warn("koneksi lambat", "ulang", 3)
logger.Error("gagal menyimpan", "penyebab", "disk penuh")
// With menambah atribut tetap pada seluruh log turunannya.
pelanggan := logger.With("layanan", "pelanggan")
pelanggan.Info("profil diperbarui", "id", 42)
// WithGroup memberi awalan grup pada atribut log turunannya.
http := logger.WithGroup("http")
http.Info("permintaan selesai", "status", 200, "metode", "GET")
// JSONHandler menulis setiap log sebagai satu objek JSON.
json := slog.New(slog.NewJSONHandler(os.Stdout, buangTime()))
json.Info("pembayaran diterima", "jumlah", 150000, "mata_uang", "IDR")
// SetDefault menjadikan logger ini logger default seluruh program.
slog.SetDefault(logger)
slog.Info("dari logger default")
}Jalankan program dari folder chapter:
go run slog-demo.goOutput yang Dihasilkan:
level=DEBUG msg="memuat konfigurasi" berkas=app.toml
level=INFO msg="server siap" port=8080 versi=1.2.0
level=WARN msg="koneksi lambat" ulang=3
level=ERROR msg="gagal menyimpan" penyebab="disk penuh"
level=INFO msg="profil diperbarui" layanan=pelanggan id=42
level=INFO msg="permintaan selesai" http.status=200 http.metode=GET
{"level":"INFO","msg":"pembayaran diterima","jumlah":150000,"mata_uang":"IDR"}
level=INFO msg="dari logger default"Mari kita telaah baris demi baris program di atas:
- Baris 10-20: fungsi
buangTimemenyusunHandlerOptionsdenganLevelDebugdanReplaceAttryang mengembalikanslog.Attr{}untuk kuncitime, sehingga atribut tersebut dihapus. - Baris 24:
TextHandlerdibuat untuk menulis ke stdout dengan opsi tersebut. - Baris 26-29: empat level dicatat berurutan; tanpa opsi
Level, barisDebugtidak akan muncul. - Baris 32-33:
Withmenyisipkan atributlayanan=pelanggansebelum atribut panggilan. - Baris 36-37:
WithGroupmengubah atribut turunannya menjadihttp.statusdanhttp.metode. - Baris 40-41:
JSONHandlermenulis kejadian sebagai objek JSON satu baris. - Baris 44-45:
SetDefaultmengarahkanslog.Infotop-level ke logger pertama, terlihat dari formatkunci=nilaipada baris terakhir.
Referensi #
- Dokumentasi package log/slog — logger, handler, dan level.
- Blog resmi: Structured logging with slog — latar desain package.
- Dokumentasi package log — API log klasik.
- slog.JSONHandler — handler format JSON.
Pencatatan terstruktur memiliki sisi yang jauh lebih dalam — rotasi, level dinamis, sampai korelasi antar layanan — yang dibahas pada chapter Log Terstruktur
di section logging. Sebelum sampai di sana, program CLI Anda perlu membaca preferensi pengguna: chapter berikutnya membahas package flag untuk mengolah command line flag.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
slog-demo.go
Unduh berkaspackage main
import (
"log/slog"
"os"
)
// buangTime membuat opsi handler yang menghapus atribut time dari setiap
// baris log, sehingga output contoh ini identik di setiap eksekusi.
func buangTime() *slog.HandlerOptions {
return &slog.HandlerOptions{
Level: slog.LevelDebug,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey && len(groups) == 0 {
return slog.Attr{}
}
return a
},
}
}
func main() {
// TextHandler menulis log satu baris dengan format kunci=nilai.
logger := slog.New(slog.NewTextHandler(os.Stdout, buangTime()))
logger.Debug("memuat konfigurasi", "berkas", "app.toml")
logger.Info("server siap", "port", 8080, "versi", "1.2.0")
logger.Warn("koneksi lambat", "ulang", 3)
logger.Error("gagal menyimpan", "penyebab", "disk penuh")
// With menambah atribut tetap pada seluruh log turunannya.
pelanggan := logger.With("layanan", "pelanggan")
pelanggan.Info("profil diperbarui", "id", 42)
// WithGroup memberi awalan grup pada atribut log turunannya.
http := logger.WithGroup("http")
http.Info("permintaan selesai", "status", 200, "metode", "GET")
// JSONHandler menulis setiap log sebagai satu objek JSON.
json := slog.New(slog.NewJSONHandler(os.Stdout, buangTime()))
json.Info("pembayaran diterima", "jumlah", 150000, "mata_uang", "IDR")
// SetDefault menjadikan logger ini logger default seluruh program.
slog.SetDefault(logger)
slog.Info("dari logger default")
}