tutorial

  1. Logging dan Debugging dengan grpc-go

85. Logging dan Debugging dengan grpc-go: Memastikan Mikroservis Anda Tetap Andal

Pada tataran pengembangan aplikasi berbasis microservices di lingkungan Go, gRPC telah menjadi teknologi terdepan untuk komunikasi antar layanan. Namun, seiring dengan pertumbuhan arsitektur yang semakin kompleks, kebutuhan untuk monitoring, logging, dan debugging pada stack grpc-go menjadi krusial. Tanpa logging dan praktik debugging yang baik, proses tracing bug dan analisa performa cenderung sulit serta menyita waktu.

Artikel ini akan membahas bagaimana melakukan logging dan debugging layanan gRPC menggunakan library grpc-go. Kita akan membedah:

  • Mengaktifkan logging pada grpc-go
  • Integrasi logging pada aplikasi
  • Memanfaatkan interceptor untuk tracing
  • Menavigasi error handling dan debugging
  • Tools dan trik debugging

Dilengkapi dengan snippet kode praktis, simulasi, dan alur kerja, artikel ini ditujukan untuk engineer backend Go yang ingin meningkatkan observabilitas dan daya tahan layanannya.


Apa Itu Logging dan Debugging pada gRPC?

Logging adalah proses mencatat aktivitas aplikasi ke log file/console untuk keperluan monitor, audit, dan troubleshooting. Sementara debugging adalah rangkaian aktivitas mengidentifikasi serta memperbaiki kesalahan pada sistem.

Pada arsitektur gRPC, logging sangat penting karena:

  1. Banyak proses terjadi secara asynchronous dan remote.
  2. Error propagation antar service kerap kali tak spesifik.
  3. Keamanan serta stabilitas layanan sangat tergantung pada observasi proses request/response.

Logging di grpc-go

Secara default, package grpc-go sudah memiliki logging internal, namun terbatas untuk error runtime. Kebutuhan real-world sering kali mensyaratkan custom logging yang bisa diintegrasikan dengan platform sesuai kebutuhan (ELK, Sentry, dsb).

Mari kita bandingkan dua pendekatan: Logging default vs Custom Interceptor Logging.

PendekatanKelebihanKekurangan
DefaultMudah, out-of-boxKurang detail, sulit dikontrol
CustomFleksibel, detailPerlu effort tambahan

1. Logging dengan Logger Bawaan (grpclog)

gRPC membawa package grpclog yang default-nya menulis ke stderr. Namun, Anda bisa mengganti implementasinya dengan library logging eksternal seperti zap, logrus, atau bahkan logger custom.

Contoh Implementasi

import (
    "go.uber.org/zap"
    "google.golang.org/grpc/grpclog"
)

func init() {
    // Replace default grpc logger with zap
    logger, _ := zap.NewProduction()
    grpclog.SetLoggerV2(
        zapLogger{logger},
    )
}

type zapLogger struct {
    l *zap.Logger
}

func (z zapLogger) Info(args ...interface{})  { z.l.Sugar().Info(args...) }
func (z zapLogger) Infoln(args ...interface{}) { z.Info(args...) }
func (z zapLogger) Infof(format string, args ...interface{}) { z.l.Sugar().Infof(format, args...) }

func (z zapLogger) Warning(args ...interface{})  { z.l.Sugar().Warn(args...) }
// dst...

Catatan: Hanya log level tertentu yang digunakan gRPC.


2. Logging Melalui Middleware: Unary dan Stream Interceptors

Salah satu fitur powerful di grpc-go adalah interceptor: fungsi pembungkus yang dapat kita gunakan menyisipkan logging, tracing, bahkan rate limiting.

a. Unary Interceptor

Cocok untuk request-response sederhana.

import "google.golang.org/grpc"

func LoggingUnaryInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    log.Printf("[Unary] Request: %s at %v", info.FullMethod, time.Now())
    resp, err := handler(ctx, req)
    if err != nil {
        log.Printf("[Unary] Error: %v", err)
    }
    return resp, err
}

// Pada grpc.NewServer:
server := grpc.NewServer(
    grpc.UnaryInterceptor(LoggingUnaryInterceptor),
)

