mybeast / logs-laravel
Laravel SDK for My Beast Logs (services.my-beast.com/logs): a log channel that sends your app's lines, scrubbed, without slowing a request or losing a line.
Requires
- php: ^8.2
- illuminate/console: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/log: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
- monolog/monolog: ^3.0
- symfony/uid: ^7.0 || ^8.0
Requires (Dev)
- justinrainbow/json-schema: ^6.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0 || ^11.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
The Laravel SDK for My Beast logs: a log channel that sends your app's lines to the service - scrubbed before they leave, without slowing a request, and without losing any while the service is away. PHP ≥ 8.2, Laravel 12 and 13.
Plug it in
composer require mybeast/logs-laravel
LOG_STACK=single,mybeast MYBEAST_LOGS_URL=https://services.my-beast.com/logs MYBEAST_LOGS_SECRET=sk_….… MYBEAST_LOGS_ENVIRONMENT=production # one of the project's environments: development, staging, production
Where the values come from. id makes the server secret with the project, and Secrets keeps the URL and secret
lines, filled in, in each of the project's environments: Secrets → the environment → Download .env is the whole block,
with the other products' lines. A secret made by hand is on the project's Settings → Keys
(services.my-beast.com/<workspace>/<project>/settings/keys), shown once, when made.
The environment is where this copy of the app runs, so each copy sets its own and Secrets doesn't hold it. Left
out, it is APP_ENV: local on a developer's machine, a name the project doesn't list, so those lines show apart
from its environments. Set development there.
That is all: the SDK adds the mybeast channel (unless config/logging.php names one), and the scheduler sends
what waits every 10 seconds (schedule:run from cron, or schedule:work). In your test suite, set
MYBEAST_LOGS_ENABLED=false (phpunit.xml) - off, the channel takes lines and keeps none.
A channel of its own name: 'payments' => ['driver' => 'mybeast', 'name' => 'payments'], then
Log::channel('payments')->error(…) arrives as channel payments. A channel's level is its mildest level
(MYBEAST_LOGS_LEVEL for the default one).
How a line travels
- Written to a spool on this server (
storage/app/private/mybeast-logs): one append to a journal file - no network, no database. A request waits for nothing else. - Sent from there: right after the response has gone out (PHP-FPM has answered), at most every 2 seconds per
server and never while the service is failing; after each queue job; and every
ship_every_secondsby the scheduler (mybeast:logs-ship). Batches of 500, gzip, oldest first. A batch leaves the spool only once the service said it has it. - While the service is away the lines wait, in order, and go when it is back - none lost, none twice (each line
carries an id the service stores once). The spool may grow to
spool_max_mb(100 MB); past that new lines are not kept until there is room, and a line then says from when to when.mybeast:logs-statusshows what waits. - Past the project's monthly limit (its package's) the service refuses the rest of the month's lines as
monthly-limit. They are dropped, and the log says so once a month, and once when the service takes lines again.
What is taken out
Before anything leaves: e-mail addresses, IBANs, Finnish personal identity codes, phone numbers, bearer tokens and long
secrets, in the message and in every string of the context; a value under a secret-looking key (password, token,
secret, api_key, authorization, cookie, card_number, …) goes whole; a model in the context is its class and
key only, never its attributes; an exception is its class, message, place and trace. The service scrubs again on
arrival. Your own rules:
// config/mybeast-logs.php (php artisan vendor:publish --tag=mybeast-logs-config) 'scrub' => ['/\bCUST-\d{6}\b/' => '[customer]'], // or in a service provider, for what a pattern cannot say Logs::scrubUsing(fn (string $text): string => PersonalIdentityCode::maskIn($text));
What each line carries
The environment, release (MYBEAST_LOGS_RELEASE, else the git commit), host, channel, and the request id: the
same on every line of one HTTP request (an incoming X-Request-Id is kept when it fits, else one is made, and it is put
on the response), on the jobs it queues (through Laravel's hidden Context), and on each artisan command run. Also what
was running, without values: GET /invoices/{invoice}/send, job App\Jobs\SendInvoice, artisan invoices:remind.
Logs::requestId() hands the id to whatever else wants it (the errors SDK will).
Working on this SDK
For whoever changes the SDK, in its repository: the Composer download leaves workbench/ and the tests out, and the
schema check needs My Beast's own checkout beside it.
The demo host
workbench/ is a tiny Laravel app with this SDK (Orchestra Testbench): a checkout that logs - personal data
included - and queues a job.
cp workbench/.env.example workbench/.env # put a project's server secret in MYBEAST_LOGS_SECRET composer demo # 40 checkouts through the app's HTTP kernel, timed vendor/bin/testbench mybeast:logs-status # what waits in the demo's spool vendor/bin/testbench mybeast:logs-ship # send it now
Develop
composer install vendor/bin/phpunit vendor/bin/pint
schemas/ is a copy of the Logs module's contract in services (../../services/schemas/logs): a test fails when they
differ.
Releasing
Add the version to CHANGELOG.md, then tag it: git tag v0.2.0 && git push --tags. Packagist reads the tag through
its GitHub hook; there is no version in composer.json. The Tests workflow runs on the tag as on every push: PHP 8.2
to 8.4 against Laravel 12 and 13 (Laravel 11 has unfixed advisories, so Composer will not install it).