Skip to content

Observability

Version: 0.1.0 · Status: Draft

Draft: isi di bawah kontrak yang berlaku.

1. Cakupan

Kontrak observability untuk engine Resource Plane (formspec serve — proses yang menjalankan handler bisnis tenant). Ia mendefinisikan apa yang wajib di-emit setiap implementasi engine sehingga operator manapun (dan reimplementasi engine yang konform) bisa dimonitor dengan cara yang sama: logging terstruktur, metrics, tracing, dan kosakata health yang machine-readable.

Ini bukan alerting engine dan bukan observability Control Plane. Observability governance (transparency log, decision log, evidence) adalah urusan 04-control-plane.md §7 dan 05-plane-protocol.md §4.4; kosakata health yang didefinisikan di sini (§5) adalah kontrak yang dikonsumsi oleh evidence health Plane Protocol dan oleh formspec/ops — tetapi engine tidak pernah mendorong alert sendiri (§6).

Larangan keras (normatif, tidak bisa dikonfigurasi lepas): telemetry tidak boleh membawa data bisnis tenant di luar level debug yang di-gate eksplisit (§2). Ini perluasan langsung batas Control ↔ Resource Plane (04-control-plane.md §1): sebagaimana evidence tidak pernah berisi data bisnis (05-plane-protocol.md §3.4), telemetry pun tidak.

2. Structured Logging

Engine wajib meng-emit log sebagai JSON lines (satu objek JSON per baris) ke stdout — satu stream, di-collect oleh stack operator (K8s log pipeline, Loki, dsb). Teks bebas non-JSON tidak konform di mode prod.

2.1 Field Wajib

Setiap record log level info ke atas wajib membawa field berikut (nilai kosong ditulis null, bukan dihilangkan):

FieldIsi
timestampRFC 3339 UTC, presisi milidetik
leveldebug | info | warn | error
request_idID korelasi request (§2.3); null untuk log di luar konteks request (boot, scheduler)
workspaceWorkspace ID; identitas dari infrastruktur, bukan dari kode aplikasi
moduleModule yang mengeksekusi
entityNama entity/Entity yang disentuh; null kalau tidak relevan
actionAction yang dieksekusi; null di luar konteks action
actorIdentitas pemanggil (user ID atau service identity); tidak pernah PII mentah (nama/email) — hanya ID
duration_msDurasi operasi, integer milidetik; hanya pada record penutup operasi
error_codeKode error kontrak (mis. DATASTORE_PERMISSION_DENIED); null saat sukses
trace_idTrace ID OpenTelemetry (§4) untuk korelasi log ↔ trace
environmentNama environment; hanya untuk atribusi log, tidak pernah untuk percabangan bisnis (../backend/02-core-extended.md §8)

Field tambahan spesifik-engine boleh ditambahkan; consumer wajib mengabaikan field yang tidak dikenal (forward-compatible).

2.2 PII & Data Discipline

info/warn/error wajib berisi hanya metadata (siapa, apa, berapa lama, kode hasil) — tidak pernah payload field bisnis, nilai record, argumen query, atau body request. Nilai bisnis yang membantu debugging hanya boleh muncul di level debug, yang wajib off secara default di prod dan hanya diaktifkan lewat kontrol operator yang tercatat (bukan flag aplikasi). Pesan error yang dipropagasi ke log wajib sudah di-redact dari nilai bisnis — kode error + identifier resource, bukan isinya.

2.3 Korelasi via request_id

Engine wajib menerbitkan request_id di boundary masuk (HTTP request, pesan queue, tick scheduler) kalau belum ada, atau meneruskan yang datang dari upstream. request_id yang sama wajib dipropagasi ke: script Starlark (terbaca sebagai ctx.request_id), call ke sidecar (lewat header transport — bagian dari kontrak wire sidecar), dan operasi ctx.* turunan. Ini yang membuat satu request bisa dijejak lintas engine → script/sidecar → persist dalam log, dan berpasangan dengan trace_id untuk tracing (§4).

3. Metrics

Engine wajib meng-expose endpoint metrics format Prometheus (text exposition, GET /metrics) pada listener administratif yang terpisah dari traffic bisnis. Endpoint ini tidak pernah membawa data bisnis — hanya counter, gauge, dan histogram beragregat.

3.1 Set Metric Minimal Wajib

Setiap engine konform wajib meng-emit minimal metric berikut (nama boleh di-prefix implementasi, semantik wajib identik):

MetricTipeMakna
http_requests_totalcounterJumlah request, per route_class + method + status_class
http_request_duration_secondshistogramLatensi request per route_class
http_request_errors_totalcounterRequest gagal per route_class + error_code
action_duration_secondshistogramDurasi eksekusi action per module + action
action_errors_totalcounterAction gagal per module + action + error_code
outbox_pendinggaugeKedalaman antrian outbox yang belum ter-flush
outbox_lag_secondsgaugeUmur entri outbox tertua yang belum terkirim
ws_connectionsgaugeKoneksi websocket aktif
db_pool_open / db_pool_idle / db_pool_wait_totalgauge / counterStatistik ConnectionPool per Datastore (06-datastore.md §7)
snapshot_age_secondsgaugeUmur snapshot Plane Protocol terakhir (dasar degradasi 05-plane-protocol.md §5)

3.2 Label & Kardinalitas

