el_print(3)

bofc manual pages

el_print(3)



 

NAME

el_print, el_vprint, el_puts, el_pmemory, el_pmemory_table, el_perror - prints message to previously configured outputs.  

SYNOPSIS

#include <embedlog.h>

int el_puts(const char *message)
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_oputs(struct el *el, const char *message)
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)

#define ELF __FILE__, __LINE__, EL_FUNC_NAME, EL_FATAL
#define ELA __FILE__, __LINE__, EL_FUNC_NAME, EL_ALERT
#define ELC __FILE__, __LINE__, EL_FUNC_NAME, EL_CRIT
#define ELE __FILE__, __LINE__, EL_FUNC_NAME, EL_ERROR
#define ELW __FILE__, __LINE__, EL_FUNC_NAME, EL_WARN
#define ELN __FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE
#define ELI __FILE__, __LINE__, EL_FUNC_NAME, EL_INFO
#define ELD __FILE__, __LINE__, EL_FUNC_NAME, EL_DBG

#define OELF __FILE__, __LINE__, EL_FUNC_NAME, EL_FATAL, EL_OPTIONS_OBJECT
#define OELA __FILE__, __LINE__, EL_FUNC_NAME, EL_ALERT, EL_OPTIONS_OBJECT
#define OELC __FILE__, __LINE__, EL_FUNC_NAME, EL_CRIT, EL_OPTIONS_OBJECT
#define OELE __FILE__, __LINE__, EL_FUNC_NAME, EL_ERROR, EL_OPTIONS_OBJECT
#define OELW __FILE__, __LINE__, EL_FUNC_NAME, EL_WARN, EL_OPTIONS_OBJECT
#define OELN __FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, EL_OPTIONS_OBJECT
#define OELI __FILE__, __LINE__, EL_FUNC_NAME, EL_INFO, EL_OPTIONS_OBJECT
#define OELD __FILE__, __LINE__, EL_FUNC_NAME, EL_DBG, EL_OPTIONS_OBJECT

#define EL_DEBUG(...) el_print(ELD, __VA_ARGS__)

Feature Test Macro

#define EL_DEBUG()

__ISOC99_SOURCE
 

DESCRIPTION

Functions print log into configured outputs.

el_puts(3) function prints message that is simple string, message is not altered in anyway - that means no automatic log levels, location of the log nor timestamp is printed - even if they are enabled. Logs printed with this functions are not filtered by log level, and will always be printed. Prints can still be suppressed by disabling output.

el_putb(3) function works same as el_puts(3) but binary data can be passed. That binary data will not be printed in ASCII like it it with el_pmemory(3). If memory contains NULL characters, they will be stored to file. If memory is for example char buf[] = { 0xb0, 0xfc, 0x13, 0x37 } then exactly these bytes will be stored into file.

el_print(3) function behaves similar to standard printf() function from the standard library, but it adds (if enabled) additional information, such as file name, line number or func name from where log has been printed, log level, and timestamp of the message. fmt and variadic variables ... have the same role as it is described in printf() manual page.

el_vprint(3) function behaves just like el_print(3) but it accepts argument pointer ap instead of variadic variables. Check vprintf() for more information.

el_perror(3) works the same way as el_print(3) but will also print information about current errno that was set. If fmt is NULL, only information about errno will be printed. It is similar to perror() function. It is guaranteed that el_perror(3) functions will not modify errno value.

el_pmemory(3) is designed to print memory location in a nice hex+ascii format. Function accepts pointer to memory and mlen number of bytes caller wants to print. If mlen is 0, function won't print anything and will return EINVAL error. Function doesn't care what is under passed address, memory block may contain strings with null characters '\0', integers, floats or even struct objects.

Example output can look like this. We print all ascii table, to present printing of printable and non-printable values.

    0x0000  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f  ................
    0x0010  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f  ................
    0x0020  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f   !"#$%&'()*+,-./
    0x0030  30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
    0x0040  40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
    0x0050  50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[]^_
    0x0060  60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno
    0x0070  70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.

el_pmemory_table(3) works just like el_pmemory(3) but also prints table-like header and footer.

    ------  -----------------------------------------------  ----------------
    offset  hex                                              ascii
    ------  -----------------------------------------------  ----------------
    0x0000  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f  ................
    0x0010  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f  ................
    0x0020  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f   !"#$%&'()*+,-./
    0x0030  30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
    0x0040  40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
    0x0050  50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[]^_
    0x0060  60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno
    0x0070  70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.
    ------  -----------------------------------------------  ----------------

el_pbinary(3) is used to log binary data into block device. Both metadata and data itself are treated as binary data. Such files cannot be read with text tools like less, but rather needs special program that will parse the file. This may be usefull when you need, for example to log every message on CAN bus. If there are a lot of messages, it may be better to store them in binary format as this may save *a lot* of space.

el_pbinary(3) uses specific format to store metadata to save as much space as possible. All numerical values in metadata are variadic in size. Order of fields are as follows:

    +-------+------------+--------------+-------------+------+
    | flags | ts_seconds | ts_fractions | data_length | data |
    +-------+------------+--------------+-------------+------+

