audunru/memory-usage
A lightweight Laravel package for monitoring memory usage in your application. It provides simple helpers to read current and peak memory consumption, making it easy to log, display, or debug resource usage with minimal setup.
Architecture fit
audunru/memory-usage is a deliberately narrow observability package: a service provider, two global middleware (LogMemoryUsage, LogSlowResponse), and two helper classes. It measures PHP peak memory and response time, then writes to a configured log channel from terminate() — after the response is sent, so streamed bodies are included and no latency is added to the user-visible request. There are no schema changes, no queue workers, no external services, and no persistent state. For a TPM this reads as a low-commitment, reversible dependency: it sits in the HTTP kernel, it can be scoped by path rule, and removing it is a Composer uninstall plus a config deletion. The main architectural question is not whether it fits — it fits any conventional Laravel app — but whether it is additive to an observability stack we already pay for.
Integration feasibility
Standard Composer install with package auto-discovery, a publishable config (--tag=memory-usage-config), and conditional middleware registration driven by memory-usage.enabled and memory-usage.slow_response_enabled. The config model is more capable than the package description implies: paths is an array of rule sets, each with patterns, ignore_patterns, limit (MiB), slow_response_limit (seconds), channel, level, and optional header.environments. Rules are evaluated independently, so the same path can fire a warning at 10 MiB to the default channel and an emergency at 100 MiB to Slack. That is enough control to make production use defensible without writing a wrapper.
Technical risk Risk is low in blast radius but material in data quality, and this release is the proof:
memory_get_peak_usage() is process-scoped and PHP does not reliably return freed memory to the OS. Under PHP-FPM this is mostly tolerable; under Octane/Swoole or long-running workers, per-request "peak" is only meaningful if the reset is correct in that execution model. The patch note says "per matched request" — we must confirm that covers worker reuse, not just sequential FPM requests.enabled flags are read at boot by the service provider, so they cannot be toggled at runtime; a misconfiguration silently yields no data while the team believes instrumentation is live.header.environments, otherwise we leak internal memory figures to clients.Key questions
patterns/ignore_patterns match — path, route name, regex — and what is the matching cost per request?Stack fit Fits any conventional Laravel HTTP application. The decision is positioning: if we already run Pulse or a commercial APM with memory graphs, scope this to a targeted diagnostic on suspected-heavy endpoints rather than a permanent fleet-wide dependency. If we have no memory visibility — the common case — it is a cheap, credible gap-filler for route-level hot-spot ranking, and the slow-response logging gives us a second, complementary signal from the same config block.
Migration path
Greenfield; no data migration. Pin ^5.1.1 explicitly and do not permit ^5.1.0 in any constraint, including transitive lockfiles in staging images. If 5.1.0 was trialled anywhere, treat its output as unusable and delete it rather than attempting to reconcile it with post-fix numbers.
Compatibility
Verify PHP and Laravel constraints against our runtime before adoption. Confirm php artisan config:cache works with the published config, that global middleware ordering does not conflict with existing entries, that the chosen log channel has retention and rotation configured, and — if we run Octane — that the reset semantics hold. Octane is a gating check, not a nice-to-have. Also confirm the response header is env-gated out of production.
Sequencing
limit and channel per tier; set ignore_patterns for health checks and static assets.Maintenance Small dependency surface, disciplined release process (conventional commits, automated semver, patch releases). Patch-level upgrades should be near-zero effort. The real risk is single-author concentration, mitigated substantially by trivial removal — no data to migrate, no schema to unwind. We should still name an internal owner and document the exit path.
Support Community-supported only, no vendor SLA, escalation via GitHub issues. Acceptable for a diagnostic tool; not acceptable as a dependency of an on-call paging path unless we own the wrapper, thresholds, and runbook. Treat it as an input to prioritisation, not a source of truth for alerts.
Scaling
Compute cost is negligible; log ingest and retention are the scaling vector. Blanket capture across a high-QPS fleet will show up as a line item. Path matching, per-rule channels and levels, and ignore_patterns are the levers — this package is well-suited to targeted instrumentation and poorly suited to global capture.
Failure modes
enabled is toggled after boot; the team believes it has data and does not.header.environments is misconfigured.Ramp-up Low mechanically — an engineer can install, configure, and validate in under a day. The real cost is interpretive: reading PHP memory numbers correctly requires literacy in allocator behaviour, opcache, and FPM-versus-Octane semantics. Budget for a short internal write-up that defines what the metric does and does not mean, records that pre-5.1.1 figures are invalid for matched requests, and states plainly that this complements rather than replaces an APM/profiling strategy.
How can I help you explore Laravel packages today?