ASL: Wrap-up

2008-01-28 14:25:04 UTC

Summary

ASL is available on both Tiger and Leopard.

To write to the log:

asl_log(client, /*msg*/ NULL, ASL_LEVEL_NOTICE, "printf-style format; interpolates values, such as %i", 42);

The predefined priority levels are Debug, Info, Notice, Warning, Error (ASL_LEVEL_ERR), Critical (ASL_LEVEL_CRIT), Alert, and Emergency (ASL_LEVEL_EMERG). Every predefined priority level has a symbol to go with it; for example, Notice is ASL_LEVEL_NOTICE.

To open a client connection:

aslclient client = asl_open(/*ident*/ NULL, /*facility*/ "MyClass" /*or other reasonable facility string*/, ASL_OPT_STDERR);

To close it:

asl_close(client);

Opening a client connection is optional (you can pass NULL instead), as is passing ASL_OPT_STDERR if you do it (you can pass 0 instead), but I recommend doing both, so that you can see your ASL output in your Xcode Run Log.

To read from (search) the log:

//Build a query message containing all our criteria.
aslmsg query = asl_new(ASL_TYPE_QUERY);
//Specify one or more criteria with calls to asl_set_query.
asl_set_query(query, ASL_KEY_MSG, "Hello world", ASL_QUERY_OP_EQUALS);

//Begin the search.
aslresponse response = asl_search(query);

//We don't need this anymore.
asl_free(query);

aslmsg msg;
while ((msg = aslresponse_next(response))) {
    //Do something with the message. For example, to iterate all its key-value pairs:
    const char *key;
    for (unsigned i = 0U; (key = asl_key(msg, i)); ++i) {
        const char *value = asl_get(msg, key);

        //Example: Print the key-value pair to stdout, with a tab between the key and the value.
        printf("%s\t%s\n", key, value);
    }

    //Don't call asl_free for the message. The response owns it and will free it itself.
}

aslresponse_free(response);

The messages in the response are those that match all of the criteria (i.e., the search is logical-AND, just like Google). You can set more than one criterion with the same key; you could use this, for example, to express one or more ranges of timestamps.


One final note

I’ve said before that the best way to learn an API whose documentation is insufficient is to document it. This series (which was originally one post) proved that in spades.

I thought I knew ASL before I started, but I think I roughly doubled my knowledge with all the research, testing, and Using the Source, Luke that went into this epic post. I also discovered a total of four issues with the API or its documentation that needed to be filed in RadarWeb.

So, if you know of an API whose documentation is a little thin, such as I/O Kit, or System Configuration, or Core Audio, please write about it. Not only will you inform your readers of what you know, but you will probably learn quite a bit simply in the course of writing it, and your readers will learn what you learned, as well. It will benefit everybody.

10 Responses to “ASL: Wrap-up”

  1. Blake C. Says:

    A most excellent series of posts, sir! I’ve learned quite a bit from your research, and I look forward to more of the same. Thanx much for taking the time to share this with us all :)

  2. Jeff Johnson Says:

    Thanks, Peter! Great work. Maybe the ADC docs should just point here. ;)

    Question: what’s your view on how to deal most effectively with logging from multiple threads in an app?

  3. Peter Hosey Says:

    Jeff: I got a similar question by email.

    If you have multiple objects that each use one (and only one) thread, you can just put the connection in their class as an ivar.

    If you have one object which messages from multiple sources pass through, I would suggest using performSelectorOnMainThread: to fob all the logging off on the main thread. This is a bottleneck, but you’d need a lot of messages to come in at the same time to cause a performance problem.

    I should probably add this to the client connections post.

  4. Ken T. Says:

    I would recommend using the pthread per-thread data routines:

    Early in your application startup, create a key for the per-thread ASL connection using pthread_key_create(). Pass in a destructor routine that closes the connection. That way, each thread’s connection will be automatically closed when that thread exits. Store the key in a global variable.

    When you need to log, call pthread_getspecific() to obtain the thread’s ASL connection. If it comes back NULL, then this is the first time you’re logging for that particular thread. In that case, create the connection for the thread and store it using pthread_setspecific(). The business of getting the connection and creating it if necessary should be hidden within a function, naturally.

    If you prefer, in Cocoa you can use -[NSThread threadDictionary] to store the connection. In that case, you’d define your own class for holding an ASL connection, whose dealloc would close the connection. This seems to add complexity for no apparent benefit, though.

  5. Peter Hosey Says:

    Ken T.: Very good suggestion. Thanks.

  6. Brian K. Says:

    As pointed out by others, excellent, well detailed series.

    I think in your summary, the asl_log call is missing a parameter for level:

    asl_log(client, /*msg*/ NULL, "printf-style format; interpolates values, such as %i", 42);

    should be (?):

    asl_log(client, /*msg*/ NULL, ASL_LEVEL_XXX, "printf-style format; interpolates values, such as %i", 42);
  7. Peter Hosey Says:

    Brian K.: You’re absolutely right. Fixed.

  8. Mattias Says:

    Excellent series of posts, congratulations on helping the world becoming a better place for us programmers.

    Now, I’ve noticed that if I set the ASL_OPT_STDERR flag, I really cannot seem to get my printouts to have proper linebreaks in my shell (nor in Xcode’s terminal). I recognize that this is the same problem as you reported in rdar://problem/5695926. Take this as an example: “This is\n a linebreak \n and here comes one more\n”. That string will be printed exactly as is, that is \n isn’t treated like a line break. However, Console.app seems to be displaying the string just as I intended it to be seen, with \n converted to a newline.

    Again; thanks for some really good posts.

  9. Peter Hosey Says:

    Mattias: Console’s use of a table view suggests that it’s probably working with aslmsg objects, not any of the external representations. Remember, those are database queries, not plain-text log files.

  10. julian Says:

    since the ASL pros seem to be here, does anyone here know how this code (1) can crash like this (2) – seems to happen on PPC with 10.5.8 only:

    time_t now = time(NULL);
    aslmsg query = asl_new(ASL_TYPE_QUERY);
    asl_set_query(query, ASL_KEY_SENDER, “kernel”, ASL_QUERY_OP_EQUAL);
    asl_set_query(query, ASL_KEY_MSG, “I/O Error”, (ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING | ASL_QUERY_OP_CASEFOLD));
    asl_set_query(query, ASL_KEY_TIME, [[NSString stringWithFormat:@”%lu”, lastPollTime] UTF8String], ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC);
    asl_set_query(query, ASL_KEY_TIME, [[NSString stringWithFormat:@”%lu”, now] UTF8String], ASL_QUERY_OP_LESS | ASL_QUERY_OP_NUMERIC);
    aslresponse response = asl_search(NULL, query);

    Thread 0 Crashed:
    0 libSystem.B.dylib 0x94f45a60 strcmp + 192
    1 libSystem.B.dylib 0x9505b3e0 asl_file_fetch_object + 172
    2 libSystem.B.dylib 0x9505bad4 asl_file_fetch_pos + 968
    3 libSystem.B.dylib 0x9505c264 asl_file_match_next + 168
    4 libSystem.B.dylib 0x9505d9e8 asl_file_list_match_timeout + 348
    5 libSystem.B.dylib 0x9505a694 asl_store_match_timeout + 276
    6 libSystem.B.dylib 0x9505a6d0 asl_store_match + 20
    7 libSystem.B.dylib 0x95060c10 asl_search + 136

Leave a Reply

Do not delete the second sentence.