Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Log Cloudwatch Laravel Package

sinemacula/laravel-log-cloudwatch

A Laravel logging driver that sends application logs directly to AWS CloudWatch. Integrate CloudWatch Logs with Laravel’s logging stack to centralize, monitor, and retain logs in AWS with simple configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the driver

    composer require sinemacula/laravel-log-cloudwatch
    

    As of v1.1.0 the package declares support for Laravel 13 (added in #22). If you are upgrading an existing app to Laravel 13, bump this package at the same time — older 1.0.x constraints may block composer update against laravel/framework:^13.0. Confirm the resolved version with composer show sinemacula/laravel-log-cloudwatch.

  2. Provide AWS credentials. The driver uses the AWS SDK for PHP, so the standard resolution chain applies: environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, optional AWS_SESSION_TOKEN), a shared credentials file, or an instance/task role. On EC2, ECS, or Lambda, prefer the role — do not hardcode keys in config/logging.php.

  3. Register a channel in config/logging.php:

    'cloudwatch' => [
        'driver' => 'cloudwatch',
        'region' => env('AWS_DEFAULT_REGION'),
        'log_group' => env('CLOUDWATCH_LOG_GROUP', '/app/' . env('APP_NAME')),
        'log_stream' => env('CLOUDWATCH_LOG_STREAM', gethostname()),
        // credentials, retention, batch/tap options per package README
    ],
    

    The exact key names are defined in the package's config file — publish it (php artisan vendor:publish) and treat that as the source of truth rather than copying snippets from blog posts.

  4. First use case: send a single low-risk event to prove the whole chain works before wiring it into the exception handler.

    Log::channel('cloudwatch')->info('cloudwatch smoke test', ['user_id' => 1]);
    

    Then confirm the log group, stream, and event actually appear in the CloudWatch console. A green composer require tells you nothing about IAM.

Implementation Patterns

Keep it as a named channel, not the default driver. The pragmatic production setup is stack → daily (local disk) + cloudwatch, so you retain a machine-local copy for fast grepping and survive an AWS outage:

'stack' => [
    'driver' => 'stack',
    'channels' => ['daily', 'cloudwatch'],
    'ignore_exceptions' => false,
],

Environment-specific group and stream naming. Derive the group from the app and environment, and the stream from the host/container plus a date or build identifier. This is what makes CloudWatch usable later — you can filter to a single deploy or a single pod without scanning everything.

Prefer roles over keys in every deployed environment. Wire AWS_* env vars only for local debugging. In ECS/Lambda the SDK picks up credentials automatically and you avoid rotating secrets in your config.

Log structured context, not strings. CloudWatch Logs Insights can only query JSON you actually sent. Pass arrays as the context argument (Log::channel('cloudwatch')->error($msg, $context)) and keep messages stable so you can group by message and filter on context.order_id. If the package's formatter lets you choose the output shape, use JSON rather than the default line format — it is the difference between a searchable log and an expensive text dump.

Route exceptions deliberately. In bootstrap/app.php (Laravel 11+) or App\Exceptions\Handler, call report() on the cloudwatch channel for exceptions only, not for every debug line. You get durable alerting without shipping your entire debug firehose to a metered service.

Use CloudWatch Logs Insights as the query layer. Once logs are there, a saved query per incident type (5xx by route, slow jobs by duration, auth failures by IP) is more valuable than any local tail -f. Treat the saved queries as part of the integration, not an afterthought.

Gotchas and Tips

  • IAM is the most common failure, and it fails quietly. You need at minimum logs:CreateLogGroup, logs:CreateLogStream, logs:DescribeLogStreams, and logs:PutLogEvents — plus logs:PutRetentionPolicy if the package manages retention. A missing DescribeLogStreams often surfaces as an apparently empty group rather than an error, so verify in the console, not just in laravel.log.
  • The driver swallows its own errors. Logging drivers typically fail silently to avoid recursive logging. If nothing appears in CloudWatch, test the credentials and permissions directly (e.g. a small SDK call or aws logs describe-log-groups) rather than debugging through the logging stack.
  • Buffering means loss on hard kill. If the channel batches events, a container OOM, SIGKILL, or an abrupt deploy can drop the tail of your logs — exactly the lines you need. Keep the second channel in the stack, and don't tune batching down to zero-and-only-cloudwatch for anything critical.
  • Cost scales with verbosity, not with app size. Debug-level logging shipped to CloudWatch is the classic surprise bill. Set LOG_LEVEL=warning for the cloudwatch channel specifically (per-channel levels via the stack's channel config) while keeping debug on the local file channel.
  • Set retention on the group, or it grows forever. CloudWatch defaults to "never expire". Apply a retention policy — via the package if supported, otherwise once with aws logs put-retention-policy — and treat it as required infrastructure, not a tuning knob.
  • Stream naming collisions. Fixed stream names across multiple containers cause interleaved events and rejected PutLogEvents calls due to sequence-token conflicts. Include the hostname, container ID, or task ID in the stream name.
  • Region and endpoint overrides. LocalStack and VPC endpoints need an explicit endpoint override; the SDK does not infer it from AWS_DEFAULT_REGION alone. Keep that override in a non-production env branch so it never leaks into production config.
  • Laravel 13 upgrade checklist (v1.1.0): update the package together with the framework, re-publish and diff the config file (new framework versions sometimes shift defaults), and re-run your smoke test — the compatibility bump is a framework-support change, not a behavioural one, so any change you observe after upgrading is more likely your own config drift than the driver.
  • Extension points: the config file is the supported surface. Where the package exposes a custom formatter or handler, prefer composing it over forking the driver — a fork will not receive the next Laravel compatibility bump, and that is precisely what v1.1.0 was.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect