{"id":642,"date":"2025-09-05T11:42:16","date_gmt":"2025-09-05T11:42:16","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=642"},"modified":"2025-09-05T11:42:18","modified_gmt":"2025-09-05T11:42:18","slug":"advanced-logging-and-monitoring-in-laravel-with-monolog","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/advanced-logging-and-monitoring-in-laravel-with-monolog\/","title":{"rendered":"Advanced Logging and Monitoring in Laravel with Monolog"},"content":{"rendered":"\n<p>In the <a href=\"https:\/\/1v0.net\/blog\/how-to-log-and-monitor-errors-in-laravel-with-monolog\/\" data-type=\"post\" data-id=\"637\">previous article<\/a>, we covered the basics of logging with Monolog in Laravel. Now, let\u2019s go deeper into advanced logging and monitoring strategies. In large-scale projects, simply writing logs to files is not enough. You need structured logs, log rotation, external storage, and integration with observability platforms. In this article, we\u2019ll configure Monolog for production-ready logging, integrate with services like AWS CloudWatch, explore asynchronous logging, and apply best practices for scaling.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Advanced Log Channels<\/strong><\/h2>\n\n\n\n<p>Laravel supports multiple channels combined in a stack. You can mix <code>daily<\/code> logs with cloud-based services and instant alerts. Let\u2019s look at a custom <code>stack<\/code> channel that combines multiple destinations:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ config\/logging.php<\/span>\n<span class=\"hljs-string\">'channels'<\/span> =&gt; &#91;\n    <span class=\"hljs-string\">'stack'<\/span> =&gt; &#91;\n        <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'stack'<\/span>,\n        <span class=\"hljs-string\">'channels'<\/span> =&gt; &#91;<span class=\"hljs-string\">'daily'<\/span>, <span class=\"hljs-string\">'slack'<\/span>, <span class=\"hljs-string\">'cloudwatch'<\/span>],\n        <span class=\"hljs-string\">'ignore_exceptions'<\/span> =&gt; <span class=\"hljs-keyword\">false<\/span>,\n    ],\n\n    <span class=\"hljs-string\">'cloudwatch'<\/span> =&gt; &#91;\n        <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'monolog'<\/span>,\n        <span class=\"hljs-string\">'handler'<\/span> =&gt; Monolog\\Handler\\StreamHandler::class,\n        <span class=\"hljs-string\">'with'<\/span> =&gt; &#91;\n            <span class=\"hljs-string\">'stream'<\/span> =&gt; <span class=\"hljs-string\">'php:\/\/stdout'<\/span>,\n        ],\n        <span class=\"hljs-string\">'formatter'<\/span> =&gt; Monolog\\Formatter\\JsonFormatter::class,\n    ],\n],<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This setup logs daily to disk, sends critical alerts to Slack, and streams JSON logs to AWS CloudWatch. The JSON format is preferred for production because it integrates better with log aggregators.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Asynchronous Logging with Queues<\/strong><\/h2>\n\n\n\n<p>Heavy logging can slow down requests. To reduce impact, send logs asynchronously using Laravel\u2019s queue system. Here\u2019s how you can wrap logging inside a job:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Jobs\/AsyncLog.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Jobs<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Log<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncLog<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Job<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> $message;\n    <span class=\"hljs-keyword\">public<\/span> $level;\n    <span class=\"hljs-keyword\">public<\/span> $context;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span><span class=\"hljs-params\">($message, $level = <span class=\"hljs-string\">'info'<\/span>, $context = &#91;])<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;message = $message;\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;level = $level;\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;context = $context;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        Log::{<span class=\"hljs-keyword\">$this<\/span>-&gt;level}(<span class=\"hljs-keyword\">$this<\/span>-&gt;message, <span class=\"hljs-keyword\">$this<\/span>-&gt;context);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Dispatch logs asynchronously:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">dispatch(<span class=\"hljs-keyword\">new<\/span> \\App\\Jobs\\AsyncLog(\n    <span class=\"hljs-string\">'Payment gateway timeout'<\/span>,\n    <span class=\"hljs-string\">'error'<\/span>,\n    &#91;<span class=\"hljs-string\">'order_id'<\/span> =&gt; <span class=\"hljs-number\">1234<\/span>]\n));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This allows logs to be processed in the background, keeping API responses fast while still persisting errors for monitoring.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Integrating with AWS CloudWatch<\/strong><\/h2>\n\n\n\n<p>For apps hosted on AWS, it\u2019s best to send logs directly to <strong>CloudWatch<\/strong>. This provides centralized storage, metrics, and alerts. Install the CloudWatch Monolog driver:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">composer require maxbanton\/cwh<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then configure the channel:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ config\/logging.php<\/span>\n<span class=\"hljs-string\">'cloudwatch'<\/span> =&gt; &#91;\n    <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'monolog'<\/span>,\n    <span class=\"hljs-string\">'handler'<\/span> =&gt; Maxbanton\\Cwh\\Handler\\CloudWatch::class,\n    <span class=\"hljs-string\">'with'<\/span> =&gt; &#91;\n        <span class=\"hljs-string\">'group'<\/span> =&gt; env(<span class=\"hljs-string\">'CLOUDWATCH_GROUP'<\/span>, <span class=\"hljs-string\">'laravel-app'<\/span>),\n        <span class=\"hljs-string\">'stream'<\/span> =&gt; env(<span class=\"hljs-string\">'CLOUDWATCH_STREAM'<\/span>, <span class=\"hljs-string\">'production'<\/span>),\n        <span class=\"hljs-string\">'retention'<\/span> =&gt; <span class=\"hljs-number\">14<\/span>,\n    ],\n],<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now all logs will appear in the CloudWatch console where you can create alarms for error spikes or unusual activity.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Centralized Log Aggregation<\/strong><\/h2>\n\n\n\n<p>In microservices or multi-server environments, centralizing logs is essential. Common solutions include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ELK Stack (Elasticsearch, Logstash, Kibana)<\/strong><\/li>\n\n\n\n<li><strong>Graylog<\/strong><\/li>\n\n\n\n<li><strong>Datadog<\/strong><\/li>\n\n\n\n<li><strong>Sentry<\/strong> (error tracking with stack traces)<\/li>\n<\/ul>\n\n\n\n<p>These tools provide real-time dashboards, error grouping, and search functionality. By shipping Monolog logs to them, you get observability across your entire infrastructure.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Structured JSON Logging<\/strong><\/h2>\n\n\n\n<p>Plain text logs are difficult to parse at scale. JSON logging makes logs machine-readable and easy to filter in aggregators.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Log<\/span>;\n\nLog::error(<span class=\"hljs-string\">'Order processing failed'<\/span>, &#91;\n    <span class=\"hljs-string\">'order_id'<\/span> =&gt; <span class=\"hljs-number\">456<\/span>,\n    <span class=\"hljs-string\">'user_id'<\/span> =&gt; <span class=\"hljs-number\">789<\/span>,\n    <span class=\"hljs-string\">'gateway'<\/span> =&gt; <span class=\"hljs-string\">'stripe'<\/span>,\n]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With JSON format, these fields become structured attributes in Elasticsearch or CloudWatch, making it possible to query by <code>order_id<\/code> or <code>user_id<\/code>.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Best Practices for Scalable Logging<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use <strong>JSON logging<\/strong> for production.<\/li>\n\n\n\n<li><strong>Send logs asynchronously<\/strong> for performance.<\/li>\n\n\n\n<li>Centralize logs in a <strong>log aggregator<\/strong> for search and alerting.<\/li>\n\n\n\n<li>Rotate logs to avoid disk overflows (<code>daily<\/code> driver).<\/li>\n\n\n\n<li>Mask sensitive data like passwords and tokens.<\/li>\n\n\n\n<li>Tag logs with environment (<code>staging<\/code>, <code>production<\/code>) for filtering.<\/li>\n<\/ul>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>Advanced logging with Monolog ensures that your Laravel applications are production-ready. By combining multiple channels, sending logs asynchronously, integrating with cloud services, and centralizing logs in aggregators, you gain full visibility into your system. Following these practices makes debugging faster, improves observability, and supports compliance requirements.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<p>Continue exploring Laravel error handling and performance monitoring:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/how-to-log-and-monitor-errors-in-laravel-with-monolog\">How to Log and Monitor Errors in Laravel with Monolog<\/a><\/li>\n\n\n\n<li><a href=\"\/blog\/using-laravel-telescope-to-debug-performance-issues\">Using Laravel Telescope to Debug Performance Issues<\/a><\/li>\n\n\n\n<li><a href=\"\/blog\/optimizing-laravel-for-high-concurrency-with-octane\">Optimizing Laravel for High Concurrency with Octane<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In the previous article, we covered the basics of logging with Monolog in Laravel. Now, let\u2019s go deeper into advanced logging and monitoring strategies. In large-scale projects, simply writing logs to files is not enough. You need structured logs, log rotation, external storage, and integration with observability platforms. In this article, we\u2019ll configure Monolog for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":644,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[72,149,150,44],"class_list":["post-642","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-debugging","tag-monitoring","tag-monolog","tag-performance"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/642","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=642"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/642\/revisions"}],"predecessor-version":[{"id":645,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/642\/revisions\/645"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/644"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=642"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=642"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=642"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}