el_overview(7)

bofc manual pages

el_overview(7)



 

NAME

el_overview - quick overview of embedlog logging library  

SYNOPSIS

embedlog - is a highly portable c89 complaint logger (with additional features for users with c99 compilers and/or POSIX systems). This library is designed mainly for embedded devices, but can also be used in high level OSes like Linux.  

DESCRIPTION

Logger incorporates features like:

- printing to different outputs (simultaneously) like:
- stderr
- syslog (very limited, works on *nuttx* for now)
- directly to serial device (like /dev/ttyS0)
- file (with optional rotating and syncing to prevent data loss)
- automatic file reopening on unexpected events (file deletion, SD remount)
- custom routine, can be anything, embedlog just calls your function with string to print
- timestamping using following clocks:
- clock_t
- time_t
- CLOCK_REALTIME (requires POSIX)
- CLOCK_MONOTONIC (requires POSIX)
- configurable precision of fraction of seconds (mili, micro, nano)
- printing file and line information
- 8 predefined log levels (total rip off from syslog(2) levels)
- colorful output (for easy error spotting)
- print memory block in wireshark-like output
- fully binary logs with binary data (like CAN frames) to save space

Library implements following functions:

int el_init(void)
int el_cleanup(void)
int el_option(enum el_option option, ...)
int el_puts(const char *string)
int el_putb(const void *memory, size_t mlen)
int el_print(const char *file, size_t line, const char *func, enum el_level level, const char *fmt, ...)
int el_vprint(const char *file, size_t line, const char *func, enum el_level level, const char *fmt, va_list ap)
int el_perror(const char *file, size_t line, const char *func, enum el_level level, const char *fmt, ...)
int el_pmemory(const char *file, size_t line, const char *func, enum el_level level, const void *memory, size_t mlen)
int el_pmemory_table(const char *file, size_t line, const char *func, enum el_level level, const void *memory, size_t mlen)
int el_pbinary(enum el_level level, const void *memory, size_t mlen)
int el_flush(void)
const struct el *el_get_el(void)

Each functions has its equivalent function that accepts el object as argument. This is helpful if we want to have more than one, separated loggers in a single program. Please see el_option(3) for more information.

int el_oinit(struct el *el)
int el_ocleanup(struct el *el)
struct el * el_new(void)
int el_destroy(struct el *el)
int el_ooption(struct el *el, enum el_option option, ...)
int el_oputs(struct el *el, const char *string)
int el_oputb(struct el *el, const void *memory, size_t mlen)
int el_oprint(const char *file, size_t line, const char *func, enum el_level level, struct el *el, const char *fmt, ...)
int el_ovprint(const char *file, size_t line, const char *func, enum el_level level, struct el *el, const char *fmt, va_list ap)
int el_operror(const char *file, size_t line, const char *func, enum el_level level, struct el *el, const char *fmt, ...)
int el_opmemory(const char *file, size_t line, const char *func, enum el_level level, struct el *el, const void *memory, size_t mlen)
int el_opmemory_table(const char *file, size_t line, const char *func, enum el_level level, struct el *el, const void *memory, size_t mlen)
int el_opbinary(enum el_level level, struct el *el, const void *memory, size_t mlen)
int el_oflush(struct el *el)

For more information about a function open manual page with functions name from section 3 (ie. for el_oputs, you'd open el_oputs(3))

Every function that accepts 3 parameters level, file and line as its first arguments, can accept short macro that injects proper values into function. Awailable macros are:

ELF Fatal errors, usually precedes application crash
ELA Alert, vey major error that should be fixed as soon as possible
ELC Critical
ELE Error
ELW Warning
ELN Normal log, but of high importance
ELI Information message, shouldn't spam too much here
ELD Debug messages, can spam as much as you'd like

So instead of calling

    el_print(__FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, "Notice message");

You can simply call it like

    el_print(ELN, "Notice message");

There are also equivalent macros for use with functions that also provides el object, so the can be used with el_o function family. To make these work, you need to provide EL_OPTIONS_OBJECT macro. Check el_print(3) for more info about that.

OELF Fatal errors, usually precedes application crash
OELA Alert, vey major error that should be fixed as soon as possible
OELC Critical
OELE Error
OELW Warning
OELN Normal log, but of high importance
OELI Information message, shouldn't spam too much here
OELD Debug messages, can spam as much as you'd like

So instead of calling

    el_oprint(__FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, &g_log_object, "Notice message");
or
    el_oprint(ELN, &g_log_object, "Notice message");

You can simply call it like

    el_oprint(OELN, "Notice message");
 

STABLE ABI CONSIDERATION

Since embedlog is versatile and can work on both hardcore embedded systems (nuttx, freertos, bare metals) and on big CPUs that can run Linux, library must provide a way for user to use stack-allocated objects and stable ABI. Unfortunately there is no way to provide both at the same time, so embedlog provides two solutions so everybody is happy.

Note that library versioning follows STABLE ABI way of using library. So if feature is added that adds new field to struct el, then this will not be treated as ABI breakage, because if you are using el_new(3) or el_init(3) functions, ABI will not be broken. Only when using el_oinit(3) ABI will be broken.  

STABLE ABI

When stable ABI is required, user must initialize embedlog either with el_init(3) or el_new(3) function. User also must not access internal fields directly and all operations on el object must be done through the API functions. el_new(3) will malloc necessary memory for the user which must be freed with el_destroy(3) function. Under no circumstances use sizeof on the object nor struct. Operation will succeed, but may return invalid results when library is updated. To put it in as few word as possible - you don't know anything about internals of struct el.  

STACK ALLOCATED OBJECT

When you use library in hardcore embedded environment, where there is not dynamic linking involved, you are save to ignore any ABI changes, since you always build your programs alongside the library. But thanks to that you can avoid malloc() call and allocate everything on the stack. To allocate object on stack you must use el_oinit(3) function, and deinitialize object with el_ocleanup(3).

If you are using el_oinit(3) on systems with dynamic library support you are suscible to ABI breakage and undefined behaviour when only new features are added, or even when bugfix introduces new field to struct el. Heck, even recompilation with different flags can break ABI here. Do not use this approach if your system have dynamic library support unless you can rebuild all programs against new version of library.

You've been warned!  

EXAMPLE

Initial setup is very trivial. One should call init function and it will print to stderr by default. Output can also be customized with proper el object, see el_option(3) for more details.

    #include <embedlog.h>

    int main(void)
    {
        /* initialize library, default output is stderr */
        el_init();

        /* print message with info severity */
        el_print(ELI, "answer is %d", 42);

        /* clean after ourselfs */
        el_cleanup();

        return 0;
    }
 

SEE ALSO

el_cleanup(3), el_destroy(3), el_flush(3), el_init(3), el_new(3), el_ocleanup(3), el_oflush(3), el_oinit(3), el_ooption(3), el_operror(3), el_opmemory(3), el_opmemory_table(3), el_oprint(3), el_option(3), el_oputs(3), el_ovprint(3), el_perror(3), el_pmemory(3), el_pmemory_table(3), el_print(3), el_puts(3), el_vprint(3).

bofc.pl

25 January 2021 (v0.6.0)

el_overview(7)