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:

go
package main

import "log"

func main() {
	log.Println("aplikasi dimulai")
	log.Printf("pengguna %s masuk", "[email protected]")
}

Output yang Dihasilkan:

text
2026/10/13 10:09:59 aplikasi dimulai
2026/10/13 10:09:59 pengguna [email protected] masuk

log.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:

go
package main

import "log/slog"

func main() {
	slog.Info("server siap", "port", 8080)
	slog.Warn("koneksi lambat", "ulang", 3)
}

Output yang Dihasilkan:

text
2026/10/13 10:10:02 INFO server siap port=8080
2026/10/13 10:10:02 WARN koneksi lambat ulang=3

Handler 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:

go
logger := slog.New(slog.NewTextHandler(os.Stdout, opts))

Level Log #

Setiap kejadian memiliki level yang menunjukkan tingkat kepentingannya:

LevelMakna kejadian
slog.LevelDebugdetail internal saat menyelidiki masalah
slog.LevelInfokejadian normal yang layak dicatat
slog.LevelWarnkondisi mencurigakan yang belum gagal
slog.LevelErrorkegagalan 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:

go
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:

bash
go run slog-demo.go

Output yang Dihasilkan:

text
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:

  1. Baris 10-20: fungsi buangTime menyusun HandlerOptions dengan Level Debug dan ReplaceAttr yang mengembalikan slog.Attr{} untuk kunci time, sehingga atribut tersebut dihapus.
  2. Baris 24: TextHandler dibuat untuk menulis ke stdout dengan opsi tersebut.
  3. Baris 26-29: empat level dicatat berurutan; tanpa opsi Level, baris Debug tidak akan muncul.
  4. Baris 32-33: With menyisipkan atribut layanan=pelanggan sebelum atribut panggilan.
  5. Baris 36-37: WithGroup mengubah atribut turunannya menjadi http.status dan http.metode.
  6. Baris 40-41: JSONHandler menulis kejadian sebagai objek JSON satu baris.
  7. Baris 44-45: SetDefault mengarahkan slog.Info top-level ke logger pertama, terlihat dari format kunci=nilai pada baris terakhir.

Referensi #

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 berkas
go
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")
}