Quantcast
Channel: Gustavo Sverzut Barbieri » Linux
Viewing all articles
Browse latest Browse all 10

Eina_Log customizations

0
0

Introduction to Eina_Log:

EFL provides a powerful logging system called “eina_log”, it can help developers and QA engineers a lot by providing key features:

  • Levels/Priorities with option to disable at compile time: similar to syslog, one can specify levels such as critical, error, warning, information or debug. An added bonus is that you can compile-out messages above certain levels, removing the function call overhead and the strings from the binary.
  • Logging domains: one can create multiple domains aside from the global/default one, this allows for easy filtering when debugging or looking for errors. Let’s say you want debug Edje, just export EINA_LOG_LEVELS="edje:4"
  • Comprehensive, Colorful default formatting: it will automatically turn on colors if your $TERM is supported (also works on Windows). Output includes PID, log domain, file name, line number and even thread ID if that’s different from the main thread. Some of the components can be toggled with environment variables.
The benefits of writing with eina_log instead of adding printf() is that you can more easily filter the output and you can keep the debug information there, without extra impacts. Often people that add printf() will forget them and pollute everybody’s else output, eventually clashing with user-output (if printf()/stdout and not fprintf()/stderr). Or they remove and when someone else run into similar bugs and needs to walk the code and think all over again on where to add logs to figure out the problem. Then it’s best that the developer who writes the code leaves proper debug messages with key variables/values, for future debug help.

Although widely documented and with examples at http://docs.enlightenment.org/auto/eina/group__Eina__Log__Group.html some still don’t use it due lack of knowledge, particularly on how to customize the output. They end writing new systems, such as Tizen did with “dlog“.

Customizing Eina_Log output:

