Custom Error Log Formatting in Apache 2.2: Prepend Vhost Name and Pipe to External Program


2 views

While Apache's access logs offer extensive customization through the LogFormat directive, error logs historically had limited formatting options. The standard error log format includes timestamp, log level, and error message, but lacks built-in support for adding virtual host identifiers.

In multi-host environments, error logs without vhost context become difficult to parse. Consider this standard error entry:


[Wed Jul 19 10:28:32 2023] [error] [client 192.168.1.5] File does not exist: /var/www/html/missing.html

You can't tell which virtual host generated this error, making troubleshooting inefficient.

Here are two practical approaches to achieve custom error logging with vhost identification:

1. ErrorLog Directive with Pipe

Apache allows piping logs to external programs. Create a wrapper script that prepends vhost names:


ErrorLog "|/usr/local/bin/vhost-logger -v myvhost.example.com"

Sample wrapper script (vhost-logger):


#!/bin/sh
while read line; do
  echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$1] $line" >> /var/log/apache2/errors.log
done

2. CustomLog with Error-level Messages

Leverage Apache's LogLevel with CustomLog:


LogLevel warn
CustomLog "|/path/to/processor" "%v %t [%l] %M" env=error-note

In your virtual host configuration:


SetEnvIf Request_URI ".*" error-note

When implementing custom error logging:

  • Ensure your logging script handles high traffic volumes
  • Consider asynchronous processing for the pipe output
  • Monitor system resources during peak loads

While this question specifically addresses Apache 2.2, note that Apache 2.4+ introduced ErrorLogFormat:


ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"

This makes vhost-aware error logging significantly easier in modern versions.

Here's a complete virtual host configuration with custom error processing:


<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example
    ErrorLog "|/usr/local/bin/vhost-logger example.com"
    CustomLog "|/usr/local/bin/vhost-logger example.com" combined
    LogLevel warn
</VirtualHost>

The accompanying processor script (vhost-logger) might look like:


#!/usr/bin/perl
use strict;
use warnings;

my $vhost = shift @ARGV;
while (my $line = <STDIN>) {
    open(my $fh, '>>', "/var/log/apache2/${vhost}_error.log") or die $!;
    print $fh "[".localtime()."] $line";
    close $fh;
}

While Apache's access logs famously support the LogFormat directive for complete customization, error logs have traditionally been more rigid in their formatting. However, with some creative configuration, we can achieve similar functionality for error logs.

The standard error log format in Apache 2.2 doesn't include virtual host information by default, making it difficult to:

  • Distinguish between errors from different vhosts in shared environments
  • Pipe logs to custom processing scripts with host context
  • Maintain organized error tracking across multiple sites

Here's a technique to prepend vhost names to error logs:


# In your virtual host configuration
<VirtualHost *:80>
    ServerName example.com
    ErrorLog "|/usr/bin/logger -t example.com_error"
    CustomLog "|/usr/bin/logger -t example.com_access" combined
</VirtualHost>

For more sophisticated processing, you can create a wrapper script:


#!/usr/bin/perl
use strict;
use warnings;

while (<STDIN>) {
    print "[$ENV{'APACHE_VHOST'}] $_";
}

Configure Apache to use it:


SetEnv APACHE_VHOST example.com
ErrorLog "|/path/to/your/script.pl >> /var/log/apache2/error.log"

For systems using syslog, you can leverage its tagging capabilities:


ErrorLog "syslog:local1"

Then configure syslog to handle the tagging:


local1.* /var/log/apache2/error.log

Remember that piping logs to external programs adds overhead. For high-traffic sites:

  • Consider buffering mechanisms
  • Implement log rotation at the script level
  • Monitor the additional process's resource usage

Here's a complete vhost configuration demonstrating the technique:


<VirtualHost *:80>
    ServerName client1.example.com
    DocumentRoot /var/www/client1
    SetEnv VHOST_ID client1
    ErrorLog "|/usr/local/bin/vhost-logger --type=error --vhost=client1"
    CustomLog "|/usr/local/bin/vhost-logger --type=access --vhost=client1" combined
</VirtualHost>