Skip to content

Configuration

The SDK is configured entirely through environment variables, merged into the monitor config namespace. Publishing config/monitor.php is optional (see Installation).

Minimum setup

Add the following to your application's .env:

dotenv
MONITOR_ENABLED=true
MONITOR_KEY=your-project-api-key

# Only for a self-hosted instance: the hosted service is the default
MONITOR_URL=https://monitor.example.com
  • MONITOR_KEY is the per-project API key generated in the Quiet Guard dashboard. It is shown only once at project creation. See the server documentation for how projects and keys are created.
  • MONITOR_URL defaults to https://quietguard.dev, the hosted service, so on the hosted service you have only the key to paste. Set it to point the SDK at your own instance if you self-host.

A base URL that already ends in /api or /api/v1 is accepted and normalised: the SDK appends /api/v1/... itself, so the suffix is stripped rather than doubled. Both shapes work, which means reading the API address off the dashboard cannot silently answer 404 on every call.

When MONITOR_KEY is empty, the SDK stays completely silent, it never sends anything and never errors. The key is the only thing that can be missing: from version 0.3.1, an empty MONITOR_URL stops nothing, it resolves to the hosted service (up to version 0.3.0, it silently cut off exception and log reporting). A machine that has a key and no address, a staging box for instance, therefore now reports to the hosted service. To keep it quiet, empty its key or use MONITOR_ENABLED=false, which says so.

All options

Env variableConfig keyDefaultDescription
MONITOR_ENABLEDmonitor.enabledtrueMaster switch. When false, no data leaves the app.
MONITOR_URLmonitor.urlhttps://quietguard.devBase URL of the Quiet Guard server. The hosted service by default; set it to your own instance if you self-host. A trailing /api or /api/v1 is stripped.
MONITOR_KEYmonitor.keynullPer-project API key (sent as a Bearer token).
MONITOR_ENVIRONMENTSmonitor.environmentsproduction,stagingComma-separated allowlist of app environments to report from. Empty = report from every environment.
—monitor.ignore_paths[]Request paths whose exceptions are never reported, in Illuminate\Http\Request::is() syntax.
MONITOR_RELEASEmonitor.releasenullDeployed version identifier (git SHA or tag) attached to events. Empty = detected automatically.
MONITOR_RELEASE_AUTOmonitor.release_autotrueDetects the release from a REVISION file or .git/HEAD when MONITOR_RELEASE is empty. See Release.
MONITOR_TIMEOUTmonitor.timeout3HTTP timeout in seconds for ingestion calls.
MONITOR_QUEUEmonitor.queuefalsefalse sends synchronously, true pushes reports onto the application's default queue connection, the name of a queue connection pushes onto that connection. A name that no connection carries falls back to a synchronous exception report and logs a warning.
MONITOR_TRACE_LIMITmonitor.trace_limit0Maximum number of stack-trace frames sent per exception. 0 (default) sends the full trace; set a frame count only if you need to trim payloads. The throw site, at the head of the stack since version 0.3, counts as one frame: 1 sends the site alone.
MONITOR_CODE_SNIPPETSmonitor.code_snippetstrueSends, with each frame of your application, the five lines of code on each side of its own, so the dashboard shows the failing line in context. Never for a dependency; a line that names a scrub key and gives it a value is masked whole. false sends file and line only. See Exception reporting.
MONITOR_LOGS_ENABLEDmonitor.logs.enabledfalseOpt-in forwarding of application logs. See Logging.
MONITOR_LOG_LEVELmonitor.logs.levelwarningMinimum PSR-3 level to forward.
MONITOR_LOGS_MAX_BATCHmonitor.logs.max_batch200Maximum log entries buffered before an early flush. Clamped to 500, the largest batch the server accepts.
MONITOR_SCHEDULER_HEARTBEATmonitor.heartbeats.schedulertruePings the scheduler heartbeat each time schedule:run starts, at your cron's own cadence. See Heartbeats.
MONITOR_QUEUE_HEARTBEATSmonitor.heartbeats.queuestrueQueues watched by a ping from the worker on every loop turn, as comma-separated connection:queue. true = every queue a worker consumes; false = none.
MONITOR_HEARTBEAT_ENVIRONMENTSmonitor.heartbeats.environmentsproductionEnvironments the automatic heartbeats leave from. Narrower than MONITOR_ENVIRONMENTS on purpose.
—monitor.scrubsee belowRequest/context keys whose values are masked before leaving the app.
—monitor.redactemail, iban, nir, card, phoneValue shapes masked wherever they sit, message text included. See Exception reporting.
—monitor.redact_custom[]Your own shapes, as label => PCRE pattern.

Environments

By default the SDK only reports from production and staging. This means that in your local environment nothing is sent unless you change the list:

dotenv
# Report from every environment
MONITOR_ENVIRONMENTS=

# Or add local while testing the integration
MONITOR_ENVIRONMENTS=production,staging,local

The check uses Laravel's app()->environment(), i.e. your APP_ENV value. Automatic heartbeats also go through their own list, MONITOR_HEARTBEAT_ENVIRONMENTS, production by default: both lists must allow it.

