Ketika arsitektur perangkat lunak berkembang dari satu server monolitik menjadi sistem terdistribusi yang melibatkan frontend Next.js di edge CDN, API gateway NestJS, antrean background jobs Redis, dan database PostgreSQL, mendiagnosis lonjakan latensi atau error sporadis menjadi tantangan besar bagi tim operasi.
Masalah klasik yang sering dihadapi adalah the needle in a haystack: pengguna melaporkan bahwa proses checkout terkadang memakan waktu 8 detik, namun log server hanya menunjukkan status sukses 200 tanpa petunjuk service mana yang menyebabkan kelambatan tersebut.
Pendekatan logging tradisional yang terisolasi per mesin tidak lagi memadai. Standar industri modern mengadopsi OpenTelemetry (OTel) untuk menyatukan tiga pilar observability (Traces, Metrics, dan Logs) dalam satu konteks eksekusi terkoordinasi.
Tiga Pilar Observability Terpadu #
OpenTelemetry adalah proyek open-source di bawah naungan Cloud Native Computing Foundation (CNCF) yang menyediakan Application Programming Interface (API), Software Development Kit (SDK), dan perkakas pengumpul data standar vendor-agnostik.
| Komponen OTel | Fungsi Utama | Contoh Kasus Penggunaan |
|---|---|---|
| Traces (Distributed Tracing) | Merekam jalur end-to-end dari satu request pengguna melintasi seluruh service | Menemukan bahwa query SQL pada service inventory menghabiskan 6.8 detik dari total 7.2 detik request |
| Metrics | Data numerik teragregasi yang diukur dalam rentang waktu teratur | Memantau persentil latensi p99, penggunaan memori pod, dan rasio error HTTP 5xx |
| Logs | Catatan tekstual terstruktur yang memuat detail peristiwa spesifik | Merekam payload error validasi atau jejak exception stack trace dengan korelasi Trace ID |
Konsep W3C Trace Context dan Span Propagation #
Jantung dari distributed tracing adalah propagasi konteks jejak (context propagation). Standar W3C Trace Context mendefinisikan header HTTP standar yang diteruskan dari satu service ke service berikutnya:
traceparent: Berisi informasi versi, Trace ID (32 karakter hex unik yang sama untuk seluruh perjalanan request), Parent Span ID (16 karakter hex), dan flag pengambilan sampel (trace flags).tracestate: Menyimpan metadata khusus vendor atau sistem tanpa merusak integritas Trace ID.
Browser Pengguna
│
│ GET /api/orders (Header: traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01)
▼
┌────────────────────────────────────────────────────────┐
│ API Gateway / NestJS (Root Span) │
│ - Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736 │
│ - Span ID : 00f067aa0ba902b7 │
└────────────────────────────────────────────────────────┘
│
│ Panggilan HTTP Internal (Meneruskan traceparent yang sama)
▼
┌────────────────────────────────────────────────────────┐
│ Payment Service (Child Span A) │
│ - Parent Span ID: 00f067aa0ba902b7 │
│ - Span ID Baru : 5fb397be34d23b0f │
└────────────────────────────────────────────────────────┘
│
│ Query Database (Child Span B)
▼
┌────────────────────────────────────────────────────────┐
│ PostgreSQL Query (Child Span C) │
│ - Eksekusi: "SELECT * FROM orders WHERE id = $1" │
│ - Durasi : 1.2 ms │
└────────────────────────────────────────────────────────┘
Dengan meneruskan header ini di setiap panggilan HTTP, message broker, atau background task, sistem observability (seperti Jaeger, Grafana Tempo, atau Datadog) dapat merangkai visualisasi grafik pohon interaktif yang menampilkan durasi tiap langkah secara akurat.
Mengonfigurasi OpenTelemetry Node.js SDK pada Backend NestJS #
Untuk mengotomasi instrumentasi tanpa perlu menulis kode tracing manual di setiap fungsi, pasang paket OTel resmi pada service backend:
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
Buat file inisialisasi tracing yang dijalankan sebelum aplikasi NestJS dimulai (tracer.ts):
// backend/src/tracer.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
// Konfigurasi exporter OTLP menuju OpenTelemetry Collector atau Grafana Tempo
const traceExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://collector.internal:4318/v1/traces',
});
export const otelSDK = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'puluhlabs-backend-api',
[SemanticResourceAttributes.SERVICE_VERSION]: '2.4.0',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'production',
}),
traceExporter,
instrumentations: [
getNodeAutoInstrumentations({
// Aktifkan auto-instrumentation untuk HTTP, Express, NestJS, Prisma, dan Redis
'@opentelemetry/instrumentation-fs': { enabled: false }, // Matikan FS untuk hemat memori
}),
],
});
// Mulai SDK sebelum proses aplikasi berjalan
otelSDK.start();
process.on('SIGTERM', () => {
otelSDK
.shutdown()
.then(() => console.log('Tracing SDK dimatikan'))
.catch((err) => console.error('Gagal mematikan Tracing SDK', err))
.finally(() => process.exit(0));
});
Jalankan server backend dengan menyertakan flag preloading Node.js:
node --require ./dist/tracer.js ./dist/main.js
Menghubungkan Structured Logging dengan Trace ID #
Keuntungan terbesar dari instrumentasi OpenTelemetry adalah kemampuan menautkan log error langsung ke span trace. Ketika terjadi exception pada aplikasi, logger menyisipkan Trace ID aktif ke dalam payload JSON log:
// Contoh interceptor NestJS untuk menyuntikkan traceId ke response header dan log
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { trace } from '@opentelemetry/api';
@Injectable()
export class TracingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const activeSpan = trace.getActiveSpan();
if (activeSpan) {
const traceId = activeSpan.spanContext().traceId;
const res = context.switchToHttp().getResponse();
// Sisipkan header agar frontend atau klien dapat mereferensikan Trace ID saat komplain
res.setHeader('X-Trace-Id', traceId);
}
return next.handle();
}
}
Ketika seorang pengguna melaporkan error transaksi dengan menyertakan X-Trace-Id, engineer cukup menempelkan string 32 karakter tersebut ke dashboard monitoring untuk melihat seluruh rekaman log, query SQL yang dieksekusi, dan latency tiap service dalam satu layar tanpa perlu menebak-nebak waktu kejadian.
Checklist Evaluasi Observability Enterprise #
- Seluruh service microservices dan API gateway menggunakan format W3C Trace Context seragam.
- Sampling rate diatur proporsional (misalnya 10% sampling untuk transaksi normal dan 100% sampling untuk transaksi yang menghasilkan status HTTP 5xx atau latensi > 1.000 ms).
- Payload log tidak memuat data sensitif (seperti nomor kartu kredit, kata sandi, atau token autentikasi rahasia) sesuai standar kepatuhan UU PDP.
- Metrik latensi p95 dan p99 dikonfigurasikan dengan batas alert otomatis ke saluran komunikasi darurat (seperti PagerDuty atau alert WhatsApp) saat terjadi anomali degradasi performa.
