.TH "el_overview" "7" "28 February 2019 (v0.5.0)" "bofc.pl" .SH NAME .PP .B el_overview - quick overview of .B embedlog logging library .SH SYNOPSIS .PP .B embedlog - is a highly portable .B c89 complaint logger (with additional features for users with .B c99 compilers and/or .B POSIX systems). This library is designed mainly for embedded devices, but can also be used in high level OSes like .BR Linux . .SH DESCRIPTION .PP Logger incorporates features like: .PP .RS - printing to different outputs (simultaneously) like: .RS - stderr .br - syslog (very limited, works on *nuttx* for now) .br - directly to serial device (like /dev/ttyS0) .br - file (with optional rotating and syncing to prevent data loss) .br - automatic file reopening on unexpected events (file deletion, SD remount) .br - custom routine, can be anything, embedlog just calls your function with string to print .RE - timestamping using following clocks: .RS - clock_t .br - time_t .br - CLOCK_REALTIME (requires POSIX) .br - CLOCK_MONOTONIC (requires POSIX) .br - configurable precision of fraction of seconds (mili, micro, nano) .RE - printing file and line information .br - 8 predefined log levels (total rip off from syslog(2) levels) .br - colorful output (for easy error spotting) .br - print memory block in wireshark-like output .br - fully binary logs with binary data (like CAN frames) to save space .RE .RE .PP Library implements following functions: .PP .BI "int el_init(void)" .br .BI "int el_cleanup(void)" .br .BI "int el_option(enum el_option " option ", ...)" .br .BI "int el_puts(const char *" string ")" .br .BI "int el_putb(const void *" memory ", size_t " mlen ")" .br .BI "int el_print(const char *" file ", size_t " line ", const char *" func ", \ enum el_level " level ", const char *" fmt ", " ... ")" .br .BI "int el_vprint(const char *" file ", size_t " line ", const char *" func ", \ enum el_level " level ", const char *" fmt ", va_list " ap ")" .br .BI "int el_perror(const char *" file ", size_t " line ", \ const char *" func ", enum el_level " level ", const char *" fmt ", " ... ")" .br .BI "int el_pmemory(const char *" file ", size_t " line ", \ const char *" func ", enum el_level " level ", const void *" memory ", \ size_t " mlen ") .br .BI "int el_pmemory_table(const char *" file ", size_t " line ", \ const char *" func ", enum el_level " level ", const void *" memory ", \ size_t " mlen ") .br .BI "int el_pbinary(enum el_level " level ", const void *" memory ", \ size_t " mlen ") .br .BI "int el_flush(void)" .br .B const struct el *el_get_el(void) .PP Each functions has its equivalent function that accepts .I el object as argument. This is helpful if we want to have more than one, separated loggers in a single program. Please see .BR el_option (3) for more information. .PP .BI "int el_oinit(struct el *" el ")" .br .BI "int el_ocleanup(struct el *" el ")" .br .B struct el * el_new(void) .br .BI "int el_destroy(struct el *" el ")" .br .BI "int el_ooption(struct el *" el ", enum el_option " option ", \ \&...)" .br .BI "int el_oputs(struct el *" el ", const char *" string ")" .br .BI "int el_oputb(struct el *" el ", const void *" memory ", \ size_t " mlen ")" .br .BI "int el_oprint(const char *" file ", size_t " line ", \ const char *" func ", enum el_level " level ", struct el *" el ", \ const char *" fmt ", " ... ")" .br .BI "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 ")" .br .BI "int el_operror(const char *" file ", size_t " line ", \ const char *" func ", enum el_level " level ", struct el *" el ", \ const char *" fmt ", " ... ")" .br .BI "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 ")" .br .BI "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 ")" .br .BI "int el_opbinary(enum el_level " level ", struct el *" el ", \ const void *" memory ", size_t " mlen ")" .br .BI "int el_oflush(struct el *" el ")" .PP For more information about a function open manual page with functions name from section 3 (ie. for el_oputs, you'd open .BR el_oputs (3)) .PP Every function that accepts 3 parameters .IR level , .I file and .I line as its first arguments, can accept short macro that injects proper values into function. Awailable macros are: .PP .RS .BR ELF " Fatal errors, usually precedes application crash" .br .BR ELA " Alert, vey major error that should be fixed as soon as possible" .br .BR ELC " Critical" .br .BR ELE " Error" .br .BR ELW " Warning" .br .BR ELN " Normal log, but of high importance" .br .BR ELI " Information message, shouldn't spam too much here" .br .BR ELD " Debug messages, can spam as much as you'd like" .RE .PP So instead of calling .PP .nf el_print(__FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, "Notice message"); .fi .PP You can simply call it like .PP .nf el_print(ELN, "Notice message"); .fi .PP There are also equivalent macros for use with functions that also provides .I el object, so the can be used with .B el_o function family. To make these work, you need to provide .B EL_OPTIONS_OBJECT macro. Check .BR el_print (3) for more info about that. .PP .RS .BR OELF " Fatal errors, usually precedes application crash" .br .BR OELA " Alert, vey major error that should be fixed as soon as possible" .br .BR OELC " Critical" .br .BR OELE " Error" .br .BR OELW " Warning" .br .BR OELN " Normal log, but of high importance" .br .BR OELI " Information message, shouldn't spam too much here" .br .BR OELD " Debug messages, can spam as much as you'd like" .RE .PP So instead of calling .PP .nf el_oprint(__FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, &g_log_object, "Notice message"); .fi or .nf el_oprint(ELN, &g_log_object, "Notice message"); .fi .PP You can simply call it like .PP .nf el_oprint(OELN, "Notice message"); .fi .SH "STABLE ABI CONSIDERATION" .PP Since .B 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 .B embedlog provides two solutions so everybody is happy. .PP Note that library versioning follows .B STABLE ABI way of using library. So if feature is added that adds new field to .BR "struct el", then this will not be treated as ABI breakage, because if you are using .BR el_new (3) or .BR el_init (3) functions, ABI will not be broken. Only when using .BR el_oinit (3) ABI will be broken. .SS "STABLE ABI" .PP When stable ABI is required, user must initialize .B embedlog either with .BR el_init (3) or .BR el_new (3) function. User also must not access internal fields directly and all operations on .I el object must be done through the API functions. .BR el_new (3) will malloc necessary memory for the user which must be freed with .BR el_destroy (3) function. Under no circumstances use .B 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 .BR "struct el" . .SS "STACK ALLOCATED OBJECT" .PP 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 .BR malloc () call and allocate everything on the stack. To allocate object on stack you must use .BR el_oinit (3) function, and deinitialize object with .BR el_ocleanup (3). .PP If you are using .BR 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 .BR "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. .PP .B You've been warned! .SH EXAMPLE .PP 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 .BR el_option (3) for more details. .PP .nf #include 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; } .fi .SH SEE ALSO .PP .BR el_cleanup (3), .BR el_destroy (3), .BR el_flush (3), .BR el_init (3), .BR el_new (3), .BR el_ocleanup (3), .BR el_oflush (3), .BR el_oinit (3), .BR el_ooption (3), .BR el_operror (3), .BR el_opmemory (3), .BR el_opmemory_table (3), .BR el_oprint (3), .BR el_option (3), .BR el_oputs (3), .BR el_ovprint (3), .BR el_perror (3), .BR el_pmemory (3), .BR el_pmemory_table (3), .BR el_print (3), .BR el_puts (3), .BR el_vprint (3).