Skip to content

fix/log-level-pino #21

Description

@khalifaalhasan

Debug: HTTP 401 Unauthorized Masih Log Level INFO

Tanggal: 2026-08-17
Endpoint: POST /api/category
Status Code: 401
Log yang muncul:

INFO [13:01:33]: request completed
    req: { "method": "POST", "url": "/api/category" }
    context: "HTTP"
    res: { "statusCode": 401 }
    responseTime: 35

Root Cause

Ini memang perilaku yang benar, tapi bukan karena desain yang tepat

Log level INFO pada response 401 bukan bug, melainkan default behavior dari pino-http.

pino-http (library yang digunakan via nestjs-pino) secara default selalu menggunakan level info untuk mencatat log "request completed", tanpa memperhatikan status code response (2xx, 4xx, maupun 5xx), kecuali dikonfigurasi secara eksplisit.


Analisis Kode

1. packages/logger/src/log.config.ts

export const logConfig: Params = {
  pinoHttp: {
    level: isDev ? "debug" : "info",
    // ... transport, serializers, customProps, autoLogging
  },
};

Tidak ada customLogLevel di sini. Artinya pino-http menggunakan default-nya, yaitu level info untuk semua request yang selesai — termasuk yang berakhir dengan 4xx dan 5xx.

2. apps/api/src/main.ts

app.useGlobalInterceptors(new LoggerErrorInterceptor());

LoggerErrorInterceptor dari nestjs-pino bekerja untuk exception yang tidak tertangkap (uncaught exception di layer NestJS interceptor), bukan untuk response HTTP yang sudah berhasil ditulis ke client (seperti 401 dari auth guard).

3. apps/api/src/modules/category/category.controller.ts

@Post()
async create(@Body() createCategoryDto: CreateCategoryDto) { ... }

Decorator @AllowAnonymous() sudah dihapus dari endpoint POST /category oleh user. Artinya request ini sekarang dilindungi oleh auth guard dari @thallesp/nestjs-better-auth. Guard menolak request dan mengembalikan 401 — response selesai normal dari sudut pandang HTTP, sehingga pino-http mencatatnya dengan level info.


Kesimpulan

Pertanyaan Jawaban
Apakah level INFO untuk 401 itu benar? Secara teknis benar (itu default pino-http), tapi kurang informatif
Apakah ini bug? Bukan bug, ini adalah perilaku default yang belum dikonfigurasi
Mengapa LoggerErrorInterceptor tidak membantu? Karena interceptor itu hanya menangkap thrown exception, sedangkan 401 ini dikirim langsung oleh auth guard sebagai HTTP response (bukan throw)
Apakah perlu diperbaiki? Ya, disarankan — supaya monitoring lebih mudah dan alert bisa dibedakan berdasarkan severity

Rekomendasi (tidak dilakukan, hanya saran)

Tambahkan customLogLevel di log.config.ts:

pinoHttp: {
  customLogLevel: (req, res, err) => {
    if (err || res.statusCode >= 500) return 'error';
    if (res.statusCode >= 400)        return 'warn';
    return 'info';
  },
  // ... konfigurasi lainnya tetap sama
}

Dengan ini:

  • 2xxINFO
  • 4xx (termasuk 401) → WARN
  • 5xx atau uncaught error → ERROR

File Terkait

File Relevansi
packages/logger/src/log.config.ts Konfigurasi pinoHttp — tidak ada customLogLevel
apps/api/src/main.ts LoggerErrorInterceptor dipasang di sini
apps/api/src/modules/category/category.controller.ts @AllowAnonymous() dihapus dari POST /category — penyebab 401

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions