tarfin-labs/laravel-cloudwatch-logger
Send Laravel application logs directly to AWS CloudWatch Logs with this ready-to-use logging driver. Configure a log group and stream, then let Laravel’s logging system ship messages to AWS for centralized monitoring and retention.
composer require tarfin-labs/laravel-cloudwatch-logger. On v2.7.0 the dependency set targets Laravel 12 / PHP 8.4, so if you're still on Laravel 10/11 or PHP 8.2–8.3, first run composer why-not tarfin-labs/laravel-cloudwatch-logger ^2.7 — if it's blocked by aws/aws-sdk-php or framework constraints, stay on ^2.6 until you upgrade.php artisan vendor:publish --tag=cloudwatch-logger or --provider="TarfinLabs\CloudwatchLogger\CloudwatchLoggerServiceProvider") and read the published file once before wiring anything — group name, stream name, region, retention and batch/flush settings all live there and are the source of truth for your version.AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY in .env for local experiments — never in a repo that also runs locally.config/logging.php as a custom driver pointing at the package's logger factory, e.g. 'cloudwatch' => ['driver' => 'custom', 'via' => \TarfinLabs\CloudwatchLogger\CloudwatchLoggerFactory::class].LOG_CHANNEL globally. Add a stack channel ('production' => ['driver' => 'stack', 'channels' => ['single', 'cloudwatch']]) so you get local files and CloudWatch, then verify with LOG_CHANNEL=cloudwatch php artisan tinker → Log::info('hello from tinker') and check the stream in the console (allow a few seconds for ingestion).single/daily as the default locally and only stack CloudWatch in staging/production. The cleanest version is a dedicated production stack selected via LOG_STACK in .env, so nothing in code branches on App::environment().app, audit, jobs, payments. CloudWatch retention, IAM scoping and metric filters are all per-group, so mixing them forces you to over-grant and over-retain.{app}-{env}-{date} or {app}-{env}-{hostname} avoids the classic "all FPM workers hammer one stream" problem and keeps streams from growing forever. Set the group retention in config (e.g. 14/30 days) rather than relying on infinite retention defaults.octane, and scheduler:work, records sit in the handler's buffer until the batch threshold or shutdown. If you deploy long-lived workers, confirm entries actually arrive; call the framework's shutdown/close path (or Log::close() where appropriate) before a worker sleeps or restarts.Log::error('Payment failed', ['order_id' => $id, 'gateway' => $gw]) keeps messages greppable and makes CloudWatch Logs Insights queries (fields @message | filter @message like /order_id/) actually usable.logs:CreateLogStream + logs:PutLogEvents. Letting the app create groups makes deploys order-dependent and quietly expands IAM permissions.ERROR counts per group; wire that up in the same PR as the logging change, otherwise you've only moved your log files.LOG_CHANNEL=null (or single) in phpunit.xml and never let the suite talk to AWS. If you must assert on emitted logs, bind a fake/memory handler instead of the CloudWatch channel.aws/aws-sdk-php in your lockfile, so run the suite and deploy to staging before production, and commit composer.lock. If you pin the SDK with a tight constraint or a fork, relax it first or composer update will fail to resolve.logs:DescribeLogStreams or logs:CreateLogStream typically surfaces as an exception on flush (often at end of request, so it looks like a random 500) rather than at boot. Test with a fresh role, not your admin credentials.ResourceNotFoundException for a second or two. Transient errors on the very first request after provisioning are normal; persistent ones are permissions or wrong region.exit, and container SIGKILL bypass the flush. If an entry must survive, keep the local single channel in the stack and/or ship stderr to the platform's log driver as a backstop.InvalidSequenceTokenException-style errors, that's the signal.debug is expensive. Set the channel's minimum level to warning (or notice) in production and keep debug for local only. Also remember CloudWatch Logs has per-request overhead — very chatty logging gets slow because the flush is a synchronous SDK call inside the request; set sane connect/request timeouts so CloudWatch latency never becomes your latency.AWS_DEFAULT_REGION and any cached config (php artisan config:clear) after env changes.composer update stays painless across future minor bumps.How can I help you explore Laravel packages today?