Belajar RoadRunner - Logging & Observability
Episode 9 of 26

Belajar RoadRunner - Logging & Observability

Mengkonfigurasi plugin logs RoadRunner dengan format dan level, menulis structured logs terpadu antara server Go dan worker PHP, memakai PSR-3 logger di worker, dan mengekspor observability ke OpenTelemetry atau file logs.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 8 kita menyusun middleware, pada episode kali ini kita memastikan semua yang terjadi bisa diamati: logging dan observability. Di aplikasi long-running, kesalahan tidak datang sebagai satu request yang gagal — melainkan sebagai pola lambat yang tersebar di ribuan request yang dilayani banyak worker. Tanpa log dan telemetry yang baik, kalian buta.

Mengapa episode ini penting? Karena RoadRunner punya dua dunia yang harus dicatat: server Go (apa yang dilakukan server, restart worker, error pool) dan worker PHP (apa yang dilakukan aplikasi). Menyatukan keduanya dalam format log yang sama adalah kunci debugging lintas lapisan.

Plugin logs RoadRunner

RoadRunner memiliki plugin logging bawaan yang menangkap log dari server Go dan plugin-pluginnya. Konfigurasinya:

Blok logs
logs:
  mode: production
  level: info
  encoding: json
  output: stdout
  err_output: stderr
  channels:
    server:
      mode: production
      level: info
      encoding: json
    http:
      mode: production
      level: debug
      encoding: json
KeyFungsi
modeproduction (JSON) atau development (human-readable)
leveldebug, info, warn, error, dpanic, panic, fatal
encodingjson, console, atau zap
output / err_outputTujuan log normal dan error (stdout/stderr/file)
channelsOverride per-plugin (misal debug lebih detail untuk http)

Untuk menulis ke file:

Log ke file
logs:
  mode: production
  level: info
  encoding: json
  output: /var/log/rr/server.log
  err_output: /var/log/rr/server.err.log

Logging di Worker PHP

Log aplikasi PHP ditangani terpisah — memakai logger PSR-3 (Monolog). Kuncinya: format log PHP sebaiknya sejalan dengan format log Go agar bisa di-agregasi (misal ke Loki/ELK). Contoh worker dengan Monolog:

Logger PSR-3 di worker
<?php
 
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Formatter\JsonFormatter;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\Worker;
 
require __DIR__ . '/vendor/autoload.php';
 
$log = new Logger('app');
$handler = new StreamHandler('php://stderr');
$handler->setFormatter(new JsonFormatter());
$log->pushHandler($handler);
 
$psr7 = new PSR7Worker(Worker::create(), $psr17, $psr17, $psr17);
 
while ($request = $psr7->waitRequest()) {
    $start = microtime(true);
    try {
        // ... proses request ...
        $log->info('request.ok', [
            'method' => $request->getMethod(),
            'path' => $request->getUri()->getPath(),
            'duration' => round(microtime(true) - $start, 4),
        ]);
        $psr7->respond($response);
    } catch (\Throwable $e) {
        $log->error('request.failed', [
            'exception' => $e::class,
            'message' => $e->getMessage(),
        ]);
        $psr7->getWorker()->error((string) $e);
    }
}

Poin penting: log ke stderr — RoadRunner menyalurkan stderr worker ke log-nya sendiri dengan konteks worker, sehingga log aplikasi muncul menyatu dengan log server. Dan selalu sertakan konteks terstruktur (method, path, durasi, request id) — bukan string bebas.

Menyatukan Log: Request ID

Agar bisa menelusuri satu request lintas lapisan, buat request ID di middleware dan bawa ke seluruh log:

Request ID di level middleware
http:
  middleware: ["requestid", "gzip"]
  requestid:
    header: X-Request-Id

Plugin requestid (atau middleware kustom PHP) memberi tiap request ID unik; worker menambahkan ID ini ke semua log context. Dengan begitu kalian bisa grep "req-abc123" dan melihat perjalanan lengkap request — dari log server Go sampai log aplikasi.

Structured Logging vs Human Readable

ModeKeuntunganKerugian
production (JSON)Mudah di-parse tool (Loki, ELK, CloudWatch), konteks terstrukturSulit dibaca manusia langsung
development (console)Nyaman di terminal saat debuggingTidak ideal untuk agregasi

Gunakan development di lokal, production di staging/production. Kalian bisa memilih per-channel, jadi plugin tertentu boleh lebih verbose.

Observability: OpenTelemetry dan Export

Untuk observability penuh, RoadRunner mendukung ekspor telemetry ke OpenTelemetry:

Konfigurasi OpenTelemetry
otel:
  address: 127.0.0.1:4318
  service_name: roadrunner-app
  insecure: true

Dengan otel aktif, server Go mengirim trace dan metrics ke OpenTelemetry Collector. Di sisi PHP, kalian memakai SDK OpenTelemetry PHP untuk instrumentasi aplikasi — span dari PHP dan span dari Go terhubung via konteks yang disebar antar lapisan. Ini pola observability yang sama dipakai bersama plugin metrics Prometheus (episode 12).

Tip

Mulai dari yang sederhana: pastikan JSON logs server + worker menyatu, lalu tambahkan request ID. OpenTelemetry adalah peningkatan berikutnya setelah pola dasar ini stabil — jangan langsung pasang semua sekaligus sebelum kalian nyaman membaca log.

Penutup

Pada episode 9 ini, kalian telah membangun fondasi observability.

Inti yang harus dibawa pulang:

  • Plugin logs mengatur log server Go: mode, level, encoding, output, channel.
  • Log worker PHP via PSR-3 ke stderr — menyatu dengan log server.
  • Selalu pakai structured context dan request ID lintas lapisan.
  • production JSON untuk produksi, development untuk lokal.
  • OpenTelemetry (otel plugin) untuk trace dan metrics penuh.

Di episode 10 selanjutnya, kita masuk ke data: KV plugin dengan driver Redis, Memory, dan File — menyimpan cache dan session, memakai TTL, dan memilih driver yang tepat untuk kebutuhan. Sampai jumpa di episode 10!