Label wajib dibatasi ke dimensi ber-kardinalitas terbatas yang diketahui dari manifest: workspace (dalam model 1 workspace = 1 Deployment praktis konstan per pod — ../../architecture/05-failover.md §3), module, action, route_class, error_code, status_class (2xx/4xx/5xx, bukan status mentah).

Dilarang jadi label (normatif — sumber ledakan kardinalitas): entity instance ID, request_id, actor, path URL mentah, nilai field bisnis, atau nilai bebas apapun yang berasal dari input tenant. route_class mengelompokkan rute menurut kelas (entity CRUD, action invoke, admin panel, websocket, health), bukan path per-record, justru supaya kardinalitas tetap terbatas.

4. Tracing

Engine wajib kompatibel OpenTelemetry: satu request menghasilkan span tree HTTP → action → script/sidecar → persist, dengan span persist menandai operasi ctx.* (query, lock, queue). Atribut span tunduk pada disiplin data yang sama dengan log (§2.2) — tidak ada nilai bisnis di atribut span pada level default.

Propagasi konteks trace adalah bagian kontrak wire. Engine wajib meng-inject dan menerima trace context format W3C Trace Context (traceparent/tracestate) pada: request HTTP masuk, dan setiap panggilan ke sidecar lewat transport SDK (lib-formspec-*). Ini menjadikan trace utuh lintas proses engine ↔ sidecar sebuah kewajiban interoperabilitas, bukan opsi implementasi — SDK sidecar konform wajib meneruskan header ini ke span-nya sendiri. Export trace (endpoint OTLP, sampling rate) adalah konfigurasi operator, bukan bagian kontrak.

5. Kosakata Health (Machine-Readable)

Engine wajib meng-expose GET /health yang mengembalikan status machine-readable memakai kosakata tunggal yang sama dengan ../../architecture/05-failover.md §7:

StatusArtiKonsekuensi
healthySemua dependency terjangkau, dalam ambangTerima traffic
degradedMasih melayani, tapi ada dependency menurun (lihat reasons)Tetap terima traffic; sinyal ke ops
unhealthyTidak bisa melayani dengan benarKeluar dari endpoint Service

Response wajib berbentuk { "status": "...", "reasons": [...], "checked_at": "..." }. reasons[] memakai kode terkontrol, minimal: snapshot_stale (umur snapshot ≥ ambang policy, 05-plane-protocol.md §4), datastore_unreachable, db_pool_exhausted, outbox_backlog, control_plane_unreachable. status: dead tidak self-reported — itu turunan observasi Control Plane (3× missed heartbeat) dan tetap didefinisikan di failover §7, bukan di sini.

Endpoint yang sama melayani liveness dan readiness probe K8s (../../architecture/05-failover.md §2): liveness lulus selama proses hidup dan event-loop responsif; readiness lulus hanya saat status ∈ {healthy, degraded}. Ringkasan health ini adalah isi evidence health yang dikirim ke Control Plane (05-plane-protocol.md §4.4) — hitungan/status saja, tidak pernah data bisnis.

6. Alerting — Stance

FormSpec tidak membangun alerting engine sendiri. Kontrak ini berhenti pada mengekspos metrics (§3) dan health (§5) dalam format standar; rule alerting, threshold, routing, dan on-call adalah tanggung jawab stack operator (Prometheus Alertmanager, Grafana, dsb). Alasan sama dengan larangan write-back Plane Protocol: engine melaporkan keadaan, keputusan (termasuk "ini layak dibangunkan tengah malam") ada di lapisan governance/ops operator. formspec/ops (aplikasi FormSpec first-party di atas Control Plane, bukan bagian engine) boleh membangun surface alerting di atas kosakata health ini — tetapi itu aplikasi, bukan kewajiban engine.

7. formspec logs

Verb CLI untuk membaca stream log terstruktur (§2) dari engine — tail dan filter tanpa harus menyaring JSON manual. Normatif untuk perilaku; implementasi CLI mengikuti dokumen ini.

bash
formspec logs --workspace corp-456 --follow            # tail live
formspec logs --module billing --entity invoice        # filter per module/entity
formspec logs --level error --since 1h                 # hanya error, jendela waktu
formspec logs --request-id req-abc123                   # satu request, lintas komponen
FlagFungsi
--follow / -fTail berkelanjutan
--workspaceFilter workspace
--module / --entity / --actionFilter dimensi eksekusi
--levelLevel minimum (debug/info/warn/error)
--request-idAmbil semua record satu request (korelasi §2.3)
--since / --untilJendela waktu
--outputpretty (default TTY) | json (raw JSON lines)

formspec logs tidak pernah menembus disiplin PII (§2.2): kalau debug tidak diaktifkan operator, nilai bisnis tetap tidak ada di stream, dan formspec logs tidak bisa memunculkannya. Verb ini wajib ditambahkan ke referensi CLI (../../cli-tools/02-formspec-cli.md).

8. Kode Error

KodeKondisi
OBSERVABILITY_METRICS_DISABLEDEndpoint /metrics diminta tapi dimatikan konfigurasi
OBSERVABILITY_DEBUG_FORBIDDENAktivasi log debug dicoba di prod tanpa otorisasi operator
LOGS_FILTER_INVALIDKombinasi filter formspec logs tidak valid

Standar terbuka (CC0) dengan implementasi referensi.