The application can specify its own logging function with eina eina_log_print_cb_set(function, function_data). By default it will use eina_log_print_cb_stderr(), but eina_log_print_cb_stdout() and eina_log_print_cb_file() are available. We’ll explore the options by writing our own functions (all source hosted at http://barbieri-playground.googlecode.com/svn/efl-tests/eina-log/)

Simple Log to Stderr:

Let’s start with a simple test that just prints to stderr without fancy formatting. Each line is the log level followed by the actual message:

#include <Eina.h>

/* gcc -o test test.c `pkg-config --libs --cflags eina` */

static void
_my_log_cb(const Eina_Log_Domain *d,
           Eina_Log_Level level,
           const char *file,
           const char *fnc,
           int line,
           const char *fmt,
           void *data,
           va_list args)
{
    fprintf(stderr, "%d: ", level);
    vfprintf(stderr, fmt, args);
    putc('\n', stderr);
}

int main(int argc, char *argv[])
{
    eina_init();
    eina_log_print_cb_set(_my_log_cb, NULL);

    EINA_LOG_ERR("Hi there: %d", 1234);

    eina_shutdown();
    return 0;
}

Output:

$ gcc -o eina-log-simple eina-log-simple.c `pkg-config --cflags --libs eina`
$ ./eina-log-simple
1: Hi there: 1234

It starts as usual initializing Eina, then sets our function to be used as print callback, then uses the standard macro that logs to the global domain. Our function is also pretty simple and easy to understand.

Simple Log just our domain to Stderr:

How about if we just want the simple output for our application domain, leaving all others with Eina’s standard eina_log_print_cb_stderr()? This was required by edje_cc tool. It’s just about using the const Eina_Log_Domain *d parameter!

#include <Eina.h>

/* gcc -o test test.c `pkg-config --libs --cflags eina` */

static void
_my_log_cb(const Eina_Log_Domain *d,
           Eina_Log_Level level,
           const char *file,
           const char *fnc,
           int line,
           const char *fmt,
           void *data,
           va_list args)
{
   if ((!d->name) || (strcmp(d->name, "mydomain") != 0))
     eina_log_print_cb_stderr(d, level, file, fnc, line, fmt, NULL, args);
   else
     {
        fprintf(stderr, "%d: ", level);
        vfprintf(stderr, fmt, args);
        putc('\n', stderr);
     }
}

int main(int argc, char *argv[])
{
   int log_domain;

   eina_init();
   eina_log_print_cb_set(_my_log_cb, NULL);

   log_domain = eina_log_domain_register("mydomain", NULL);

   EINA_LOG_ERR("Hi there: %d", 1234);
   EINA_LOG_DOM_ERR(log_domain, "Just for domain: %x", 0xff00);

   eina_shutdown();
   return 0;
}

Output:

$ ./eina-log-simple-domain
ERR: eina-log-simple-domain.c:34 main() Hi there: 1234
1: Just for domain: ff00

As one can see, just “mydomain” is printed in a simpler way, other domains are still using Eina’s standard output.

Log to Syslog:

If you’re writing a key component of some platform, such as the phone dialer, you may want to log it to syslog. It may also be useful in some embedded systems. That’s as simple:

#include <Eina.h>
#include <syslog.h>

/* gcc -o test test.c `pkg-config --libs --cflags eina` */

static void
_my_log_cb(const Eina_Log_Domain *d,
           Eina_Log_Level level,
           const char *file,
           const char *fnc,
           int line,
           const char *fmt,
           void *data,
           va_list args)
{
    int priority;
    switch (level) {
     case EINA_LOG_LEVEL_CRITICAL:
        priority = LOG_CRIT;
        break;
     case EINA_LOG_LEVEL_ERR:
        priority = LOG_ERR;
        break;
     case EINA_LOG_LEVEL_WARN:
        priority = LOG_WARNING;
        break;
     case EINA_LOG_LEVEL_INFO:
        priority = LOG_INFO;
        break;
     case EINA_LOG_LEVEL_DBG:
        priority = LOG_DEBUG;
        break;
     default:
        priority = level + LOG_CRIT;
    }
    vsyslog(priority, fmt, args);
}

int main(int argc, char *argv[])
{
    eina_init();
    eina_log_print_cb_set(_my_log_cb, NULL);

    EINA_LOG_ERR("Hi there: %d", 1234);

    eina_shutdown();
    return 0;
}

Output:

Oct 27 16:20:18 solid eina-log-syslog[25582]: Hi there: 1234

As simple as you wondered, just noticed that we are converting Eina’s level to syslog priorities.

Log to Systemd’s Journal

Systemd will be used in Tizen, then it would be nice to have Eina to log to its powerful Journal system. Lennart wrote a nice article systemd for Developers III where he explains how to do it and the benefits. Looking at sd-journal.h we can see how to do it:

#include <Eina.h>
#include <systemd/sd-journal.h>

/* http://0pointer.de/blog/projects/journal-submit */
/* gcc -o test test.c `pkg-config --libs --cflags eina libsystemd-journal` */

static void
_my_log_cb(const Eina_Log_Domain *d,
           Eina_Log_Level level,
           const char *file,
           const char *fnc,
           int line,
           const char *fmt,
           void *data,
           va_list args)
{
    char filestr[PATH_MAX + sizeof("CODE_FILE=")];
    char linestr[128];
    int priority;
    switch (level) {
     case EINA_LOG_LEVEL_CRITICAL:
        priority = LOG_CRIT;
        break;
     case EINA_LOG_LEVEL_ERR:
        priority = LOG_ERR;
        break;
     case EINA_LOG_LEVEL_WARN:
        priority = LOG_WARNING;
        break;
     case EINA_LOG_LEVEL_INFO:
        priority = LOG_INFO;
        break;
     case EINA_LOG_LEVEL_DBG:
        priority = LOG_DEBUG;
        break;
     default:
        priority = level + LOG_CRIT;
    }

    snprintf(filestr, sizeof(filestr), "CODE_FILE=%s", file);
    snprintf(linestr, sizeof(linestr), "CODE_LINE=%d", line);
    sd_journal_printv_with_location(priority, filestr, linestr, fnc, fmt, args);
}

int main(int argc, char *argv[])
{
    eina_init();
    eina_log_print_cb_set(_my_log_cb, NULL);

    EINA_LOG_ERR("Hi there: %d", 1234);

    eina_shutdown();
    return 0;
}

Output (json-pretty)

$ ./eina-log-journal
$ journalctl -o json-pretty
{
        "__CURSOR" : "s=3c7eaf66e4734bb286e54efd20bb9f8d;i=645;b=c6681f0bd53d41d4ba682e12021095e7;m=23487887b;t=4cd0e8d5c0bf3;x=ab7f934fdb40485f;p=system.journal",
        "__REALTIME_TIMESTAMP" : "1351362291698675",
        "__MONOTONIC_TIMESTAMP" : "9471232123",
        "_BOOT_ID" : "c6681f0bd53d41d4ba682e12021095e7",
        "MESSAGE" : "Hi there: 1234",
        "PRIORITY" : "3",
        "CODE_FILE" : "eina-log-journal.c",
        "CODE_LINE" : "50",
        "CODE_FUNC" : "main",
        "_TRANSPORT" : "journal",
        "_PID" : "25655",
        "_UID" : "500",
        "_GID" : "500",
        "_SOURCE_REALTIME_TIMESTAMP" : "1351362291698325",
        "_MACHINE_ID" : "b84a5d286a7bfe411836d69100004cf6",
        "_HOSTNAME" : "solid"
}

See that we can get our file, line and function to be saved by the journal. Just awesome! :-)


Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images