Search by

liberusoftware / module-cms-observability

curtisdelicata

Observability for Liberu CMS: a public readiness probe backed by a module-contributed health-check registry, and a backend-agnostic metrics recorder with a zero-infra log-channel default.

Package info

github.com/liberusoftware/module-cms-observability

Type:liberu-module

pkg:composer/liberusoftware/module-cms-observability

Statistics

Installs: 44

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.1 2026-11-14 13:56 UTC

README

Repository

Source, issues, and release history: https://github.com/liberusoftware/module-cms-observability

Composer package: https://packagist.org/packages/liberusoftware/module-cms-observability

The operability layer for Liberu CMS (Phase 6.5). Answers "is the system healthy right now?" (readiness) and "what is it doing?" (metrics) as removable seams with zero-infra defaults. Owns no domain data; feature modules contribute checks and the system records metrics through contracts in cms-contracts.

See ADR 0003 — observability as seams.

Readiness — GET /health/ready

Public, unauthenticated, untenanted, deliberately outside /api/v1, with a light per-IP throttle. Returns coarse per-check statuses and no infrastructure detail:

{ "status": "ok", "checks": [ { "name": "database", "status": "ok" } ] }

Overall status is ok / degraded / down. Each check declares its criticality (config-overridable under cms-observability.readiness.critical):

Check Criticality HTTP on failure
database critical 503 (down)
cache degraded 200 (degraded)
queue degraded 200 (degraded)
storage degraded 200 (degraded)
search degraded 200 (degraded)

Only the database is critical by default: the app cannot serve without it, but it can serve with cache / queue / storage degraded — so those must never pull a node out of rotation. The storage check is contributed by cms-media and the search check by cms-search (backed by its active driver's isReady()), both through HealthCheckRegistryInterface — observability imports no feature module.

Metrics

A backend-agnostic recorder seam (MetricsRecorderInterface, StatsD-shaped: increment / timing / gauge). The default LogMetricsRecorder writes one structured record per metric to an isolated log channel (cms-observability.metrics.channel, default cms-metrics), so the seam is exercised out of the box without polluting the app log. NullMetricsRecorder is bound when cms-observability.metrics.enabled is false.

No metrics backend is bundled. Binding a Pulse / StatsD / Prometheus recorder is an operator step (documented in docs/PERFORMANCE.md).

Domain counters are recorded by a pure EventBus listener (zero coupling, like the audit and notifications subscribers): content.published, content.state_changed, form.submitted, media.uploaded.

The RecordApiMetrics middleware records api.request count + latency tagged by route name and status. It self-appends to the global middleware stack and self-filters to api/v1/*, so cms-api never references it and no other path (web, /health/ready, /up) is measured.

Design

  • Removable & headless-safe. Depends only on cms-contracts + cms-core; imports no feature module, and no module imports it — it self-registers its route, throttle, and listeners. Removing it just stops the probe and metrics.
  • Zero-infra defaults. Readiness runs on SQLite; metrics land in a log channel. Real backends (Redis, Meilisearch, Pulse) are the operator's choice.