# Stopwatch > A profiler and debugging aid for PHP and Laravel, distributed as the Composer package `sandermuller/stopwatch`. Add checkpoints to a request, command or job, read the time between them, and attach values plus query, memory and outbound-HTTP metrics. With debug mode on, every checkpoint is written to disk as it happens, so an agent can add `probe()` calls, reproduce, and read `stopwatch:runs:show latest`. It looks at the run in front of you; it aggregates nothing across requests. Key properties an agent should know before reaching for it: - **Not an APM.** No cross-request aggregation, no service map, no trend alerting. For "which segment of this request is slow", it is the right tool; for "has p95 regressed this week", it is not. - **Off is free.** `STOPWATCH_ENABLED=false`, or `stopwatch()->disable()`, makes every call a no-op. Leaving instrumentation in shipped code is intended. - **Debug mode is opt-in and guarded.** `STOPWATCH_DEBUG=true` streams each checkpoint to `.jsonl` and forces the run log on, but only when `APP_DEBUG=true` or `APP_ENV` is `local`/`testing`, and never under Octane; a blocked process leaves a note, so `stopwatch:runs:list` and `stopwatch:runs:show latest` say why. Metadata is written as passed. Write probes as `stopwatch()->probe(` and remove them afterwards. - **The run log is opt-in and Laravel-only.** `STOPWATCH_LOG_RUNS=true` turns it on, it is unsupported under Octane and Swoole, and runs faster than 50ms are skipped by default. Never assume a run was recorded. - **The toolbar is default-deny by environment.** Its panel exposes raw SQL with bound values, so `STOPWATCH_INJECT_ENVIRONMENTS` defaults to `local` alone. Do not widen it to a rule like "not production" for an internet-reachable environment. - **HTTP tracking sees Laravel's `Http::` facade only.** A direct Guzzle client bypasses the event dispatcher and is invisible, the same limitation Telescope has. - **PII is opt-in, in both directions.** SQL bindings and exception messages are excluded from the run log until explicitly enabled; stack-trace `args` are never persisted, and hidden context is never read. ## Formats - [llms-full.txt](https://sandermuller.github.io/Stopwatch/llms-full.txt): every page below, in reading order, in one fetch - Any page is also served as markdown at its own URL plus `.md` (for example [checkpoints.md](https://sandermuller.github.io/Stopwatch/checkpoints.md)) ## Getting started - [Why Stopwatch?](https://sandermuller.github.io/Stopwatch/why-stopwatch): What it answers that a log line does not, and when an APM is the better tool. - [Installation](https://sandermuller.github.io/Stopwatch/installation): Require the package, publish the config, and read your first profile. - [Getting started](https://sandermuller.github.io/Stopwatch/getting-started): Two checkpoints on a slow request, and the line that owns the time. ## Measuring - [Checkpoints](https://sandermuller.github.io/Stopwatch/checkpoints): Mark the path, measure a closure, and choose where each checkpoint is emitted. - [Query, memory and HTTP tracking](https://sandermuller.github.io/Stopwatch/tracking): Attach database, memory and outbound-HTTP metrics to every checkpoint. ## Reading the profile - [HTML report](https://sandermuller.github.io/Stopwatch/html-report): The self-contained card: severity tiers, per-row detail modals, dark mode, print. - [Profiler toolbar](https://sandermuller.github.io/Stopwatch/profiler-toolbar): A Debugbar-style toolbar on eligible HTML responses, off outside allowed environments. - [Server-Timing and Debugbar](https://sandermuller.github.io/Stopwatch/server-timing): Read timings in DevTools, or as a Debugbar timeline tab. ## Run log - [Persistent run log](https://sandermuller.github.io/Stopwatch/run-log): Keep finished runs on disk so a slow request can be read after the fact. - [Crash diagnostics](https://sandermuller.github.io/Stopwatch/crash-diagnostics): Capture the exception behind a failed run and pivot to laravel.log. - [Debugging with probes](https://sandermuller.github.io/Stopwatch/debugging): Debug mode (STOPWATCH_DEBUG=true): probe() calls written to a per-run JSONL stream, read with stopwatch:runs:show latest, compared with stopwatch:runs:diff, and removed with an exact grep or the opt-in PHPStan rule. ## More - [Slow-run notifications](https://sandermuller.github.io/Stopwatch/notifications): Dispatch a notification when a run crosses a duration threshold. - [AI assistant integration](https://sandermuller.github.io/Stopwatch/ai-assistant): Let an agent drive the run log instead of running the commands yourself. - [Standalone PHP](https://sandermuller.github.io/Stopwatch/standalone): Use the profiler outside Laravel, and what needs the container. ## Reference - [Configuration reference](https://sandermuller.github.io/Stopwatch/configuration): Every env var and config key, grouped by the feature that owns it. - [API reference](https://sandermuller.github.io/Stopwatch/api): Lifecycle, serialization, and the runtime enable/disable switch.