/* * Pinelog lightweight logging library * * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) * * SPDX-License-Identifier: MIT */ /** * @file logging.h * @brief Logging utility library * * This file contains the prototypes for the pinelog logging library * used by any programs that need to log messages. * * @author Nirenjan Krishnan (nirenjan@nirenjan.org) */ #ifndef LOGGING_H #define LOGGING_H #include #ifndef PINELOG_TEST #include #endif #ifdef __cplusplus extern "C" { #endif /** * @brief Logging levels * * The log levels indicate the lowest severity level that will actually be * logged to the logging framework. */ enum { /** * In modules, it will defer to the global level. * Equivalent to \ref PINELOG_LVL_NONE for the global level */ PINELOG_LVL_NOTSET = -2, /** No messages will be logged */ PINELOG_LVL_NONE = -1, /** Only fatal messages will be logged */ PINELOG_LVL_FATAL, /** Error messages. This is the default log level */ PINELOG_LVL_ERROR, /** Warning messages */ PINELOG_LVL_WARNING, /** Informational messages */ PINELOG_LVL_INFO, /** Debug messages */ PINELOG_LVL_DEBUG, /** Trace messages */ PINELOG_LVL_TRACE, }; /** * @brief Set the default log level and output stream */ void pinelog_set_defaults(void); /** * @brief Initialize pinelog to have these many modules * * @param[in] num_modules Maximum number of modules * * @returns 0 on success, errno on failure. */ int pinelog_init(int num_modules); /** * @brief Save the current module name * * @param[in] module Module identifier * @param[in] name Module name - displayed in logs * * @returns 0 on success, errno on failure. */ int pinelog_setup_module(int module, const char *name); /** * @brief Close the output stream and terminate the logs */ void pinelog_close_output_stream(void); #ifdef PINELOG_TEST /** * @brief Get the pointer to the output stream. Only used in test harness. * * @returns FILE pointer to output stream */ FILE * pinelog_get_output_stream(void); #endif /** * @brief Set the output stream. Must be a FILE pointer. * * @param[in] stream Pointer to the output stream * * @returns 0 on success, EINVAL if the pointer is not valid. */ int pinelog_set_output_stream(FILE *stream); /** * @brief Set the output file. * * @param[in] file Filename to write to * * @returns 0 on success, EINVAL if the filename pointer is not valid, other * error if the file could not be opened for writing. */ int pinelog_set_output_file(const char *file); /** * @brief Set the global logging level * * @param[in] level Level to filter * * @returns 0 on success, EINVAL if the level is not valid */ int pinelog_set_level(int level); /** * @brief Get the global logging level * * @returns the configured global logging level */ int pinelog_get_level(void); /** * @brief Set the per-module logging level * * @param[in] module Module identifier * @param[in] level Level to filter * * @returns 0 on success, EINVAL if the level or module identifier is not valid */ int pinelog_set_module_level(int module, int level); /** * @brief Get the per-module logging level * * @param[in] module Module identifier * * @returns the configured per-module logging level, or the global logging level * if the module identifier is outside the configured range. */ int pinelog_get_module_level(int module); /** * @brief Log a message to the logger * * This is the actual function that logs the message. The application should * never need to call this directly, but instead, should always use the * \code PINELOG_* macros. * * @param[in] module Module identifier * @param[in] level Level to log the message at * @param[in] fmt Format string * * @returns None */ #if defined __has_attribute # if __has_attribute(format) __attribute__((format(printf, 5, 6))) # endif #endif void pinelog_log_message(int module, int level, const char *file, int line, const char *fmt, ...); // Test harness will redefine pinelog_exit #ifndef PINELOG_TEST #define pinelog_exit exit #endif // Base filename #if defined __has_builtin # if __has_builtin(__builtin_strrchr) # define PINELOG_FILE __builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__ # endif #endif #ifndef PINELOG_FILE # define PINELOG_FILE __FILE__ #endif // Global module - used for generic logging #define PINELOG_MODULE_GLOBAL -1 #ifndef PINELOG_MODULE #define PINELOG_MODULE PINELOG_MODULE_GLOBAL #endif #define PINELOG_FATAL(fmt, ...) do { \ if (PINELOG_LVL_FATAL <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_FATAL, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ pinelog_exit(1); \ } while (0) #define PINELOG_ERROR(fmt, ...) do { \ if (PINELOG_LVL_ERROR <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_ERROR, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ } while (0) #define PINELOG_WARN(fmt, ...) do { \ if (PINELOG_LVL_WARNING <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_WARNING, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ } while(0) #define PINELOG_INFO(fmt, ...) do { \ if (PINELOG_LVL_INFO <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_INFO, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ } while(0) #define PINELOG_DEBUG(fmt, ...) do { \ if (PINELOG_LVL_DEBUG <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_DEBUG, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ } while(0) /* PINELOG_DISABLE_TRACE allows all traces to be compiled out */ #ifndef PINELOG_DISABLE_TRACE #define PINELOG_TRACE(fmt, ...) do { \ if (PINELOG_LVL_TRACE <= pinelog_get_module_level(PINELOG_MODULE)) { \ pinelog_log_message(PINELOG_MODULE, PINELOG_LVL_TRACE, PINELOG_FILE, __LINE__, fmt, ##__VA_ARGS__); \ } \ } while(0) #else #define PINELOG_TRACE(fmt, ...) do { } while(0) #endif #ifdef __cplusplus } #endif #endif // !defined LOGGING_H