Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use ASL to log to console.app

Tags:

c

macos

In OSX 10.8 output to stdout and stderr no longer ends up in Console.app. I'd like to get output in Console.app without using NSLog because I need to support code that uses basic print statements to print debug information (see https://bitbucket.org/ronaldoussoren/py2app/issue/77 for some background information).

NSLog output ends up in the ASL (Apple System Log) log "somehow", as you can view those loglines using "syslog -C". That's why I tried adding this code to my application:

aslclient c = asl_open("py2app", "com.apple.console", ASL_OPT_NO_DELAY);
int fd = dup(2);
asl_set_filter(c, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
asl_add_log_file(c, fd);
asl_log(c, NULL, ASL_LEVEL_INFO, "Hello world from py2app launcher");
asl_log_descriptor(c, NULL, ASL_LEVEL_INFO, 1, ASL_LOG_DESCRIPTOR_WRITE);
asl_log_descriptor(c, NULL, ASL_LEVEL_INFO, 2, ASL_LOG_DESCRIPTOR_WRITE);

This somewhat works: when I write lines to the stdout stream those lines get transformed by ASL: the output is now prefixed by usual logging prefix:

Nov 20 13:46:14 Gondolin.local py2app[43722] <Info>: Hello world from py2app launcher

The logfile does not end up in the ASL datastore or Console.app though.

Does anyone know what I'm doing wrong?

like image 761
Ronald Oussoren Avatar asked Nov 20 '12 13:11

Ronald Oussoren


2 Answers

The following C code appears to do what I want:

#include <asl.h>
#include <unistd.h>
#include <stdio.h>

static void
setup_logging(void)
{
        aslmsg msg;
        aslclient c = asl_open("py2app", "com.apple.console", 0);

        msg = asl_new(ASL_TYPE_MSG);
        asl_set(msg, ASL_KEY_FACILITY, "com.apple.console");
        asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
        asl_set(msg, ASL_KEY_READ_UID, "-1");

        int fd = dup(2);
        //asl_set_filter(c, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
        asl_add_log_file(c, fd);
        asl_log(c, NULL, ASL_LEVEL_INFO, "Hello world from py2app launcher");
        asl_log_descriptor(c, msg, ASL_LEVEL_INFO, 1,  ASL_LOG_DESCRIPTOR_WRITE);
        asl_log_descriptor(c, msg, ASL_LEVEL_INFO, 2, ASL_LOG_DESCRIPTOR_WRITE);
}

int main(void)
{
        setup_logging();
        printf("hello world, this is a printf\n");
}

Compared with my first attempt this contains a single change: it explicitly sets the ASL Facility, Level and ReadUID using an "aslmsg" argument to asl_log_descriptor. Without these arguments the messages won't end up in Console.app. The ReadUID in particular is needed to be able to read the log entries without having super user privileges.

Note: to easily test you can use "syslog -C | tail" to read the console log. Without ReadUID my program's output only was visible when I used "sudo syslog -C".

like image 120
Ronald Oussoren Avatar answered Sep 20 '22 05:09

Ronald Oussoren


There's no need to do the asl_add_log_file(c, dup(2)).

Also, you can just set the log level in the call to asl_log_descriptor rather than in the asl_msg. Note that the reason you weren't seeing your messages in syslog when at the info level was because messages that are below notice are filtered out by default (see /etc/asl.conf).

Example:

#include <asl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    asl_log_descriptor(NULL, NULL, ASL_LEVEL_INFO, STDOUT_FILENO, ASL_LOG_DESCRIPTOR_WRITE);
    asl_log_descriptor(NULL, NULL, ASL_LEVEL_NOTICE, STDERR_FILENO, ASL_LOG_DESCRIPTOR_WRITE);
    fprintf(stdout, "This is written to stdout which will be at log level info.");
    fprintf(stderr, "This is written to stderr which will be at log level notice.");
    return 0;
}
like image 24
Jeremy Huddleston Sequoia Avatar answered Sep 22 '22 05:09

Jeremy Huddleston Sequoia