ASL provides two ways to write to the log.
The first, and most recognizable, way is the asl_log pair of functions:
int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...); int asl_vlog(aslclient asl, aslmsg msg, int level, const char *format, va_list ap);
The last two arguments (
ap) are the same as the arguments to
vsyslog. They’re also similar to
NSLogv, except that ASL does not support
%@ in its format strings.
The first two arguments to
asl_log are optional. If you pass
NULL as the client, it uses a default connection with default settings. If you pass
NULL for the message, all of the message’s properties other than the level and content will have their default values. Normally, you will pass
NULL for the first two arguments.
The argument in the middle,
level, specifies the importance of the message. It works exactly the same way as syslog(3)’s
priority argument; in fact, they use the same values, and those values have the same names.
If you don’t feel like changing over all your
NSLog calls to use
asl_log because you’ll have to change your format strings to use
-UTF8String instead of
%@, don’t worry. Here’s a one-line macro you can stash in a header file to give you an
#define asl_NSLog(client, msg, level, format, ...) asl_log(client, msg, level, "%s", [[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String])
This macro gives you all the flexibility of
asl_log and all the format specifiers of
The other way to write to the log is to construct a message object yourself and send the whole thing.
ASL defines a type called aslmsg, which is little more than a key-value mapping. All keys and values are UTF-8 C strings. ASL provides and uses several stock keys for the normal properties of a message:
- The priority level of the message, expressed as the decimal representation of an integer (
ASL_LEVEL_*). When setting this property, you can use the level-name strings (
ASL_STRING_*) here; they are names, not numbers, but they will be translated to the correct values for you.
- The content of the message.
- The time the message was sent. This is read-only; you can’t back-date a message.
- The name of the process that sent this message. You can set a different value for the sender, either in your call to
asl_set. Conversely, don’t assume that all sender names (even those that match the name of a process) are accurate, since the real sender may have lied.
- The name of the facility that sent this message. The wording of the manpage suggests that this property is intended for identifying subsystems in your program; in the context of a Cocoa app, this could mean different classes. On the other hand, if you want your messages to show up in Console, you must use the facility string
NSLogseems to do this, and it’s obliquely documented in the manpage for the syslog(1) utility; I’ll talk more about syslog(1) in one of the later posts). By the way, you can’t use any facility name starting with “com.apple.system” unless you’re running as root; otherwise, syslogd will correct it to the default value, “user”.
- The process ID that sent the message. Read-only.
- The user ID of the user who owns the process that sent the message. Read-only.
- The group ID of the primary group of the user who owns the process that sent the message. Read-only.
- The hostname of the machine this message was sent from. Read-only since Leopard (writable in 10.4.10—that’s probably a bug, and I have no idea whether they’ve fixed it in 10.4.11).
You can actually use any keys you want; you’re not limited to the Apple-provided keys. However, you would be wise to use reverse-DNS notation in your custom keys, just in case Apple adds one or more standard keys in a future version of Mac OS X; you wouldn’t want to horribly confuse syslogd.
You create a message with the asl_new function:
aslmsg asl_new(uint32_t type);
For a message you plan to write, the correct type is ASL_TYPE_MSG. (That’s not the only type; there’s one other. I’ll tell you about it a few posts from now.)
Of course, an empty message isn’t much good to anybody. The next step is to set its properties using
int asl_set(aslmsg msg, const char *key, const char *value);
You don’t need to set all the properties; all of the properties except
ASL_KEY_MSG have reasonable default values.
Once you have a filled-out message, you need to write it to the log. There are two ways to do this.
The first way is to use
asl_vlog. When you pass your message to one of these functions, it’s used as a template; ASL creates a new message based on the one you provide, sets its
ASL_KEY_MSG property to the string it constructs from your format and arguments, then puts that message into the log.
The other way is to directly call asl_send:
int asl_send(aslclient asl, aslmsg msg);
asl_log, that first argument is optional.
You probably won’t find a reason to use
asl_send. On the one hand, you could create an entire
aslmsg from scratch, including
ASL_KEY_MSG and any custom properties that you want to attach to the message, and call
asl_send with it—every time. On the other hand, you could fill out a basic
aslmsg once during initialization, putting all your custom properties in it, and keep it around, and use it later as a template in one or more one-line calls to
asl_send mutates the message you pass in. When you omit one of the stock keys,
asl_send adds it to your message, with a default value. The default values are:
||The current time (set unconditionally)|
|On Leopard: The “.local” hostname, derived from Sharing prefs (set unconditionally)|
||The name of your process (only on Leopard; already set by
||Your process ID (set unconditionally)|
||The user (set unconditionally)|
||The primary group of the user (set unconditionally)|
I determined this simply by iterating on the key-value pairs of a message after I sent it. (I’ll talk more about inspecting messages in the forthcoming post on searching.)
In case you’re wondering whether you can safely mutate a message object that you’ve already passed to
asl_send, without inadvertently modifying the message in the database: Currently, yes, but this fact isn’t documented, so there’s no guarantee that it will always be this way. (A future version could, for example, poke syslogd whenever you call
The documentation seems to assume that you will throw your message away immediately after sending it;
asl_log, on the other hand, explicitly uses your message “as a template”.
The documentation says that
asl_send is for when you have completely filled out a message already, but this actually is not necessary (at least, not at this time). Most properties will be added for you; the only one you need to add is
ASL_KEY_LEVEL, and only because if you don’t set it yourself, ASL acts as if it were set to
ASL_LEVEL_DEBUG, which is excluded by the default filter mask (more on those in tomorrow’s post), which will result in your message not being sent on to syslogd.
Also, on Tiger,
asl_send does not add the
ASL_KEY_HOST key for you; this means that the message will not have an
ASL_KEY_HOST property at all when it is entered into the log. Only
asl_vlog add the property. This was fixed in Leopard; the
ASL_KEY_HOST property is always added (by
asl_send) whenever you don’t set it yourself.
When you’re done with the message, free it with asl_free:
void asl_free(aslmsg msg);
Next in the ASL series: Client connections