{"id":637,"date":"2025-09-05T11:39:32","date_gmt":"2025-09-05T11:39:32","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=637"},"modified":"2025-09-05T11:39:34","modified_gmt":"2025-09-05T11:39:34","slug":"how-to-log-and-monitor-errors-in-laravel-with-monolog","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-log-and-monitor-errors-in-laravel-with-monolog\/","title":{"rendered":"How to Log and Monitor Errors in Laravel with Monolog"},"content":{"rendered":"\n<p>Error logging is one of the most important parts of maintaining a reliable Laravel application. While simple <code>dd()<\/code> statements can help in development, production apps need structured, configurable, and persistent logs. Laravel uses <strong>Monolog<\/strong> under the hood, which is a powerful logging library that supports multiple channels, handlers, and integrations. In this article, we\u2019ll learn how to configure Monolog, log errors to different destinations, monitor logs in production, and apply best practices for scaling logging in real-world projects.<\/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>Laravel Logging Basics<\/strong><\/h2>\n\n\n\n<p>Laravel provides a unified <code>Log<\/code> facade for writing logs. By default, logs are written to <code>storage\/logs\/laravel.log<\/code>. The logging configuration is located in <code>config\/logging.php<\/code> and defines channels like <code>stack<\/code>, <code>single<\/code>, <code>daily<\/code>, <code>slack<\/code>, and more.<\/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-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::info(<span class=\"hljs-string\">'User visited dashboard.'<\/span>);\nLog::warning(<span class=\"hljs-string\">'Payment processing is slow.'<\/span>);\nLog::error(<span class=\"hljs-string\">'Unable to connect to database.'<\/span>);<\/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>Each log entry has a level (<code>info<\/code>, <code>warning<\/code>, <code>error<\/code>, <code>critical<\/code>, etc.) defined by the <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc5424\">RFC 5424 standard<\/a>. This helps filter logs when monitoring large systems.<\/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>Configuring Log Channels<\/strong><\/h2>\n\n\n\n<p>Channels define where and how logs are stored. Laravel supports multiple channels at once using the <code>stack<\/code> driver. Common drivers include <code>single<\/code> (one file), <code>daily<\/code> (rotating logs), <code>slack<\/code>, <code>syslog<\/code>, and custom Monolog handlers.<\/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\">\/\/ config\/logging.php<\/span>\n<span class=\"hljs-keyword\">return<\/span> &#91;\n    <span class=\"hljs-string\">'default'<\/span> =&gt; env(<span class=\"hljs-string\">'LOG_CHANNEL'<\/span>, <span class=\"hljs-string\">'stack'<\/span>),\n\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>],\n        ],\n\n        <span class=\"hljs-string\">'daily'<\/span> =&gt; &#91;\n            <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'daily'<\/span>,\n            <span class=\"hljs-string\">'path'<\/span> =&gt; storage_path(<span class=\"hljs-string\">'logs\/laravel.log'<\/span>),\n            <span class=\"hljs-string\">'level'<\/span> =&gt; <span class=\"hljs-string\">'debug'<\/span>,\n            <span class=\"hljs-string\">'days'<\/span> =&gt; <span class=\"hljs-number\">14<\/span>,\n        ],\n\n        <span class=\"hljs-string\">'slack'<\/span> =&gt; &#91;\n            <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'slack'<\/span>,\n            <span class=\"hljs-string\">'url'<\/span> =&gt; env(<span class=\"hljs-string\">'LOG_SLACK_WEBHOOK_URL'<\/span>),\n            <span class=\"hljs-string\">'level'<\/span> =&gt; <span class=\"hljs-string\">'critical'<\/span>,\n        ],\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>This configuration sends all logs to daily files and sends <code>critical<\/code> level errors to Slack. This way, developers are alerted instantly for severe issues, while other logs are stored for analysis.<\/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>Custom Monolog Handlers<\/strong><\/h2>\n\n\n\n<p>Sometimes you want to send logs to third-party services (like Logstash, Graylog, or CloudWatch). Laravel lets you define custom Monolog handlers by extending <code>tap<\/code> in <code>logging.php<\/code>.<\/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\"><span class=\"hljs-comment\">\/\/ app\/Logging\/CustomizeFormatter.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Logging<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Monolog<\/span>\\<span class=\"hljs-title\">Formatter<\/span>\\<span class=\"hljs-title\">LineFormatter<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CustomizeFormatter<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__invoke<\/span><span class=\"hljs-params\">($logger)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">foreach<\/span> ($logger-&gt;getHandlers() <span class=\"hljs-keyword\">as<\/span> $handler) {\n            $handler-&gt;setFormatter(<span class=\"hljs-keyword\">new<\/span> LineFormatter(\n                <span class=\"hljs-string\">\"&#91;%datetime%] %channel%.%level_name%: %message% %context% %extra%\\n\"<\/span>\n            ));\n        }\n    }\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>Then register it in <code>config\/logging.php<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-string\">'daily'<\/span> =&gt; &#91;\n    <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'daily'<\/span>,\n    <span class=\"hljs-string\">'path'<\/span> =&gt; storage_path(<span class=\"hljs-string\">'logs\/laravel.log'<\/span>),\n    <span class=\"hljs-string\">'level'<\/span> =&gt; <span class=\"hljs-string\">'debug'<\/span>,\n    <span class=\"hljs-string\">'days'<\/span> =&gt; <span class=\"hljs-number\">14<\/span>,\n    <span class=\"hljs-string\">'tap'<\/span> =&gt; &#91;App\\Logging\\CustomizeFormatter::class],\n],<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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 you to format log messages consistently across all environments, making parsing easier for 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>Context and Structured Logging<\/strong><\/h2>\n\n\n\n<p>Logs become far more useful when you include structured context data, like user IDs, request IDs, or payloads.<\/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\">Log::error(<span class=\"hljs-string\">'Order failed to process.'<\/span>, &#91;\n    <span class=\"hljs-string\">'order_id'<\/span> =&gt; $order-&gt;id,\n    <span class=\"hljs-string\">'user_id'<\/span> =&gt; $order-&gt;user_id,\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>This context can be filtered in monitoring tools, making it easier to trace specific errors and build dashboards.<\/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>Monitoring Logs in Production<\/strong><\/h2>\n\n\n\n<p>For production, logs should be centralized and monitored. Common approaches include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Slack \/ Teams Alerts<\/strong> for critical errors.<\/li>\n\n\n\n<li><strong>Log Aggregators<\/strong> (ELK stack, Graylog, Datadog, Papertrail, Sentry).<\/li>\n\n\n\n<li><strong>Cloud Logging<\/strong> (AWS CloudWatch, GCP Logging).<\/li>\n\n\n\n<li><strong>Custom Dashboards<\/strong> with daily log summaries.<\/li>\n<\/ul>\n\n\n\n<p>By centralizing logs, you can set up alerting, search logs by metadata, and ensure compliance for audits.<\/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 Logging<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use <strong>appropriate log levels<\/strong> (<code>info<\/code> for events, <code>warning<\/code> for recoverable issues, <code>error<\/code> for failures).<\/li>\n\n\n\n<li><strong>Rotate logs<\/strong> with the <code>daily<\/code> driver to avoid large files.<\/li>\n\n\n\n<li><strong>Don\u2019t log sensitive data<\/strong> (passwords, credit card numbers).<\/li>\n\n\n\n<li>Include <strong>context<\/strong> like user ID, request ID, or environment.<\/li>\n\n\n\n<li>Integrate with <strong>alerting tools<\/strong> for critical errors.<\/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>Monolog vs Telescope vs Ray<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Tool<\/th><th>Best For<\/th><th>Environment<\/th><th>Output<\/th><\/tr><\/thead><tbody><tr><td>Monolog<\/td><td>Persistent error logging<\/td><td>Local + Production<\/td><td>Files, Slack, Syslog, Cloud<\/td><\/tr><tr><td>Telescope<\/td><td>Monitoring requests, jobs, queries<\/td><td>Local + Staging<\/td><td>Web dashboard<\/td><\/tr><tr><td>Ray<\/td><td>Quick, interactive debugging<\/td><td>Local Development<\/td><td>Desktop app<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Monolog is your backbone for logging across environments. Ray and Telescope are great companions for debugging and monitoring, but Monolog handles the long-term storage and integration with external systems.<\/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>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>Laravel\u2019s Monolog integration makes logging flexible and production-ready. We covered basic logging, channel configuration, custom handlers, structured context, monitoring, and best practices. Combined with Ray and Telescope, Monolog gives you full visibility into your app across local development and production.<\/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 learning about monitoring and optimization with these articles:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\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\/debugging-laravel-applications-with-ray-and-telescope\">Debugging Laravel Applications with Ray and Telescope<\/a><\/li>\n\n\n\n<li><a href=\"\/blog\/query-performance-tuning-in-laravel-mysql\">Query Performance Tuning in Laravel + MySQL<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Error logging is one of the most important parts of maintaining a reliable Laravel application. While simple dd() statements can help in development, production apps need structured, configurable, and persistent logs. Laravel uses Monolog under the hood, which is a powerful logging library that supports multiple channels, handlers, and integrations. In this article, we\u2019ll learn [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":641,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[72,149,150,44],"class_list":["post-637","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\/637","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=637"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/637\/revisions"}],"predecessor-version":[{"id":640,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/637\/revisions\/640"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/641"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=637"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=637"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=637"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}