nuttx-apps/examples/embedlog/embedlog_main.c
Michał Łyszczek d17f554355 examples/embedlog: update example for embedlog-v0.7.0
Signed-off-by: Michał Łyszczek <michal.lyszczek@bofc.pl>
2024-03-03 12:03:54 +08:00

349 lines
13 KiB
C

/****************************************************************************
* apps/examples/embedlog/embedlog_main.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <logging/embedlog.h>
#include <errno.h>
#include <nuttx/config.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
/****************************************************************************
* 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 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 isn't 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_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);
/* nuttx runs on rather slow cpus, so millisecond precision is enough */
el_ooption(&g_el, EL_TS_FRACT, EL_TS_FRACT_MS);
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));
el_ooption(&g_el, EL_PMEMORY_SPACE, 1);
el_oprint(OELI, "print whole ASCII table with additional spacing");
el_opmemory(OELI, ascii, sizeof(ascii));
}
/****************************************************************************
* 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 */
snprintf(log_path, sizeof(log_path), "%s/log-rotate", workdir);
el_ooption(&g_el, EL_FILE_SYNC_LEVEL, EL_CRIT);
/* Enable logging to file with file rotation based on file size.
* 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.
* This function will also automatically enable file output without
* disabling any - already enabled - outputs.
*/
if (el_oenable_file_log(&g_el, log_path, 5, 512))
{
if (errno == ENAMETOOLONG || errno == EINVAL)
{
/* These are the only unrecoverable errors 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;
}
/* 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.
*
* Setting that 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 losing 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_oset_file_sync(&g_el, 4096, EL_CRIT);
/* 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 and");
el_oprint(OELI, "if library creates EL_FROTATE_NUMBER files oldest");
el_oprint(OELI, "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 [<log-directory>]\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_oprint(OELI, "You can enable most common and usefull options with just "
"a few special functions");
el_oset_timestamp(&g_el, EL_TS_LONG, EL_TS_TM_REALTIME, EL_TS_FRACT_US);
el_oprint_extra_info(&g_el, 1);
el_oprint(OELI, "timestamp and information about log place in code is "
"most usefull for developer");
el_oprint(OELI, "If these are not enough, you can always really fine");
el_oprint(OELI, "tune embedlog behaviour with options");
el_oset_timestamp(&g_el, EL_TS_OFF, EL_TS_TM_REALTIME, EL_TS_FRACT_OFF);
el_oprint_extra_info(&g_el, 0);
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;
}