← Semua pembelajaran / Laravel Nol → Enterprise
Fase 10 · Enterprise & Capstone

Context — jejak lintas proses

Saat sebuah permintaan memicu tiga job yang masing-masing memicu job lain, menelusuri apa yang terjadi jadi mustahil tanpa benang merah. Context adalah benang itu, dan ia ikut masuk ke antrean secara otomatis.

Sumber asli laravel.com Resmi Rangkuman ~5 menit baca

Intisari

  • Context::add() menyimpan data untuk seluruh siklus permintaan dan job yang dikirimnya.
  • Data context ikut serta secara otomatis saat job diantre — inilah yang membedakannya dari variabel biasa.
  • Context::addHidden() untuk data yang dibawa tapi tidak ikut tercetak di log.
  • Dipasangkan dengan Log::withContext(), satu request_id menyatukan seluruh jejak.
  • Sangat berguna untuk multi-tenancy: tenant_id ikut ke setiap job tanpa perlu dioper manual.

Masalah yang dipecahkan

Permintaan POST /artikel
  ├── log: "artikel dibuat"                   ← permintaan HTTP
  ├── job BuatRingkasan
  │     └── log: "ringkasan selesai"          ← proses lain, beberapa detik kemudian
  ├── job KirimNotifikasi
  │     └── log: "notifikasi terkirim"        ← proses lain lagi
  └── job InvalidasiCdn
        └── log: "cdn diinvalidasi"

Tanpa benang merah: empat baris log yang tidak bisa dihubungkan satu sama lain.

Memakainya

use Illuminate\Support\Facades\Context;

// Middleware, di awal permintaan
Context::add('request_id', $request->header('X-Amzn-Trace-Id') ?? (string) Str::uuid());
Context::add('tenant_id', $request->user()?->tenant_id);
Context::add('rute', $request->route()?->getName());

// Data yang dibawa tapi TIDAK ikut tercetak di log
Context::addHidden('token_internal', $token);
// Di dalam job — nilainya sudah ada, tanpa dioper lewat konstruktor
class BuatRingkasan implements ShouldQueue
{
    public function handle(): void
    {
        Log::info('ringkasan selesai');
        // Log berisi request_id yang sama dengan permintaan HTTP asalnya

        Context::get('tenant_id');       // masih tersedia di sini
    }
}

Inilah yang membedakan Context dari sekadar variabel. Data yang kamu tambahkan ikut diserialisasi bersama job dan dipulihkan di sisi worker. Tanpa itu, satu-satunya cara membawa request_id ke dalam job adalah menambahkannya sebagai parameter konstruktor — di setiap job, selamanya, dan satu yang terlewat memutus rantainya.

Menyambungkannya ke log

// AppServiceProvider::boot()
Log::withContext(Context::all());

// Atau otomatis untuk setiap penulisan log
Context::dehydrating(function (Repository $context) {
    $context->addHidden('dikirim_pada', now()->toIso8601String());
});
{"message":"artikel dibuat","request_id":"Root=1-abc","tenant_id":7,"rute":"artikel.store"}
{"message":"ringkasan selesai","request_id":"Root=1-abc","tenant_id":7}
{"message":"notifikasi terkirim","request_id":"Root=1-abc","tenant_id":7}
{"message":"cdn diinvalidasi","request_id":"Root=1-abc","tenant_id":7}
-- CloudWatch Logs Insights: seluruh jejak satu permintaan
fields @timestamp, message, @logStream
| filter request_id = "Root=1-abc"
| sort @timestamp asc

Satu query itu menjawab pertanyaan yang biasanya butuh setengah jam. Pengguna melaporkan sesuatu yang aneh; kamu punya X-Request-Id dari respons; satu filter mengembalikan setiap baris log dari permintaan itu beserta seluruh job yang dipicunya — melintasi beberapa kontainer sekaligus.

Tampak versus tersembunyi

add()addHidden()
Ikut ke job antreanYaYa
Ikut tercetak lewat Context::all()YaTidak
UntukID, nama rute, tenantToken, data yang tidak boleh masuk log

Jangan pernah menaruh data pribadi di context yang tampak. Ia akan ikut tercetak di setiap baris log yang ditulis selama permintaan itu — termasuk log error yang mungkin dikirim ke layanan pihak ketiga. Nama dan email pengguna lebih baik tetap berupa user_id; yang membacanya bisa mencarinya sendiri kalau memang berhak.

Untuk multi-tenancy

// Middleware
Context::add('tenant_id', $tenant->id);

// Job apa pun bisa memulihkan tenant tanpa dioper manual
class BuatLaporan implements ShouldQueue
{
    public function handle(): void
    {
        Tenant::jalankanSebagai(Context::get('tenant_id'), function () {
            // global scope aktif kembali
        });
    }
}

Ini melengkapi materi multi-tenancy: alih-alih mengingat untuk menambahkan tenant_id ke konstruktor setiap job, konteksnya mengalir sendiri. Tetap sediakan pemeriksaan yang gagal keras kalau tenant tidak ditemukan — mengalir otomatis tidak sama dengan dijamin ada.

Perbedaan dari alat serupa

AlatCakupanIkut ke antrean
ContextPermintaan + job turunannyaYa
Log::withContext()Hanya log, dalam proses iniTidak
SessionAntar permintaan satu penggunaTidak
Container scopedSatu permintaanTidak
Properti jobSatu jobManual, satu per satu

Latihan: tambahkan request_id ke Context di middleware, kirim sebuah job dari controller, dan pastikan log dari job itu memuat request_id yang sama dengan permintaan HTTP-nya. Lalu kembalikan ID itu sebagai header X-Request-Id pada respons.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.