Release

The release is the identifier of the deployed version, attached to every event: the server uses it to attribute an issue to a specific deploy and, when it is a commit SHA, to open from the issue the synced commit that was running when the error happened.

There is nothing to set in the common case: when MONITOR_RELEASE is empty, the SDK detects the release by itself, once per process, by reading in order a REVISION file at the application root (Capistrano, Deployer and most deploy scripts write one), then the checkout's own .git/HEAD, following the branch ref down to the commit. An explicit value always wins:

dotenv
MONITOR_RELEASE=v2.4.1

To send only the configured value, for instance when the checkout on the server is not the branch that actually runs, switch detection off:

dotenv
MONITOR_RELEASE_AUTO=false

Automate the git hash at deploy time

The server links an issue to a commit only when the release is that commit's SHA. Depending on how you deploy, here is how that hash reaches MONITOR_RELEASE without anybody typing it.

Laravel Forge. In zero-downtime mode every release directory is a git checkout: .git/HEAD carries the SHA and there is nothing to do, a --depth 1 clone included. In classic mode, add one line to the deploy script after the git pull:

bash
git rev-parse HEAD > REVISION

Envoyer. Envoyer exposes the deployed SHA as the {{ sha }} variable; write it to a REVISION file from a deployment hook, before the release is activated:

bash
cd {{ release }} && echo "{{ sha }}" > REVISION

GitHub Actions, or any CI. The commit SHA is in GITHUB_SHA; write it into the .env of the deployed artifact, or into a REVISION file shipped with it:

bash
echo "MONITOR_RELEASE=${GITHUB_SHA}" >> .env

If your configuration is cached, run php artisan config:cache again after changing .env, otherwise the cached value stays.

Docker. Pass the hash at build time and freeze it into an environment variable:

dockerfile
ARG GIT_SHA
ENV MONITOR_RELEASE=$GIT_SHA
bash
docker build --build-arg GIT_SHA=$(git rev-parse HEAD) .

A server updated with git pull. The repository's .git/HEAD is read; there is nothing to do.

The server matches the release against the full 40-character SHA of the synced commits; an abbreviated SHA of at least 7 characters is resolved as a prefix. A tag (v2.4.1) works as a label for a deploy but links to no commit.

Asynchronous reporting

By default reports are sent synchronously during the request, bounded by MONITOR_TIMEOUT. To remove that work from the request lifecycle, push it onto a queue:

dotenv
# Use a specific queue connection
MONITOR_QUEUE=redis

# Or the default connection
MONITOR_QUEUE=true

Reports are then dispatched as queued jobs (SendExceptionToMonitor, SendLogsToMonitor), so you need a running queue worker.

MONITOR_QUEUE names a queue connection, a key of your queue.connections array, not a queue name. MONITOR_QUEUE=default, =monitoring or =high name no connection in a stock Laravel application. Such a value is refused for exception reports: the SDK logs a warning naming the connections that do exist and sends the report synchronously, because a report that blocks the request is worth more than a report that never leaves. Log batches read the same setting and have no such fallback, so give it a name your application actually declares.

Two properties of the queued path are worth knowing. Payloads are masked before they are queued, so the shapes the value redactor exists to stop never sit in clear in your own jobs and failed_jobs tables. And a job whose send failed throws, so the second of its two attempts is real and a definitive failure is recorded in failed_jobs instead of being marked as done.

Ignored request paths

By default every exception is reported, wherever it happened. Set ignore_paths to a list of request paths whose exceptions are never reported, written in the syntax Laravel's Illuminate\Http\Request::is() accepts:

php
'ignore_paths' => ['api/v1/*'],

Empty by default. A console command carries a dummy request whose path is /, which matches none of these patterns, so ignore_paths never silences a queued job or a scheduled task, only HTTP requests. It gates exceptions only, logs are not affected.

This exists for an application that hosts a monitoring endpoint of its own: the exceptions raised while serving that endpoint should not be reported back to it.

Scrubbed keys

The default monitor.scrub list masks any request/context key whose name contains one of these terms (case-insensitive, recursive):

password, password_confirmation, passphrase, token, signature, secret,
authorization, cookie, php_auth_pw, api_key, access_token,
referer, referrer,
x-forwarded-for, x-real-ip, cf-connecting-ip,
true-client-ip, x-client-ip, forwarded

referer and referrer are the address of the page the visitor came from, and a URL is where applications put their one-off secrets: an unsubscribe link, a signed download, a password reset, a share link. It is masked by name because it has no shape a value redactor could recognise.

The last six are the headers that carry a visitor's IP address. Behind a reverse proxy, which is the ordinary production topology, one of them holds the address of the person who triggered the error. That is personal data, and it is not about you but about your visitors, who chose nothing. It is masked by default. If you have a lawful basis for keeping it, remove the matching line: the decision is yours, as the controller.

To extend it, publish the config and edit the scrub array. Masked values are replaced with [scrubbed]. See Exception reporting for details.

You are reading the Laravel SDK v1.0 documentation.