{"id":77068,"date":"2025-12-19T10:25:18","date_gmt":"2025-12-19T04:55:18","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=77068"},"modified":"2026-01-06T15:50:12","modified_gmt":"2026-01-06T10:20:12","slug":"extending-and-improving-drupals-logging-system-with-monolog","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/extending-and-improving-drupals-logging-system-with-monolog\/","title":{"rendered":"Extending and Improving Drupal\u2019s Logging System with Monolog"},"content":{"rendered":"<p>Effective logging sits at the heart of building and operating dependable web applications. It gives engineering teams the visibility they need to understand runtime behavior, investigate errors, maintain audit trails, and act decisively when issues arise in live environments. Drupal ships with two native logging options: <strong>Database Logging (dblog)<\/strong> and <strong>Syslog<\/strong>. In the dblog approach, messages are written directly into database tables, whereas Syslog redirects those messages to the underlying operating system\u2019s logging facility, avoiding database persistence altogether.<\/p>\n<p>Although dblog is convenient during development and performs adequately on smaller or low-usage sites, its reliance on constant database writes can create performance strain as traffic and complexity increase. <strong>Syslog<\/strong> mitigates this concern by shifting logging responsibilities to the system level, making it a more practical choice for cloud-hosted or server-based deployments where centralized log access and quicker diagnostics are essential.<\/p>\n<p>For Drupal applications operating at scale, especially in high-traffic or enterprise environments, <strong>Monolog<\/strong> stands out as the preferred logging solution. It introduces a highly configurable and extensible logging layer that supports multiple channels, handlers, and output formats. <strong>Monolog<\/strong> enables logs to be streamed to files, external services, or modern observability platforms, while also supporting structured formats like JSON, severity-based filtering, and environment-specific setups. This approach not only reduces runtime overhead but also delivers deeper operational insight and long-term scalability for production systems.<\/p>\n<h2>Difference Between dblog, Syslog, and Monolog in Drupal<\/h2>\n<table style=\"height: 164px; width: 100%; border-collapse: collapse; border-style: solid; font-size: 16px;\">\n<tbody>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 24px;\"><strong>\u00a0Feature \/ Aspect<\/strong><\/td>\n<td style=\"width: 25%; height: 24px;\"><strong>\u00a0dblog (Database Logging)<\/strong><\/td>\n<td style=\"width: 25%; height: 24px;\"><strong>\u00a0Syslog<\/strong><\/td>\n<td style=\"width: 25%; height: 24px;\"><strong>\u00a0Monolog<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Type<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Core Module<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Core Module<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Contributed Module<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Storage Location<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Database tables (watchdog)<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0System log (Linux syslog)<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Files, stdout, syslog, cloud services<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Installation<br \/>\n<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Enabled by default<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Enable manually<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Install via Composer<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Performance<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Slower (DB writes)<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Better than dblog<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0High performance<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Database Load<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0High<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0None<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0None<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Log Rotation<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0No<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0OS-level<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Built-in (RotatingFileHandler)<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Scalability<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Poor for large sites<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Limited<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Excellent<\/td>\n<\/tr>\n<tr style=\"height: 48px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Log Levels<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Basic (Notice, Warning, Error)<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Basic<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Full (DEBUG, INFO, WARNING, ERROR, CRITICAL)<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Multiple Outputs<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not allowed<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not allowed<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Yes (file + stdout + cloud)<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Container Support<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not suitable<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Limited<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Perfect for Docker\/K8s<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Centralized Logging<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not supported<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0OS-dependent<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0ELK, Graylog, New Relic<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Alerting (Slack\/Email)<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not allowed<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Not allowed<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Yes allowed<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Ease of Debugging<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Basic UI<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0OS access needed<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Advanced &amp; flexible<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 25%; height: 10px;\"><strong>\u00a0Best Use Case<\/strong><\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Small \/ local sites<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Traditional Linux servers<\/td>\n<td style=\"width: 25%; height: 10px;\">\u00a0Modern, cloud, enterprise apps<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>What is Monolog?<\/h2>\n<p><strong>Monolog<\/strong> is a popular and powerful PHP logging library used by many frameworks, including Symfony (which Drupal is built on). It provides advanced logging capabilities and supports multiple output destinations, known as <strong>handlers<\/strong>.<\/p>\n<p>With Monolog, logs can be sent to:<\/p>\n<ul>\n<li>Files (with rotation)<\/li>\n<li>Stdout \/ stderr (ideal for Docker &amp; Lando)<\/li>\n<li>Syslog<\/li>\n<li>Cloud logging services<\/li>\n<li>Centralized systems like:\n<ul>\n<li>Graylog<\/li>\n<li>Logstash \/ ELK Stack<\/li>\n<li>Papertrail<\/li>\n<li>New Relic<\/li>\n<\/ul>\n<\/li>\n<li>Notifications (Slack, Email, Webhooks)<\/li>\n<\/ul>\n<p>Drupal integrates Monolog through the contributed <strong>Monolog module<\/strong>, allowing you to completely extend and replace the default logging system.<\/p>\n<h2>Why Use Monolog in Drupal?<\/h2>\n<p>Using Monolog in Drupal provides several benefits:<\/p>\n<ul>\n<li><strong>Better performance<\/strong> than database logging<\/li>\n<li><strong>Centralized log management<\/strong> for distributed systems<\/li>\n<li><strong>Multiple handlers<\/strong> (log to file + terminal + external service)<\/li>\n<li><strong>Log rotation<\/strong> to prevent disk overuse<\/li>\n<li><strong>Severity\u2011based logging<\/strong> (DEBUG, INFO, WARNING, ERROR, CRITICAL)<\/li>\n<li><strong>Perfect for Docker \/ Lando \/ Kubernetes environments<\/strong><\/li>\n<\/ul>\n<p>In production environments, logging to stdout or external services is considered a best practice.<\/p>\n<h2>Installing Monolog in Drupal<\/h2>\n<h3>Step 1: Install via Composer<\/h3>\n<pre><strong>composer require drupal\/monolog<\/strong><\/pre>\n<h3>Step 2: Enable the Module<\/h3>\n<pre><strong>drush en monolog -y<\/strong><\/pre>\n<h3>Step 3: Disable Database Logging (Optional but Recommended)<\/h3>\n<pre><strong>drush pmu dblog -y<\/strong><\/pre>\n<p>This step is optional, but highly recommended for production to reduce database overhead.<\/p>\n<h2>Basic Monolog Configuration in Drupal<\/h2>\n<p>Drupal allows you to define Monolog handlers using Symfony service definitions.<\/p>\n<h3>Step 4: Create a Site\u2011Specific Service File<\/h3>\n<p>Create a file named:<\/p>\n<pre><strong>sites\/default\/monolog.services.yml<\/strong><\/pre>\n<p>This file will define where and how logs are written.<\/p>\n<h3>Example 1: Output Logs to Terminal (Stdout)<\/h3>\n<p>This setup is ideal for <strong>Docker, Lando, and Kubernetes<\/strong>, where logs are collected from stdout.<\/p>\n<pre><strong>parameters:\r\n monolog.channel_handlers:\r\n  default: ['stdout']\r\n\r\nservices:\r\n monolog.handler.stdout:\r\n  class: Monolog\\Handler\\StreamHandler\r\n  arguments: ['php:\/\/stdout', 'DEBUG']\r\n  tags:\r\n   - { name: monolog.handler, channel: default }\r\n<\/strong><\/pre>\n<h3>Example 2: Output Logs to a Rotating File<\/h3>\n<p>Logs will be stored in:<br \/>\n<strong>public:\/\/logs\/debug.log<\/strong><br \/>\n<strong>\u2192 sites\/default\/files\/logs\/debug.log<\/strong><\/p>\n<pre><strong>parameters:\r\n monolog.channel_handlers:\r\n  default: ['rotating_file']\r\n\r\nservices:\r\n monolog.handler.rotating_file:\r\n  class: Monolog\\Handler\\RotatingFileHandler\r\n  arguments: ['public:\/\/logs\/debug.log', 10, 'DEBUG']\r\n  tags:\r\n   - { name: monolog.handler, channel: default }\r\n<\/strong><\/pre>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><strong>10<\/strong> \u2192 Keeps the last 10 rotated log files<\/li>\n<li><strong>DEBUG<\/strong> \u2192 Minimum log level<\/li>\n<li>Make sure the <strong>logs<\/strong> directory exists and is writable.<\/li>\n<\/ul>\n<h3>Example 3: Output Logs to Both Terminal and File<\/h3>\n<pre><strong>parameters:\r\n monolog.channel_handlers:\r\n  default: ['stdout', 'rotating_file']\r\n\r\nservices:\r\n monolog.handler.stdout:\r\n class: Monolog\\Handler\\StreamHandler\r\n arguments: ['php:\/\/stdout', 'DEBUG']\r\n tags:\r\n  - { name: monolog.handler, channel: default }\r\n\r\n monolog.handler.rotating_file:\r\n class: Monolog\\Handler\\RotatingFileHandler\r\n arguments: ['public:\/\/logs\/debug.log', 10, 'DEBUG']\r\n tags:\r\n  - { name: monolog.handler, channel: default }\r\n<\/strong><\/pre>\n<p>This is a very common setup for local and staging environments.<\/p>\n<h3>Writing Drupal Monolog Logs to S3 \/ Azure Blob<\/h3>\n<p><strong>Recommended Approach:\u00a0<\/strong>Monolog logs in Drupal can be stored in Amazon S3 or Azure Blob Storage, but they should not be written there directly from the application layer. Instead, follow this two-step, production-ready approach:<\/p>\n<ul>\n<li><strong>Write logs locally or to STDOUT:<\/strong> Configure Monolog to log into local files or STDOUT. This keeps logging fast, reliable, and independent of network latency.<\/li>\n<li><strong>Forward logs to S3 \/ Azure Blob asynchronously: <\/strong>Use infrastructure-level tools such as:\n<ul>\n<li>AWS CloudWatch \u2192 S3<\/li>\n<li>Azure Monitor \/ Log Analytics \u2192 Azure Blob<\/li>\n<li>Cron-based batch uploads or log shippers (Fluent Bit, Filebeat)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>This approach ensures <strong>better performance, lower cost, and higher reliability<\/strong>, while still centralizing logs in S3 or Azure Blob for long-term storage and analysis.<\/p>\n<h3>Step 5: Tell Drupal to Load the Monolog Services File<\/h3>\n<p>Add the following line to your <strong>settings.php<\/strong> file:<\/p>\n<pre><strong>$settings['container_yamls'][] = 'sites\/default\/monolog.services.yml';<\/strong><\/pre>\n<p>Clear caches after making this change:<\/p>\n<pre><strong>drush cr<\/strong><\/pre>\n<h2>Step 6: Test Logging<\/h2>\n<p>Use Drupal\u2019s logger service anywhere in custom code:<\/p>\n<pre><strong>\\Drupal::logger('custom_channel')-&gt;debug('This is a debug message.');\r\n\\Drupal::logger('custom_channel')-&gt;error('This is an error message.');\r\n<\/strong><\/pre>\n<p>You can also create different channels for different modules or features.<\/p>\n<h2>Viewing Logs in Containers<\/h2>\n<p><strong>Lando<\/strong><\/p>\n<pre><strong>lando logs -f<\/strong><\/pre>\n<p><strong>Docker<\/strong><\/p>\n<pre><strong>docker logs -f &lt;container_name&gt;\r\ndocker logs -f\r\n<\/strong><\/pre>\n<p>This makes Monolog ideal for modern DevOps workflows.<\/p>\n<h2>Best Practices for Using Monolog in Drupal<\/h2>\n<ul>\n<li>Use stdout logging in containers<\/li>\n<li>Disable dblog in production<\/li>\n<li>Separate log levels (ERROR vs DEBUG)<\/li>\n<li>Rotate file logs to prevent disk issues<\/li>\n<li>Send critical logs to alerting systems (Slack, Email)<\/li>\n<li>Avoid excessive DEBUG logging in production<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>By integrating <strong>Monolog<\/strong> into Drupal, you move from basic database logging to a <strong>scalable, high\u2011performance, and enterprise\u2011ready logging system<\/strong>. Whether you are running a small site, a headless Drupal backend, or a large\u2011scale cloud deployment, Monolog gives you the flexibility and control needed to make your logs meaningful and actionable.<\/p>\n<p>Monolog is not just a logging replacement\u2014it is a foundation for better monitoring, debugging, and observability in modern Drupal applications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Effective logging sits at the heart of building and operating dependable web applications. It gives engineering teams the visibility they need to understand runtime behavior, investigate errors, maintain audit trails, and act decisively when issues arise in live environments. Drupal ships with two native logging options: Database Logging (dblog) and Syslog. In the dblog approach, [&hellip;]<\/p>\n","protected":false},"author":1700,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":121},"categories":[3602],"tags":[8257],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/77068"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1700"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=77068"}],"version-history":[{"count":14,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/77068\/revisions"}],"predecessor-version":[{"id":77353,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/77068\/revisions\/77353"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=77068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=77068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=77068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}