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.
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(), saturequest_idmenyatukan seluruh jejak. - Sangat berguna untuk multi-tenancy:
tenant_idikut 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 antrean | Ya | Ya |
Ikut tercetak lewat Context::all() | Ya | Tidak |
| Untuk | ID, nama rute, tenant | Token, 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
| Alat | Cakupan | Ikut ke antrean |
|---|---|---|
Context | Permintaan + job turunannya | Ya |
Log::withContext() | Hanya log, dalam proses ini | Tidak |
| Session | Antar permintaan satu pengguna | Tidak |
Container scoped | Satu permintaan | Tidak |
| Properti job | Satu job | Manual, 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.