You can use two major APIs in Mac OS X to log errors: syslog
and asl
. In addition, you can use a number of higher-level APIs built on top of these such as NSLog
. However, because most daemons are written in C, you probably want to use the low-level APIs when writing code that executes at startup.
The syslog
API is the most commonly used logging API on UNIX and Linux systems. For this reason, you should consider using it when writing cross-platform software. For software specific to Mac OS X, you should use the asl
API because it provides more functionality.
If your daemon uses standard output or standard error to notify users of problems during startup, those error messages are not generally visible to a user. You can, of course, boot in verbose mode (by holding down Command-v at startup), but you may have a hard time reading the errors as they scroll by. To solve this problem, you should use system logging to record error conditions for later analysis by the user or system administrator.
Log Levels and Log Files
Logging Errors Using the syslog API
Logging Errors Using the asl API
In Mac OS X (and other UNIX-based and UNIX-like operating systems), the system logger supports logging at a number of priority levels. The priority levels (and suggested uses for these levels) are:
Emergency (level 0): The highest priority, usually reserved for catastrophic failures and reboot notices.
Alert (level 1): A serious failure in a key system.
Critical (level 2): A failure in a key system.
Error (level 3): Something has failed.
Warning (level 4): Something is amiss and might fail if not corrected.
Notice (level 5): Things of moderate interest to the user or administrator.
Info (level 6): The lowest priority that you would normally log, and purely informational in nature.
Debug (level 7): The lowest priority, and normally not logged except for messages from the kernel.
The system logger in Mac OS X determines where to log messages at any given priority level based on the file /etc/syslog.conf
.
Note: A daemon or application may mask low-priority messages before they even get to the system logger using the setlogmask
function call. Thus, if you want to see these debugging messages in the system log, you may have to pass certain debugging flags to the daemon, regardless of how you have configured the system logger in /etc/syslog.conf
.
The syslog
API is relatively straightforward. It consists of five main functions:
void syslog(int priority, const char *message, ...); |
void vsyslog(int priority, const char *message, |
va_list args); |
void openlog(const char *ident, int logopt, int facility); |
void closelog(void); |
int setlogmask(int maskpri); |
The first function you should call is openlog
. This function associates future calls to syslog
with a particular facility. You can find a list of facilities in the man page for syslog
.
Note: Technically, you can call syslog
without calling openlog
, but as a rule, you should always call openlog
to specify a facility and logging options. If you do not call openlog
before you call syslog
, the API will use the default facility, LOG_USER
.
Next, you should call syslog
. This function actually logs your message at a specified priority level. The priority levels for log messages are LOG_EMERG
, LOG_ALERT
, LOG_CRIT
, LOG_ERR
, LOG_WARNING
, LOG_NOTICE
, LOG_INFO
, or LOG_DEBUG
, in decreasing order of importance. These correspond with the levels described in “Log Levels and Log Files.”
Warning: It is very important to choose an appropriate priority level for log messages. The system logger discards most low-priority messages, depending on the facility specified. To find out how the system logger decides which facilities and priority levels to log in a given log file, look in the file /etc/syslog.conf
.
If you need to write a wrapper function for the syslog
function, you should use the function vsyslog
instead. This function is identical to syslog
except that it takes a variable argument list parameter instead of a series of individual parameters.
Finally, when your program exits (or when you need to specify a different facility), you should call closelog
. This function resets the facility and logging options to the default settings as though you had never called openlog
.
The following code example shows how to log a simple error message:
#include <fcntl.h> |
#include <syslog.h> |
main() |
{ |
int cause_an_error = open("/fictitious_file", O_RDONLY, 0); // sets errno to ENOENT |
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); |
syslog(LOG_EMERG, "This is a silly test: Error %m: %d", 42); |
closelog(); |
} |
The flags passed to openlog
specify the following:
LOG_CONS: If the syslogd
daemon is not running, the syslog
function should print the message to the console.
LOG_PERROR: in addition to normal logging, the syslog
function should also print the message to standard error.
LOG_PID: The syslog
function should append the process ID after the process name at the beginning of the log message.
These and other flags are described in more detail in the syslog
manual page.
In addition to the usual printf
format flags, this command supports an additional flag, %m
. If this flag appears in the log string, it is replaced by a string representation of the last error stored in errno
. This is equivalent to what would be reported if you called perror
or strerror
directly.
Thus, the code sample above prints the following message to standard output:
LogIt[165]: This is a silly test: Error No such file or directory: 42 |
Then, the code sample tells the system logger to log that message. As a result, assuming you have not changed /etc/syslog.conf
, the system logger broadcasts this message to all users:
Broadcast Message from user@My-Machine-Name.mycompany.com |
(no tty) at 13:28 PDT... |
Jul 24 13:28:46 My-Machine-Name LogIt[601]: This is a silly test: Error No such file or directory: 42 |
In this example, the process ID was 601
, and the process name was LogIt
.
For additional control over what gets logged, you can use the function setlogmask
to quickly enable or disable logging at various levels. For example, the following code disables logging of any messages below the LOG_EMERG
level (which is one higher than the LOG_ALERT
level):
setlogmask(LOG_UPTO(LOG_ALERT)); |
You might, for example, use this function to disable logging of debug messages without recompiling your code or adding conditional statements.
The asl
API is short for Apple System Logger. The Apple System Logger API is very similar to syslog
but provides additional functionality.
There are a few key differences, though; the asl
logging API:
Uses a text string for the facility identifier for more precise filtering of log messages
Provides functions for querying the log files
Is safe for use in a multithreaded environment because it provides functions for obtaining a separate communication handle for each thread
The following sample code is equivalent to the code in “Logging Errors Using the syslog API,” except that it uses asl
for logging:
#include <fcntl.h> |
#include <asl.h> |
#include <unistd.h> |
main() |
{ |
aslclient log_client; |
int cause_an_error = open("/fictitious_file", O_RDONLY, 0); |
log_client = asl_open("LogIt", "The LogIt Facility", ASL_OPT_STDERR); |
asl_log(log_client, NULL, ASL_LEVEL_EMERG, "This is a silly test: Error %m: %d", 42); |
asl_close(log_client); |
} |
A complete explanation of the additional features of the asl
API is beyond the scope of this document. For more information, see the asl
manual page.
© 2003, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-11-19)