The only mandatory fields are flags, data_length and data. flags determin what fields are present. flags field is always 1 byte in size and its format is

    +------+-------+-----------------------------------------------------------+
    | bits | value | description                                               |
    +------+-------+-----------------------------------------------------------+
    |    0 |   0   | both ts_seconds and ts_fraction will not appear           |
    |      +-------+-----------------------------------------------------------+
    |      |   1   | at least ts_seconds will appear, ts_fraction appearance   |
    |      |       | depends on 1..2 bits values                               |
    +------+-------+-----------------------------------------------------------+
    | 1..2 |   0   | ts_fractions will not appear                              |
    |      +-------+-----------------------------------------------------------+
    |      |   1   | ts_fractions will hold milliseconds value                 |
    |      +-------+-----------------------------------------------------------+
    |      |   2   | ts_fractions will hold microseconds value                 |
    |      +-------+-----------------------------------------------------------+
    |      |   3   | ts_fractions will hold nanoseconds value                  |
    +------+-------+-----------------------------------------------------------+
    | 3..5 |  0..7 | severity of the log, 0 is the highest and 7 is the lowest |
    +------+-------+-----------------------------------------------------------+
    | 6..7 |  0..3 | reserved                                                  |
    +------+-------+-----------------------------------------------------------+

ts_seconds, ts_fractions and data_length are numerical values with dynamic size. Each byte of a numerical value can hold number up to 127 and oldest bit is used as continuation bit, if that bit is set, program should treat next byte as next part of the same numerical value. Below is table with example decimal values and it's encoded counterpart.

    +---------------+--------------------------+
    | decimal value | encoded hex value        |
    +---------------+--------------------------+
    |             0 | 0x00                     |
    |             1 | 0x01                     |
    |             2 | 0x02                     |
    |           127 | 0x7f                     |
    |           128 | 0x80 0x01                |
    |           129 | 0x81 0x01                |
    |           255 | 0xff 0x01                |
    |           256 | 0x80 0x02                |
    |           257 | 0x81 0x02                |
    |         16383 | 0xff 0x7f                |
    |         16384 | 0x80 0x80 0x01           |
    |         16385 | 0x81 0x80 0x01           |
    |     438478374 | 0xa6 0xcc 0x8a 0xd1 0x01 |
    |    2147483647 | 0xff 0xff 0xff 0xff 0x07 |
    |    4294967295 | 0xff 0xff 0xff 0xff 0x0f |
    +---------------+--------------------------+

Encoded number are always little-endian, that is first byte is always the least significant byte.

data is be whatever you want of any size. el_pbinary(3) uses only timestamp and log level, rest of the options in el are simply ignores.

el_print(3), el_vprint(3), el_perror(3), el_pmemory_table(3) and el_pmemory(3) are filtered based on their level.

All of above functions have their counterpart that also accepts custom el object. This is useful if we want for example, print logs into one file, and program queries into another. It can also be used to print every level into different file.

When user defines EL_OPTIONS_OBJECT macros with OEL* can be used to save yourself the trouble to type &opt_obj each time. This macro should be defined to what normally would be passed to el_oprint functions familly. It usually is used with extern keyword like

    extern struct el  log_foobar;
    #define EL_OPTIONS_OBJECT &log_foobar

It is real pain in the ass to provide information about file in every print function. For that matter very simple macros have been provided.

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

These macros comprese first 3 parameters file, line and level into single, short parameter. For example instead calling this

    el_print(__FILE__, __LINE__, EL_FUNC_NAME, EL_NOTICE, "notice message number %d", num);

you can simply call

    el_print(ELN, "notice message number %d", num);

This solution works both for c89 and c99 standards. All side effects (like from calling function or incrementing variable in print function call) will take effect always regardless of set logging level. Also strings used in functions will be compiled and put into binary - always. If user defines NOFINFO in his project, all __FILE__ , __LINE__ and EL_FUNC_NAME will be unconditionally set to NULL and 0 and no file information will be printed - even if it was enabled with el_option(3).

If user has access to c99 compiler, one can use EL_DEBUG for debuging messages only. Later when application is compiled with NDEBUG, all side effects as well as strings used will be stripped out from final binary - so no vital information will be leaked in such way. This is not possible on c89 compiller due to absence of variadic variables in preprocesor macros.

EL_FUNC_NAME will work only on c99 compilers, where __func__ is defined, on c89 compilers function name won't be printed, and setting EL_FUNCINFO won't help it.  

RETURN VALUE

All functions return 0 when whole message has been successfuly printed to all configured outputs. If message couldn't be printed, it was printed only partially, or was not send to at least one configured output -1 is returned. Note that only one error is returned even if there was multiple errors.  

ERRORS

All functions may return one of these on error

EINVAL
Any of the input parameters is invalid.
EBADF
Loggig to file is enabled and filename was not set with EL_FPATH option
EBADF
Logging to file is enabled, file was opened sucessfuly, but log cannot be stored into file. This usually happen that file was unlinked from the file system and embedlog couldn't create new file again (no access to directory or directory doesn't exist at all). Log is lost, but embedlog will try to recreate log file everytime el_print(3) is called.
ENODEV
All possible outputs are disabled

el_print(3), el_vprint(3), el_perror(3), el_pmemory_table(3) and el_pmemory(3) may also return:

ERANGE
Message will not be logged as message log level is lower than configured one.
ENOBUFS
Message is bigger than EL_LOG_MAX and will be truncated.

When logging to file is enabled, all functions may also return errors from fwrite() and if file rotation is enabled also from fopen()  

SEE ALSO

el_overview(7), 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_option(3).

bofc.pl

25 January 2021 (v0.6.0)

el_print(3)