tutorial

34 Logging Interceptor dengan Context

Jika kita bicara soal monitoring dan observability di aplikasi modern, maka logging adalah denyut nadinya. Tidak ada yang lebih menyebalkan daripada bug yang hanya muncul “kadang-kadang” tanpa log yang cukup untuk tracing—sebuah mimpi buruk bagi developer backend mana pun.

Biasanya, kita menambahkan logging di berbagai bagian aplikasi, dari controller, service layer, hingga data layer. Tapi, jika kamu mendesain API atau back-end yang scalable, kamu pasti ingin logging yang fleksibel, terstruktur, context-aware, dan minimal overheat.

Di artikel ini, kita akan bahas bagaimana membangun Logging Interceptor dengan Context Awareness—interceptor yang bisa mencatat dengan pintar, dapat membaca context (misal, user ID, request ID, source tracing), dan scalable tanpa membanjiri log dengan noise.


TL;DR

  • Logging interceptor = solusi lintas-cutting concern untuk pencatatan request/response secara global.
  • Context membantu log tetap informatif: user, endpoint, trace ID, dan lainnya bisa otomatis masuk log.
  • Simulasi dibuat dengan bahasa Go, namun konsep dapat diaplikasikan di banyak bahasa (Java Spring, .NET Middleware, dsb).
  • Disertai contoh kode, tabel perbandingan, dan diagram alur.

Apa itu Logging Interceptor?

Interceptor (atau kadang disebut middleware) adalah mekanisme yang memungkinkan kita melakukan sesuatu sebelum atau setelah request/goroutine dieksekusi—tanpa harus mengotak-atik kode di semua handler. Cocok untuk logging, validasi, rate limit, dsb.

Pola ini banyak ditemukan di framework modern:

  • Go: http.Handler, grpc.UnaryInterceptor
  • Java Spring: HandlerInterceptor, Filter
  • .NET: Middleware

Mengapa Pakai Context?

Salah satu tantangan log tradisional:

  • Konteks hilang… Misal, “Internal Server Error on endpoint GET /order/123 by user X”, kalau log-nya global, user tidak kelihatan.
  • Sulit menghubungkan trace antara microservice/module.
  • Susah debug kasus tertentu, karena log yang terkelompok kurang rapi.

Dengan Context, kita bisa:

  • Inject trace ID/request ID.
  • Tambahkan user ID/session info.
  • Pasangkan metadata seperti elapsed time.
  • Lempar context ini ke seluruh handler/layer.

Studi Kasus: Logging Interceptor pada HTTP API (Golang)

Mari kita simulasikan dengan Go. Namun, kamu bisa menerapkan prinsip ini di framework lain.

1. Logging Utility Siap Context

package logger

import (
  "context"
  "log"
)

type contextKey string

const (
  TraceIDKey contextKey = "trace_id"
  UserIDKey  contextKey = "user_id"
)

func WithTraceID(ctx context.Context, traceID string) context.Context {
  return context.WithValue(ctx, TraceIDKey, traceID)
}

func WithUserID(ctx context.Context, userID string) context.Context {
  return context.WithValue(ctx, UserIDKey, userID)
}

func LogWithContext(ctx context.Context, msg string, args ...interface{}) {
  traceID, _ := ctx.Value(TraceIDKey).(string)
  userID, _ := ctx.Value(UserIDKey).(string)
  log.Printf("[TraceID:%s][UserID:%s] %s", traceID, userID, fmt.Sprintf(msg, args...))
}

2. Middleware: Logging Interceptor

package middleware

import (
  "net/http"
  "github.com/yourmodule/logger"
  "github.com/google/uuid"
)

func LoggingInterceptor(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    traceID := uuid.New().String()
    // Misalkan kita dapatkan userID dari header
    userID := r.Header.Get("X-User-ID")

    ctx := logger.WithTraceID(r.Context(), traceID)
    if userID != "" {
      ctx = logger.WithUserID(ctx, userID)
    }

    logger.LogWithContext(ctx, "Incoming request %s %s", r.Method, r.URL.Path)
    next.ServeHTTP(w, r.WithContext(ctx))
    logger.LogWithContext(ctx, "Completed request %s %s", r.Method, r.URL.Path)
  })
}

3. Usage di Main

mux := http.NewServeMux()
mux.HandleFunc("/hello", handlerWithLog)

logged := middleware.LoggingInterceptor(mux)
http.ListenAndServe(":8080", logged)

4. Handler di Bawah Bisa Tetap Context-Aware!

func handlerWithLog(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()
  logger.LogWithContext(ctx, "Process utama berjalan...")
  // ... kode lainnya
  fmt.Fprintln(w, "Hello World")
}

Simulasi Hasil Log

Mari lihat simulasi output logging kita:

TimeTraceIDUserIDEventInfo
13:01:02abc-123999Incoming requestGET /hello
13:01:02abc-123999Process utama
13:01:02abc-123999Completed requestGET /hello

Dengan context, semua log entry terkait satu permintaan akan punya TraceID & UserID yang sama.


Diagram Alur: Logging Interceptor

sequenceDiagram
  participant Client
  participant Interceptor
  participant Handler

  Client->>Interceptor: Kirim HTTP Request
  Interceptor->>Interceptor: Buat TraceID & baca UserID
  Interceptor->>Interceptor: Inject ke Context
  Interceptor->>Interceptor: Log "incoming request"
  Interceptor->>Handler: Lanjutkan ke Handler (dengan Context)
  Handler->>Interceptor: Selesai Handle
  Interceptor->>Interceptor: Log "completed request"
  Interceptor->>Client: Response

Kelebihan & Tantangan Logging Interceptor Context-Aware

KelebihanTantangan
Tidak perlu kopi-paste logging di setiap handlerDistribusi context di background routine
Siap scale-out, trace otomatisPotensi issue bila context salah propagate
Mudah debug satu sesi request end-to-endOverhead kecil jika log sangat verbose
Bisa extend (user agent, ip addr, dsb)Pemahaman context di seluruh tim developer

Best Practice

  1. Log seperlunya. Jangan log seluruh payload jika tidak dibutuhkan.
  2. Awas privacy. Jangan tulis sensitive info ke log.
  3. Inject ke context layer sedini mungkin—ideal di gateway/middleware.
  4. Favor struktur log JSON untuk parsing yang lebih mudah oleh ELK/Splunk.

Kesimpulan

Penerapan Logging Interceptor dengan Context adalah langkah sederhana namun vital untuk mendapatkan log yang bermakna, terstruktur, dan traceable. Dengan pattern ini, tracing masalah jadi jauh lebih produktif—no more “misteri di server log”—dan monitoring microservice jadi scalable.

Pattern ini kompatibel dengan hampir seluruh bahasa dan framework modern. Jadi engineer yang peduli observability dan debugging efisien? Segera refactor logger-mu berbasis context-aware!


Mau diskusi lebih lanjut? Drop pertanyaanmu di kolom komentar!

comments powered by Disqus