/**************************************************************************** * examples/embedlog/embedlog_main.c * * Copyright (C) 2019 Michał Łyszczek. All rights reserved. * Author: Michał Łyszczek * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include /**************************************************************************** * Preprocessor macros ****************************************************************************/ /* Macro used by OEL* macros, so we don't have to specify &g_el every time * we want to print something. */ #define EL_OPTIONS_OBJECT &g_el /**************************************************************************** * Global variables ****************************************************************************/ /* Logger state, in flat build you need to create embedlog object and use it * with el_o* functions. It is so, because embedlog defines one global object * to make it easier to use embedlog on systems with MMU where each thread * has it's own global memory. In nuttx in flat build that single object is * shared between *all* tasks. You could probably get away with this if you * use embedlog only in one task, but it's usually not the case. So in nuttx * always create embedlog object. You can define EL_OPTIONS_OBJECT macro and * use OEL* macros to make it almost as easy as using embedlog with global * object. see el_options(3) to see learn more about it. */ static struct el_options g_el; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: el_print_options * * Description: * Presents how to use options and how it's rendered. This assumes all * options are enabled in compile time. If any of the options is not enabled * in compile time, setting it will have no effect and logs will vary. * * Input Parameters: * None. * * Returned Value: * None. * ****************************************************************************/ static void el_print_options(void) { /* We will be printing logs to standard error output only */ el_ooption(&g_el, EL_OUT, EL_OUT_STDERR); el_ooption(&g_el, EL_PRINT_LEVEL, 0); el_oprint(OELI, "We can disable information about log level"); el_oprint(OELF, "message still will be filtered by log level"); el_oprint(OELA, "but there is no way to tell what level message is"); el_oprint(OELD, "like this message will not be printed"); el_ooption(&g_el, EL_TS, EL_TS_SHORT); el_oprint(OELF, "As every respected logger, we also have timestamps"); el_oprint(OELF, "which work well with time from time()"); el_ooption(&g_el, EL_TS_TM, EL_TS_TM_MONOTONIC); el_oprint(OELF, "or CLOCK_MONOTONIC from POSIX"); el_ooption(&g_el, EL_TS, EL_TS_LONG); el_ooption(&g_el, EL_TS_TM, EL_TS_TM_TIME); el_oprint(OELF, "we also have long format that works well with time()"); el_ooption(&g_el, EL_TS_TM, EL_TS_TM_REALTIME); el_oprint(OELF, "if higher precision is needed we can use CLOCK_REALTIME"); el_ooption(&g_el, EL_TS, EL_TS_SHORT); el_oprint(OELF, "we can also mix REALTIME with short format"); el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_OFF); el_oprint(OELF, "and if you don't need high resolution"); el_oprint(OELF, "you can disable fractions of seconds to save space!"); el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_MS); el_oprint(OELF, "or enable only millisecond resolution"); el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_US); el_oprint(OELF, "or enable only microsecond resolution"); el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_NS); el_oprint(OELF, "or enable only nanosecond resolution " "(not that it makes much sense on nuttx)"); el_ooption(&g_el, EL_TS, EL_TS_LONG); el_ooption(&g_el, EL_TS, EL_TS_OFF); el_oprint(OELF, "no time information, if your heart desire it"); el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_NS); el_ooption(&g_el, EL_FINFO, 1); el_oprint(OELF, "log location is very useful for debugging"); el_ooption(&g_el, EL_TS, EL_TS_LONG); el_ooption(&g_el, EL_TS_TM, EL_TS_TM_REALTIME); el_ooption(&g_el, EL_PRINT_LEVEL, 1); el_oprint(OELF, "Different scenarios need different options"); el_oprint(OELA, "So we can mix options however we want"); el_ooption(&g_el, EL_PRINT_NL, 0); el_oprint(OELF, "you can also remove printing new line "); el_oputs(&g_el, "to join el_oprint and el_oputs in a single "); el_oputs(&g_el, "long line as needed\n"); el_ooption(&g_el, EL_PRINT_NL, 1); el_ooption(&g_el, EL_COLORS, 1); el_ooption(&g_el, EL_LEVEL, EL_DBG); el_oprint(OELD, "And if we have"); el_oprint(OELI, "modern terminal"); el_oprint(OELN, "we can enable colors"); el_oprint(OELW, "to spot warnings"); el_oprint(OELE, "or errors"); el_oprint(OELC, "with a quick"); el_oprint(OELA, "glance into"); el_oprint(OELF, "log file"); el_ooption(&g_el, EL_PREFIX, "embedlog: "); el_oprint(OELI, "you can also use prefixes"); el_oprint(OELI, "to every message you send"); el_ooption(&g_el, EL_PREFIX, NULL); el_oprint(OELI, "set prefix to null to disable it"); } /**************************************************************************** * Name: el_print_memory * * Description: * Presents how to print blob of memory. * * Input Parameters: * None. * * Returned Value: * None. * ****************************************************************************/ static void el_print_memory(void) { char s[] = "some message\0that contains\0null characters"; char ascii[128]; int i; for (i = 0; i != 128; ++i) { ascii[i] = (char)i; } /* We will be printing logs to standard error output only */ el_ooption(&g_el, EL_OUT, EL_OUT_STDERR); el_oprint(OELI, "print whole ASCII table"); el_opmemory(OELI, ascii, sizeof(ascii)); el_oprint(OELI, "print memory region that contains string with NULL " "chars"); el_opmemory(OELI, s, sizeof(s)); el_oprint(OELI, "print the same region but this time with nice ascii " "table"); el_opmemory_table(OELI, s, sizeof(s)); } /**************************************************************************** * Name: el_print_file * * Description: * Presents how to print data to file with log rotation * * Input Parameters: * workdir - directory where logs will be stored. * * Returned Value: * None. * ****************************************************************************/ static void el_print_file(const char *workdir) { char log_path[PATH_MAX]; /* create user specified directory if it doesn't exist */ if (mkdir(workdir, 0755) != 0 && errno != EEXIST) { fprintf(stderr, "Couldn't create %s, error %s\n", workdir, strerror(errno)); return; } /* Create full path to log file embedlog will use */ sprintf(log_path, "%s/log-rotate", workdir); /* Enable file rotation, maximum 5 files will be created, none of the log * files size shall exceed 512 bytes. Rotate size is low to present how * rotation works, in real life this should be much higher, unless you * really know what you are doing and understand the consequences of low * rotate size. */ el_ooption(&g_el, EL_FROTATE_NUMBER, 5); el_ooption(&g_el, EL_FROTATE_SIZE, 512); /* Tell embedlog to sync all buffers and metadata regarding log file. * There are cases, when data (a lot of data) goes into block device, * but metadata of the file are not updated when power loss occurs. This * leads to data loss - even though they landed physically on block device * like sdcard. To prevent losing too much data, you can define how often * you want embedlog to call sync() functions. It basically translates to * "how much data am I ready to loose?". In this example, we sync() once * every 4096 bytes are stored into file. */ el_ooption(&g_el, EL_FILE_SYNC_EVERY, 4096); /* Setting above value to too small value, will cause sync() to be called * very often (depending on how much data you log) and that may criple * performance greatly. And there are situations when you are willing to * loose some info or debug data (up to configured sync every value), but * when critical error shows up, you want data to be synced immediately to * minimize chance of loosing information about critical error. For that * you can define which log level will be synced *every* time regardless * of sync every value. Here we will instruct embedlog to log prints that * have severity higher or equal to critical (so critial, alert and fatal) */ el_ooption(&g_el, EL_FILE_SYNC_LEVEL, EL_CRIT); /* Print logs to both file and standard error */ el_ooption(&g_el, EL_OUT, EL_OUT_FILE | EL_OUT_STDERR); /* Register path to log file in embedlog. This will cause logger to look * for next valid log file (when log rotate in enabled) and will try to * open file for reading. */ if (el_ooption(&g_el, EL_FPATH, log_path) != 0) { if (errno == ENAMETOOLONG || errno == EINVAL) { /* These are the only unrecoverable errors that embedlog may return, * any other error it actually a warning, telling user that file * could not have been opened now, but every el_print function with * output to file enabled, will try to reopen file. This of course * apply to situation when problem is temporary - like directory * does not exist but will be created after warning, or file has * no write permission but it gets fixed later. */ fprintf(stderr, "log file name too long"); return; } fprintf(stderr, "WARNING: couldn't open file: %s\n", strerror(errno)); return; } /* Now we can print messages */ el_oprint(OELI, "Now we enabled log rotation"); el_oprint(OELI, "If log cannot fit in current file"); el_oprint(OELI, "it will be stored in new file"); el_oprint(OELI, "and if library creates EL_FROTATE_NUMBER files"); el_oprint(OELI, "oldest file will be deleted and new file will be created"); el_oprint(OELI, "run this program multiple times and see how it works"); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * embedlog_main ****************************************************************************/ int main(int argc, FAR char *argv[]) { if (argc > 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { fprintf(stderr, "usage: %s []\n", argv[0]); return 1; } /* Initialize embedlog object to sane and known values */ el_oinit(&g_el); el_oprint(OELI, "Right after init, embedlog will print to stderr with just " "log level information - these are default settings."); el_print_options(); el_print_memory(); if (argc == 2) { el_print_file(argv[1]); } /* This will cleanup whatever has been allocated by el_oinit(), like opened * file descriptors */ el_ocleanup(&g_el); return 0; }