When managing multiple servers (or even just the one) logging to syslog instead of /var/log/php_error.log becomes very handy. Setting up syslog logging couldn't be easier either, all you have to do is changing your php.ini 'error_log' config setting to 'syslog'. Granted you can't define the facility or ident manually but that is a minor issue.
The Syslog C lib provides 3 important functions to facilitate logging to syslog. These are openlog, syslog and closelog.
void openlog(char *ident, int option, int facility);
void syslog(int facility_priority, char *format, ...);
void closelog(void);
A call to openlog opens a connection to syslog and sets the parameters to use when logging. It is also the only way to set the ident to use in the logged messages. closelog simply closes the connection again. You don't have to call openlog before you call syslog, because syslog will call openlog implicitly the first time it is used. But if you don't call openlog explicitly then syslog will use the default ident and options, which can be important to know. The default ident for example will usually be the process name. Note that there's no connection reference in either of the functions, this is because the syslog library only keeps one global connection within the process. This means that any options set directly or indirectly through openlog will be used until they are changed by another call to openlog.
As we can see in the excerpt below PHPs internal error logger doesn't explicitly call openlog so the facility will default to LOG_USER and the ident to the process name*.
main/main.c
616 #ifdef HAVE_SYSLOG_H
617 if (!strcmp(PG(error_log), "syslog")) {
618 php_syslog(LOG_NOTICE, "%s", log_message);
619 PG(in_error_log) = 0;
620 return;
621 }
622 #endif
php_syslog is defined as syslog in main/php_syslog.h.
Obviously openlog could have been called from another location in the code but that isn't the case.
Now in itself this isn't a problem, unless we've changed the defaults in syslog.conf (or rsyslog.conf) our PHP errors should end up in /var/log/syslog, /var/log/messages or /var/log/user.log depending on the OS/dist. But PHP also exposes the 3 functions openlog, syslog and closelog as PHP functions so that our scripts can log directly to syslog, and this is where things get interesting. Because the connection to syslog is global within the process, as mentioned above, any calls to openlog in a PHP script will alter the syslog settings for the entire process potentially causing all kinds of trouble.
To demonstrate this we'll set up syslog to send messages to the facility LOG_LOCAL6 to the file /var/log/php-junk.log. In syslog.conf add:
local6.* /var/log/php-junk.log
If your PHP errors aren't showing up in /var/log/user.log, /var/log/syslog or /var/log/messages then add the following as well
user.* /var/log/user.log
Restart syslog and then run this simple script:
<?php
trigger_error("Some php error…");
openlog("myscript", LOG_LOCAL6);
syslog(LOG_NOTICE, "Kilroy was here");
trigger_error("Some other php error…");
?>
The first line should correctly end up in user.log while the following two should be sent to php-junk.log.
Now if we had called closelog(); directly after our syslog call the last message would have been sent to user.log since openlog gets called implicitly as the result of trigger_error().
A quick fix would be to add a call to openlog/closelog before/after the call to php_syslog in main/main.c but that would break any script that expected openlog to be called only once. A much better solution was suggested by Jérôme Loyet in the rfc 'Allow multiple simultaneous syslog connections' but it seems it didn't gather enough interest so the solution has since been abandoned.
:(
* Another quirk whose origin I haven't tracked down yet is that when the process name is used as ident it will sometimes be cut. I noticed that when running php-fpm with the default pool name 'www', logged messages would get the ident 'ool www'.