b. Stream Interceptor

Untuk komunikasi stream (client/server/bidirectional).

func LoggingStreamInterceptor(
    srv interface{},
    ss grpc.ServerStream,
    info *grpc.StreamServerInfo,
    handler grpc.StreamHandler,
) error {
    log.Printf("[Stream] Started method: %s", info.FullMethod)
    return handler(srv, ss)
}

Simulasi Output Logging

[Unary] Request: /proto.OrderService/GetOrder at 2024-06-15T09:00:00Z
[Unary] Error: rpc error: code = NotFound desc = Order not found
[Stream] Started method: /proto.ChatService/Chatstream

3. Error Propagation dan Contextual Logging

Logging tanpa konteks request kadang tidak terlalu informatif. Gunakan unique request ID/trace ID untuk setiap call.

Contoh: menambah metadata pada context.

import (
    "github.com/google/uuid"
    "google.golang.org/grpc/metadata"
)

func addRequestID(ctx context.Context) context.Context {
    id := uuid.New().String()
    md := metadata.Pairs("x-request-id", id)
    return metadata.NewOutgoingContext(ctx, md)
}

Pada interceptor, log request ID beserta method dan error.


4. Debugging gRPC API: Praktik dan Tools

Diagram Alur Logging pada Interceptor

flowchart TD
    Start[Start Request] --> Interceptor{Interceptor?}
    Interceptor -- Yes --> Log[Log Data]
    Log --> Handler[Handler Execution]
    Handler --> Resp[Response/Error]
    Resp --> Interceptor
    Interceptor -- No --> Handler

Diagram di atas memperlihatkan alur sederhana intercept request–logging–handler–response.


Debug dengan grpctools & Reflection

  • grpccurl: tool CLI untuk probing endpoint gRPC, bisa melihat detail request/response dan error secara real time.
  • gRPC Reflection: gunakan plugin reflection (reflection.Register(server)) supaya endpoint dapat di-explore selama pengujian.

Contoh Debugging Error

grpcurl -plaintext -d '{"id":123}' localhost:50051 proto.OrderService/GetOrder

Jika terjadi error, stacktrace pada log (karena logging interceptor) akan sangat membantu menemukan sumber masalah.


5. Integrasi Logging Produksi (Stackdriver, ELK, Sentry)

Di production, log perlu di-export ke sistem observabilitas.

Saran Library:

  • zap: log berstruktur dengan performa tinggi
  • logrus: fleksibel dan mudah dikustomisasi

Contoh Log JSON untuk ELK:

{
  "level": "info",
  "service": "order-service",
  "method": "/proto.OrderService/GetOrder",
  "request_id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "error",
  "error": "Order not found",
  "timestamp": "2024-06-15T09:00:01Z"
}

Studi Kasus: Handling Slow Request dan Error Trace

Misal: order service Anda tiba-tiba lambat.

Dengan logging interceptor berikut, Anda bisa telusuri delay:

func MonitoringInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    start := time.Now()
    resp, err := handler(ctx, req)
    duration := time.Since(start)
    if duration > 500*time.Millisecond {
        log.Printf("[WARN] Slow GRPC call: %s, duration: %s", info.FullMethod, duration)
    }
    return resp, err
}

Simulasi Output:

[WARN] Slow GRPC call: /proto.OrderService/GetOrder, duration: 712ms

Kesimpulan

Tooling dan praktik logging serta debugging di grpc-go adalah investasi utama dalam menjaga keandalan layanan backend Go Anda. Dengan menempatkan interceptor logging, context-aware trace, serta mengintegrasikan dengan observability stack, Anda tidak hanya membuat troubleshooting lebih mudah, tapi juga mempersiapkan fondasi untuk SRE dan DevOps di masa depan.

Selalu lakukan review pada kebutuhan logging Anda: pastikan tidak berlebihan, tetap patuh pada compliance/security, dan mudah ditelusuri.


Sumber kode dan eksperimen dapat diakses di https://github.com/namapengguna/grpc-go-logging-playground.

Selamat mencoba–dan semoga debugging Anda tidak pernah lagi frustasi! 🚀

comments powered by Disqus