Appearance
Chapter 6 - Logging
Ikhtisar Chapter
Chapter ini memperkenalkan logging sebagai bagian integral dari siklus pengembangan, bukan tambahan di akhir. Dengan Logger bawaan NestJS, kita dapat mencatat aktivitas aplikasi di berbagai level — dari informasi ringan hingga error kritis lengkap dengan stack trace.
Peta Cepat
FokusLogger bawaan NestJS, level log, konteks, stack trace
ManfaatMemahami apa yang terjadi di aplikasi saat berjalan atau saat error
Hasil AkhirLog bermakna di controller dan repository, dengan penanganan error yang informatif
Gambaran Besar
Ketika aplikasi berjalan di production dan sesuatu gagal, kita tidak bisa membuka debugger dan melangkah baris per baris. Satu-satunya cara memahami apa yang terjadi adalah melalui log. Tanpa log yang baik, proses investigasi masalah bisa memakan waktu berjam-jam hanya untuk menemukan satu baris yang salah.
Logging yang baik bukan berarti mencatat semua hal. Justru sebaliknya — log yang terlalu banyak dan tidak terstruktur sama buruknya dengan tidak ada log. Kuncinya adalah memilih level yang tepat, memberikan konteks yang jelas, dan memastikan log error menyertakan stack trace agar masalah dapat ditelusuri.
NestJS sudah menyediakan Logger dari paket @nestjs/common, sehingga tidak perlu menginstal library tambahan untuk memulai. Untuk kebutuhan produksi yang lebih serius, library seperti Pino atau Winston sangat direkomendasikan.
Lima Level Log
text
┌──────────┬───────────────────────────────────────────────────────────────────┐
│ Level │ Kapan Digunakan │
├──────────┼───────────────────────────────────────────────────────────────────┤
│ log │ Informasi penting umum. Contoh: "App berjalan di port 3000" │
│ warn │ Masalah yang tidak fatal. Contoh: "Task berhasil disimpan │
│ │ setelah dua percobaan" │
│ error │ Masalah fatal. Contoh: "Gagal menyimpan task ke database" │
│ debug │ Info untuk developer saat debugging. Biasanya dimatikan di prod │
│ verbose │ Info sangat detail untuk operator/support. Sering dianggap │
│ │ terlalu banyak informasi (TMI) di production │
└──────────┴───────────────────────────────────────────────────────────────────┘Level Log per Environment
Setiap environment biasanya memiliki kebijakan log level yang berbeda. Makin sensitif environment-nya, makin sedikit informasi yang sebaiknya ditampilkan.
| Level | Development | Staging | Production |
|---|---|---|---|
| log | ✅ | ✅ | ✅ |
| warn | ✅ | ✅ | ✅ |
| error | ✅ | ✅ | ✅ |
| debug | ✅ | ✅ | ❌ |
| verbose | ✅ | ❌ | ❌ |
Struktur Direktori
Chapter ini tidak menambahkan file baru. Logger diimpor dari @nestjs/common dan diinstansiasi langsung di dalam file yang sudah ada:
src/
├── main.ts ← DIUBAH: + new Logger('bootstrap')
├── auth/
│ └── auth.service.ts ← DIUBAH: + this.logger.log/warn/error
└── tasks/
├── tasks.controller.ts ← DIUBAH: + this.logger.verbose/debug
└── tasks.repository.ts ← DIUBAH: + this.logger.error (+ stack trace)Pola Logger di Setiap Class
typescript
import { Logger } from '@nestjs/common'
@Injectable()
export class TasksService {
private logger = new Logger('TasksService') // konteks = nama class
// ↑
// label yang muncul di setiap baris log
}Setiap class membuat instance Logger-nya sendiri dengan konteks yang jelas — sehingga log dari TasksService dan AuthService mudah dibedakan di terminal.
Ringkasan Lecture
1. Introduction to Logging
Kursus membuka dengan pertanyaan mendasar: mengapa kita perlu log? Jawabannya sederhana — jika sesuatu salah, kita butuh konteks: apa yang terjadi, di mana terjadinya, dan kapan. Log memberi kita jejak digital yang bisa ditelusuri.
Lima tipe log diperkenalkan beserta contoh situasi nyata masing-masing. Penting juga untuk memahami bahwa log yang dimunculkan harus disesuaikan per environment. Menampilkan semua level di production berisiko membocorkan informasi sensitif atau membebani sistem dengan output yang tidak perlu.
Instruktur menekankan satu prinsip penting yang sering diabaikan: logging seharusnya dilakukan bersamaan dengan pengembangan fitur, bukan setelah semua fitur selesai. Pendekatan "tambahkan logging nanti" hampir selalu menghasilkan log yang tidak bermakna atau tidak lengkap.
Berikut contoh sederhana penggunaan Logger di entry point aplikasi:
typescript
// src/main.ts
import { Logger } from '@nestjs/common'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
const logger = new Logger('bootstrap')
await app.listen(3000)
logger.log('Application is running on port 3000')
}
bootstrap()Argumen 'bootstrap' adalah konteks — label yang muncul di setiap baris log dari logger ini. Ketika log muncul di terminal, Anda langsung tahu dari bagian mana pesan itu berasal.
2. Implementing Logs in our NestJS App
Implementasi dimulai dari file main.ts sebagai titik masuk aplikasi. Cara membuat logger sangat mudah:
typescript
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule)
const logger = new Logger()
await app.listen(3000)
logger.log(`Application listening on port 3000`)
}Satu best practice penting: di dalam class (controller, service, repository), logger sebaiknya dijadikan properti private dengan konteks berupa nama class. Ini memudahkan identifikasi sumber log saat membaca output.
typescript
// tasks.controller.ts
@Controller('tasks')
export class TasksController {
private logger = new Logger('TasksController')
getTasks(...) {
this.logger.verbose(
`User "${user.username}" retrieving all tasks. Filters: ${JSON.stringify(filterDto)}`
)
...
}
}Argumen string pada new Logger('TasksController') adalah konteks yang akan muncul di setiap baris log dari logger ini, sehingga mudah difilter.
Logging error dengan stack trace adalah langkah krusial berikutnya. Saat operasi database dibungkus try/catch, error perlu di-log sebelum dilempar ulang. Method logger.error() menerima argumen kedua berupa error.stack — stack trace yang menunjukkan persis di mana error berasal.
typescript
// tasks.repository.ts
async getTasks(filterDto, user): Promise<Task[]> {
try {
return await query.getMany()
} catch (error) {
this.logger.error(
`Failed to get tasks for user "${user.username}". Filters: ${JSON.stringify(filterDto)}`,
error.stack,
)
throw new InternalServerErrorException()
}
}Kenapa harus melempar ulang exception setelah catch? Karena blok try/catch menghentikan propagasi error secara default. Tanpa melempar ulang, NestJS tidak tahu ada masalah dan tidak akan mengembalikan respons 500 ke client.
Anatomi Satu Log yang Baik
text
[NestApplication] Application listening on port 3000 ← konteks + pesan
^ ^
| |
konteks logger isi pesan bermaknatext
[TasksController] User "ariel" retrieving all tasks. Filters: {"status":"OPEN"}
^ ^ ^
| | |
konteks siapa user data relevantext
[TasksRepository] Failed to get tasks for user "ariel". Filters: {} ← level error
Error: column "stats" does not exist ← stack trace
at QueryFailedError (/app/node_modules/typeorm/error/...)Konsep Kunci
| Konsep | Penjelasan |
|---|---|
new Logger('Konteks') | Membuat instance logger dengan label konteks |
logger.log() | Level paling umum, informasi penting |
logger.verbose() | Detail tinggi, cocok untuk aktivitas rutin |
logger.error(msg, stack) | Error kritis, selalu sertakan error.stack |
logger.warn() | Masalah tidak fatal yang patut diperhatikan |
try/catch + throw | Tangkap error, log, lalu lempar ulang agar NestJS kirim 500 |
| Private logger property | Best practice — logger sebagai member class |
Alur Penanganan Error dengan Logging
text
Operasi database dijalankan (query.getMany())
|
|-- Sukses --> return tasks
|
|-- Gagal --> catch (error)
|
v
logger.error(pesan deskriptif, error.stack)
|
v
throw new InternalServerErrorException()
|
v
NestJS mengembalikan HTTP 500 ke clientJebakan Umum
Jangan log data sensitif
logger.verbose(JSON.stringify(createTaskDto)) mungkin tampak aman, tapi jika suatu saat DTO mengandung field seperti password atau token, data itu akan muncul di log. Selalu pilih field mana yang aman untuk dilog.
try/catch tanpa throw ulang
Jika error ditangkap tapi tidak dilempar ulang, aplikasi akan mengembalikan respons 200 OK meski sebenarnya ada masalah. Selalu pastikan error-path menghasilkan exception yang tepat.
Logging seharusnya menjadi kebiasaan pengembangan
Jangan menunggu fitur selesai untuk menambahkan log. Tambahkan saat menulis method — ini menghemat waktu debugging jauh lebih banyak daripada waktu yang "terbuang" untuk mengetiknya.
Gunakan library khusus untuk production
Logger bawaan NestJS cocok untuk belajar dan pengembangan awal. Untuk production, pertimbangkan Pino (performa tinggi, JSON output) atau Winston (fleksibel, banyak transport).
Checklist Implementasi
- [ ] Tambah
const logger = new Logger()dibootstrap()padamain.ts - [ ] Log setelah
app.listen():logger.log('Application listening on port ...') - [ ] Tambah
private logger = new Logger('NamaClass')di setiap class yang perlu logging - [ ] Gunakan
logger.verbose()untuk aktivitas rutin di controller - [ ] Bungkus operasi database kritis dalam
try/catch - [ ] Di dalam
catch: log denganlogger.error(pesan, error.stack), lalu lempar ulang exception - [ ] Pastikan tidak ada data sensitif (password, token) yang masuk ke log
Pertanyaan Reflektif
- Mengapa log level
verbosesebaiknya dimatikan di production, sementaraerrorselalu menyala? - Apa perbedaan antara log yang bermakna dan log yang hanya menambah kebisingan (noise)?
- Jika menggunakan library logging pihak ketiga seperti Pino, apa yang harus diubah dari implementasi saat ini?
- Bagaimana cara memastikan log dari berbagai class mudah dikorelasikan untuk melacak satu request?