ASL: Logging

2008-01-20 13:48:14 UTC

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 (format, and .../ap) are the same as the arguments to printf/vprintf or syslog/vsyslog. They’re also similar to NSLog/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 %s and -UTF8String instead of %@, don’t worry. Here’s a one-line macro you can stash in a header file to give you an asl_log-like NSLog:

#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 NSLog.

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_open or by 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 "" (NSLog seems 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 “” 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 asl_set:

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_log or 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);

As with 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_log.

Note that 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:

Key Default value
ASL_KEY_TIME The current time (set unconditionally)
ASL_KEY_HOST On Tiger: NULL (set unconditionally)
On Leopard: The “.local” hostname, derived from Sharing prefs (set unconditionally)
ASL_KEY_SENDER The name of your process (only on Leopard; already set by asl_new on Tiger)
ASL_KEY_PID Your process ID (set unconditionally)
ASL_KEY_UID The user (set unconditionally)
ASL_KEY_GID 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 asl_set.)
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_log and 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

2 Responses to “ASL: Logging”

  1. Hans van der Meer Says:

    I do not have not so much as a reply, but a question.

    Using XCode 3.0 (Leopard) I coded:
    asl_log(NULL, NULL, 1, “TESTING ASL-LOG:%s”, “asl-log test”);
    but do not see anything resulting from it.
    Neither in the XCode console, nor in the Console app. Why not?
    Am I doing something wrong?

  2. Peter Hosey Says:

    Hans van der Meer:

    For how to make it appear in Xcode, read the next post, Client connections.

    For how to make it appear in Console, first read the section on ASL_KEY_FACILITY, then read Client connections (you can set the default facility of every message with a client connection).

    Reading the entire series is a good idea in general. ☺

Leave a Reply

Do not delete the second sentence.