lampwick / laravel
Send monitoring data from your Laravel app to lampwick over HTTPS.
Requires
- php: ^8.2
- ext-curl: *
- ext-json: *
- illuminate/console: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- laravel/nightwatch: ^1.24
Requires (Dev)
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
- ext-pcntl: Lets lampwick:agent flush its buffer on SIGTERM/SIGINT before exiting.
Provides
None
Conflicts
None
Replaces
None
README
Send requests, queries, jobs, exceptions, logs and more from your Laravel app to Lampwick, over HTTPS.
your app ──local TCP──▶ php artisan lampwick:agent ──HTTPS + project token──▶ https://app.lampwick.dev/ingest
Your app hands its records to a small agent on the same server (a quick write to
127.0.0.1:2407, so requests never wait on the network). The agent batches them and sends them
to Lampwick over HTTPS, authenticated with your full project token. If Lampwick is briefly
unreachable, the agent keeps the records in memory (bounded) and retries.
The instrumentation itself comes from the MIT-licensed
laravel/nightwatch package, which is installed as a
dependency (pinned to the versions this package is tested with); you configure everything with
the LAMPWICK_* variables below.
Requirements: PHP 8.2+ with the curl extension, Laravel 12 or 13. The pcntl extension
(present on almost every Linux PHP build) lets the agent flush its buffer when it is stopped.
Install
composer require lampwick/laravel echo "LAMPWICK_TOKEN=uk_..." >> .env php artisan lampwick:agent
Use your project token from the project's Settings page in Lampwick. Open your app; within a few seconds the request shows up in Lampwick. In production, keep the agent running under a process manager (below).
php artisan lampwick:test checks the token, the network and the agent. To also send your
application logs, add the lampwick channel to your log stack: LOG_STACK=single,lampwick.
What is never sent
Before anything leaves your app, these values are replaced by [redacted]:
- Sensitive names, wherever a name comes with a value: request headers, request payload
fields, URL query and fragment parameters (nested ones too,
user[api_token]), log context and extra, and command options. A name is sensitive when it containspassw,passphrase,secret,token,apikey,accesskey,privatekey,auth(notauthor),credential,cookie,session,sessid,signature,csrf,xsrf,otp,cvv,cvc,cardnumber,iban,ssn,bearer,jwt,assertion,oidc,dsn,databaseurl,connectionstringor a localized "password" (wachtwoord,passwort,motdepasse, ...); when it ends inkey(stripe_key,app_key; notprimary_key,cache_key,idempotency_key,sort_key); or when it ispin,sig,code,key,pw,pwd,pass. Case,-,_and.are ignored. - Secrets in URLs: user info (
https://user:secret@host→https://user:[redacted]@host, a token as user namehttps://ghp_…@github.com→https://[redacted]@github.com), the segment after/reset/,/verify/,/confirm/,/invite/,/token/,/unsubscribe/and similar, and the values of route parameters with sensitive names or namedhashornonce(/reset/{token},/verify/{id}/{hash}) in the request URL and its label. Outgoing request URLs also lose webhook secrets (Telegram/bot<token>/, Slack/services/…, Discord/api/webhooks/<id>/<token>) and any path segment that looks like a random token. - Secrets inside text: in log messages, exception messages and string values in log
context, every URL is redacted the same way; so are the value after
Authorization:orProxy-Authorization:(any scheme), credentials afterBearer,Basic,Token,Bot,DigestorApiKey, JWTs (eyJ….….…), and the value of a sensitive name inname=value,name: value,password:value, JSON ("password":"…") and PHP arrays ('password' => '…',[password] => …). - Command lines: the values of sensitive options (
--password=…,--token …,--password='a b',-psecret) and ofNAME=valuearguments with a sensitive name, and URLs in any argument. Other arguments are sent as given. - Headers listed in
LAMPWICK_REDACT_HEADERS, on top of the names above. - Payload fields listed in
LAMPWICK_REDACT_PAYLOAD_FIELDS(default_token,password,password_confirmation,current_password,new_password,token,secret,api_key).
The redaction runs through hooks of laravel/nightwatch; with a version that lacks them, the
package turns monitoring off instead of sending unredacted records.
Never sent at all: environment variables and configuration values, request bodies (unless LAMPWICK_CAPTURE_REQUEST_PAYLOAD=true; then only for 500 responses, with the rules above),
response bodies, query bindings (only the SQL with its placeholders), the values of function
arguments in stack traces (only their types), mail bodies and recipient addresses (only their
number), and the project token (it only goes from the agent to Lampwick, over HTTPS).
What is sent as written: SQL text, cache keys, mail subjects, log and exception messages apart
from the URLs in them, and the source lines of your own files around an exception
(LAMPWICK_CAPTURE_EXCEPTION_SOURCE_CODE=false turns them off; files under vendor/, var/,
bootstrap/cache/, storage/ and .env* files are never read). Keep secrets out of those, as
you would for any log. Lampwick's ingest also drops Authorization, Cookie and other
credential headers itself, as a second line of defence.
Run the agent in production
The agent is a long-running process, like a queue worker: run one per server, next to your app,
and let a process manager keep it running. Restart it after deploys so it picks up changed
configuration (php artisan lampwick:agent reads .env and config at start-up).
Laravel Forge
Server → Daemons → New daemon:
- Command:
php artisan lampwick:agent - Directory:
/home/forge/example.com/current(your site's directory) - User:
forge - Stop wait seconds:
15
Supervisor
[program:lampwick-agent] command=php /var/www/example.com/artisan lampwick:agent directory=/var/www/example.com user=www-data autostart=true autorestart=true stopsignal=TERM stopwaitsecs=15 redirect_stderr=true stdout_logfile=/var/log/lampwick-agent.log
sudo supervisorctl reread && sudo supervisorctl update
On SIGTERM (or SIGINT) the agent stops accepting records, sends what is buffered and exits,
within LAMPWICK_AGENT_SHUTDOWN_TIMEOUT seconds (10 by default). Keep stopwaitsecs above that.
Docker and multiple servers
Each server (or container) that runs PHP needs its own agent. Run the agent in the same
container as the app (for example as a second process under Supervisor), so it keeps listening
on 127.0.0.1.
The agent's port takes data from anyone who can connect and nothing on it is secret (the token
hash in a payload is 7 hex characters), so never publish it (ports: in Compose, -p in
docker run), never bind it to a public interface, and don't share it between apps. If the
agent must run in a separate container, put both on an internal network of their own (Compose
internal: true), set LAMPWICK_AGENT_LISTEN to the agent's address there in both, and keep
the port unpublished.
Record deploys
Run lampwick:deploy as the last step of your deploy (Forge deploy script, Envoyer hook, CI):
php artisan lampwick:deploy # version: LAMPWICK_DEPLOY, or the current git commit php artisan lampwick:deploy v1.4.0 --author="$FORGE_DEPLOY_AUTHOR"
Lampwick puts a marker on its charts and ties new errors to the deploy ("first seen in v1.4.0",
"new since last deploy"). It sends the current git commit, the origin remote (to link the
commit) and the app environment along; override them with --commit, --repo and
--environment. It uses the same LAMPWICK_TOKEN and needs no agent. On GitHub Actions you can
use the Lampwick deploy action instead (oosterhoff/lampwick/integrations/github-action@deploy-action-v1).
Configuration
Everything is set with environment variables. To change the defaults in code instead, publish
the config file: php artisan vendor:publish --tag=lampwick-config.
| Variable | Default | |
|---|---|---|
LAMPWICK_TOKEN |
— | Project token. Without it nothing is captured. |
LAMPWICK_URL |
https://app.lampwick.dev |
Lampwick address. Must be https:// (plain http:// only to this machine: localhost, 127.0.0.1, ::1). |
LAMPWICK_ENABLED |
true |
false turns the instrumentation off. |
LAMPWICK_AGENT_LISTEN |
127.0.0.1:2407 |
Address the agent listens on and the app sends to. |
LAMPWICK_AGENT_FLUSH_INTERVAL |
1.0 |
Seconds a record may wait before its batch is sent. |
LAMPWICK_AGENT_BATCH_BYTES |
2097152 |
Send a batch early once it reaches this size. |
LAMPWICK_AGENT_BUFFER_BYTES |
67108864 |
Memory for records while Lampwick is unreachable; the oldest are dropped beyond this. |
LAMPWICK_AGENT_SHUTDOWN_TIMEOUT |
10 |
Seconds to flush on stop. |
LAMPWICK_CA_BUNDLE |
system | PEM file with CA certificates, only needed behind a TLS-intercepting proxy. |
LAMPWICK_SERVER |
hostname | Server name shown in Lampwick. |
LAMPWICK_DEPLOY |
Forge/Vapor/Cloud commit | Deployment identifier. |
LAMPWICK_REQUEST_SAMPLE_RATE |
1.0 |
Share of requests to capture (0.0–1.0). |
LAMPWICK_COMMAND_SAMPLE_RATE |
1.0 |
Same, for artisan commands. |
LAMPWICK_EXCEPTION_SAMPLE_RATE |
1.0 |
Same, for exceptions outside sampled requests. |
LAMPWICK_SCHEDULED_TASK_SAMPLE_RATE |
1.0 |
Same, for scheduled tasks. |
LAMPWICK_IGNORE_QUERIES |
false |
Don't capture database queries. |
LAMPWICK_IGNORE_CACHE_EVENTS |
false |
Don't capture cache hits/misses/writes. |
LAMPWICK_IGNORE_MAIL |
false |
Don't capture mail. |
LAMPWICK_IGNORE_NOTIFICATIONS |
false |
Don't capture notifications. |
LAMPWICK_IGNORE_OUTGOING_REQUESTS |
false |
Don't capture outgoing HTTP requests. |
LAMPWICK_LOG_LEVEL |
LOG_LEVEL |
Lowest log level sent through the lampwick channel. |
LAMPWICK_CAPTURE_EXCEPTION_SOURCE_CODE |
true |
Include source lines around exceptions. |
LAMPWICK_CAPTURE_REQUEST_PAYLOAD |
false |
Include request bodies (redacted fields below). |
LAMPWICK_REDACT_PAYLOAD_FIELDS |
_token,password,password_confirmation,current_password,new_password,token,secret,api_key |
Comma-separated; on top of the rules in What is never sent. |
LAMPWICK_REDACT_HEADERS |
Authorization,Cookie,Proxy-Authorization,X-XSRF-TOKEN,X-CSRF-TOKEN,X-Api-Key,X-Auth-Token |
Comma-separated; on top of the rules in What is never sent. |
The variable names of the underlying instrumentation keep working; LAMPWICK_* values win.
How the agent behaves
- It answers the app as soon as a payload is received, so the app is never slowed down by Lampwick or the network. If the agent is not running, stuck or not reading, the app waits at most about 1 second (the instrumentation's connection timeout plus its timeout) and keeps working; records from that period are lost. After a failure the app stops trying for 10 seconds, so a long command or queue worker loses at most one timeout per 10 seconds.
- One HTTPS request is in flight at a time; records keep their order.
503,429, other5xxand network errors are retried, honouringRetry-After, otherwise with exponential backoff up to 60 seconds. A413splits the batch. Other4xxanswers (for example401for a wrong token) drop the batch and are logged as errors.- When your team has used its monthly events, Lampwick answers
429until the next month or an upgrade. The agent then keeps the newest records buffered (up to its buffer size, oldest dropped first) and tries again every minute; the app itself is not affected. - Payloads whose token does not match
LAMPWICK_TOKENare refused, so two apps on one server can't mix up their data: give each app its ownLAMPWICK_AGENT_LISTENport. - It logs to stdout/stderr: warnings at most once a minute per kind, and a summary every five minutes.
- Certificates are always verified. The token only ever leaves the server over HTTPS.
Troubleshooting
- Nothing shows up: run
php artisan lampwick:test. Check that the agent runs with the same.envas the app and thatLAMPWICK_TOKENis set in both. address already in use: another agent (or another monitoring agent) already listens on that port. Stop it or pick anotherLAMPWICK_AGENT_LISTEN(the app and agent read the same setting).HTTP 401: the token is wrong or the project was deleted; copy it again from the project's settings page.- Config cached: after changing
.env, runphp artisan config:cacheagain and restart the agent.
Development
composer install vendor/bin/phpunit
In the Lampwick monorepo, Composer takes the shared core (lampwick/php-agent, the agent,
client and record contract) from ../php-agent.
The contract tests (tests/Contract) run a small Laravel app (tests/Contract/App: a route,
a command, a queued job and a scheduled task) as real processes — php -S, queue:work,
schedule:run — through a real lampwick:agent to a fake HTTP ingest, and check every record
against the format Lampwick reads. The supported laravel/nightwatch versions are pinned to the
range these tests pass on (composer.json); CI runs them on the lowest and highest version of
that range, and once a week on the newest Nightwatch release, so a release that changes the
record format shows up in CI before the range is widened.
License
MIT