Working Apache and Rsyslog configuration

From rsyslog wiki
Jump to: navigation, search

Working Apache and Rsyslog configuration

Post by hkspvt on Tue Jul 08, 2008 9:46 pm

This is just a quick howto for anyone who might want to configure Apache to log to rsyslog. There are other ways to do it (set rsyslog to read in from a file), but this is my preferred method.

Background Apache maintains its own error and access logs. These are fine for some installations, but lack a lot as the complexity/criticality of an environment increases.

How to First step is to decide what facility you'll be logging to. I decided to log access to local6 and errors to local7 for a few reasons:
- I want to keep access and error logs separate
- I want to leave room for debugging Apache (ie, setting the loglevel in httpd.conf to debug) without mixing the two
- Apache's interaction with syslog isn't particularly well documented

Apache's error logs are very easy to point at syslog with the ErrorLog directive:

 ErrorLog syslog:local7

Because the Apache error log uses syslog-standard severity ratings, you can use normal syslog configurations to split syslog output into different files based on severity. For example, if you wanted to log only critical errors to a particular file, add the line in your /etc/rsyslog.conf file:

 local1.crit   /var/log/apache.crit

This still causes the logging of entries of level crit and higher to the file /var/log/apache.crit.

The access logs are a bit trickier. They don't have a built-in syslog function, but do accept pipes. A quick perl script based off of O'Reilly's article and installed in /usr/local/sbin/apache_syslog does the trick:

 #!/usr/bin/perl
 use Sys::Syslog qw (:DEFAULT setlogsock);

 setlogsock('unix');

 # open our log socket
 openlog('httpd', 'pid', 'local6');

 # log all our input
 while (<STDIN>) {
        syslog('info', $_);
 }

 # close the log socket
 closelog;

Now you can point your access logs at it with the CustomLog directive in httpd.conf (combined refers to the format - use common if you're unsure):

CustomLog |/usr/local/sbin/apache_syslog combined

A C version of the above, which seems to run much faster and with less resource requirements:

#include <stdio.h>
#define SYSLOG_NAMES
#include <syslog.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char *argv[])
{
  if (2 != argc)
  {
    fprintf(stderr, "Usage: stdin-syslogd priority\n");
    exit (1);
  }

  const char *priority_name = argv[1];
  int priority = -1;
  int i;

  for (i = 0; prioritynames[i].c_name; ++i)
  {
    if (! strcmp(prioritynames[i].c_name, priority_name))
    {
      priority = prioritynames[i].c_val;
      break;
    }
  }

  if (priority < 0)
  {
    fprintf(stderr, "stdin-syslogd: priority \"%s\" not recognized",
      priority_name);
    exit (1);
  }

  openlog("httpd", LOG_PID|LOG_NDELAY, LOG_LOCAL6);

  char buffer[8192];

  while (NULL != fgets(buffer, sizeof(buffer), stdin))
  {
    syslog(priority, "%s", buffer);
  }
  return 0;
}

The above C version is not geared for unicode. I'm not sure but maybe it should be changed to use fgetws and friends.

Using logger

This can also achieved using the widely-available logger utility. (See man page : [1]) If you are using Redhat, this program is provided by "util-linux" rpm package which comes as default; for Debian/Ubuntu it's included in the "bsdutils" package.

To do this edit apache config and add a new CustomLog line similar to:


# (20100219) Rianto Wahyudi - Send apache log to syslog 
CustomLog "|/usr/bin/logger -t httpd -p local6.info" combined
CustomLog logs/access_log combined

The above configuration will send access log to syslog and also to standard file log.

Configuring rsyslog.conf

Now for rsyslog.conf. It's possible that other applications are logging under the local6 and local7 facilities, so we want to log based on both facility and program name. Moreover, having these logs included in multiple places would not be good, so we'll just dump them after we've pulled them out.

==Code: Select all==
if $syslogfacility-text == 'local6' and $programname == 'httpd' then /var/log/httpd-access.log
if $syslogfacility-text == 'local6' and $programname == 'httpd' then ~
if $syslogfacility-text == 'local7' and $programname == 'httpd' then /var/log/httpd-error.log
if $syslogfacility-text == 'local7' and $programname == 'httpd' then ~

Restart rsyslog, then restart Apache, and that's it.

Hopefully that's a help to someone. -HKS