Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
// File: kjmj_log.h // SPDX-License-Identifier: Unlicensed // This is free and unencumbered software released into the public domain. /*! * \file kjmj_log.h * * \brief This file is a \e thin wrapper around the C Macro Collections * \c log.h header file. * * Defines the macro functions \c LOG_INIT, \c LOG_TO_FILE, LOG_TO_TERMINAL, * and \c LOG_TERM. These macros initialize, setup, and terminate logging. * * Defines the macro values \c LOG_LEVEL_TRACE, \c LOG_LEVEL_DEBUG, * \c LOG_LEVEL_INFO, \c LOG_LEVEL_WARN, \c LOG_LEVEL_ERROR, and * \c LOG_LEVEL_FATAL. These macro values are used to setup logging to the * given level or higher (i.e., \c LOG_LEVEL_TRACE is the lowest level, and * \c LOG_LEVEL_FATAL is the highest level). * * Defines the logging macro functions \c LOG_TRACE, \c LOG_DEBUG, * \c LOG_INFO, \c LOG_WARN, \c LOG_ERROR, and \c WARN_FATAL. * * \version 1.0 * \date 2023 */ #ifndef KJMJ_LOG_H_ #define KJMJ_LOG_H_ #ifdef __cplusplus extern "C" { #endif #include
// for FILE and file-related functions, stdout, stderr #include
// for string manipulation functions #include
// for time_t, time, localtime #include
// for va_list, va_start, va_end, vfprintf #include
// for assert // ANSI color codes. #define ANSI_BLACK_FG "\e[0;30m" #define ANSI_RED_FG "\e[0;31m" #define ANSI_GREEN_FG "\e[0;32m" #define ANSI_YELLOW_FG "\e[0;33m" #define ANSI_BLUE_FG "\e[0;34m" #define ANSI_PURPLE_FG "\e[0;35m" #define ANSI_CYAN_FG "\e[0;36m" #define ANSI_WHITE_FG "\e[0;37m" #define ANSI_BOLD_BLACK_FG "\e[1;30m" #define ANSI_BOLD_RED_FG "\e[1;31m" #define ANSI_BOLD_GREEN_FG "\e[1;32m" #define ANSI_BOLD_YELLOW_FG "\e[1;33m" #define ANSI_BOLD_BLUE_FG "\e[1;34m" #define ANSI_BOLD_PURPLE_FG "\e[1;35m" #define ANSI_BOLD_CYAN_FG "\e[1;36m" #define ANSI_BOLD_WHITE_FG "\e[1;37m" #define ANSI_BRIGHT_BLACK_FG "\e[0;90m" #define ANSI_BRIGHT_RED_FG "\e[0;91m" #define ANSI_BRIGHT_GREEN_FG "\e[0;92m" #define ANSI_BRIGHT_YELLOW_FG "\e[0;93m" #define ANSI_BRIGHT_BLUE_FG "\e[0;94m" #define ANSI_BRIGHT_PURPLE_FG "\e[0;95m" #define ANSI_BRIGHT_CYAN_FG "\e[0;96m" #define ANSI_BRIGHT_WHITE_FG "\e[0;97m" #define ANSI_BOLD_BRIGHT_BLACK_FG "\e[1;90m" #define ANSI_BOLD_BRIGHT_RED_FG "\e[1;91m" #define ANSI_BOLD_BRIGHT_GREEN_FG "\e[1;92m" #define ANSI_BOLD_BRIGHT_YELLOW_FG "\e[1;93m" #define ANSI_BOLD_BRIGHT_BLUE_FG "\e[1;94m" #define ANSI_BOLD_BRIGHT_PURPLE_FG "\e[1;95m" #define ANSI_BOLD_BRIGHT_CYAN_FG "\e[1;96m" #define ANSI_BOLD_BRIGHT_WHITE_FG "\e[1;97m" #define ANSI_UNDERLINE_BLACK_FG "\e[4;30m" #define ANSI_UNDERLINE_RED_FG "\e[4;31m" #define ANSI_UNDERLINE_GREEN_FG "\e[4;32m" #define ANSI_UNDERLINE_YELLOW_FG "\e[4;33m" #define ANSI_UNDERLINE_BLUE_FG "\e[4;34m" #define ANSI_UNDERLINE_PURPLE_FG "\e[4;35m" #define ANSI_UNDERLINE_CYAN_FG "\e[4;36m" #define ANSI_UNDERLINE_WHITE_FG "\e[4;37m" #define ANSI_BLACK_BG "\e[40m" #define ANSI_RED_BG "\e[41m" #define ANSI_GREEN_BG "\e[42m" #define ANSI_YELLOW_BG "\e[43m" #define ANSI_BLUE_BG "\e[44m" #define ANSI_PURPLE_BG "\e[45m" #define ANSI_CYAN_BG "\e[46m" #define ANSI_WHITE_BG "\e[47m" #define ANSI_RESET "\e[0;0m" // Log colors. #ifndef ANSI_START_TRACE #define ANSI_START_TRACE ANSI_BRIGHT_WHITE_FG #endif #ifndef ANSI_START_DEBUG #define ANSI_START_DEBUG ANSI_BRIGHT_BLUE_FG #endif #ifndef ANSI_START_INFO #define ANSI_START_INFO ANSI_BRIGHT_CYAN_FG #endif #ifndef ANSI_START_WARN #define ANSI_START_WARN ANSI_BRIGHT_YELLOW_FG #endif #ifndef ANSI_START_ERROR #define ANSI_START_ERROR ANSI_BRIGHT_RED_FG #endif #ifndef ANSI_START_FATAL #define ANSI_START_FATAL ANSI_BRIGHT_PURPLE_FG #endif /*! * \enum kjmjLogLevelEnum * * \brief The possible logging levels. * * The log level \c KJMJ_LOG_LEVEL_FATAL can be used to specify that only the * most important (i.e., fatal) log messages shold be logged. Using this level * to specify the log level for \c kjmj_log_to_file, \c kjmj_log_to_stdout, or * \c kjmj_log_to_stderr means that only messages logged using the \c FATAL * macro will be logged. * * Conversely, the log level \c KJMJ_LOG_LEVEL_TRACE can be used to specify * that \em any logging message will be logged. * * The log level \c KJMJ_LOG_LEVEL_DISABLED can be used to specify that * logging should not occur. * * The log level \c KJMJ_LOG_LEVEL_ALL can be used to specify that all log * messages should be logged. * * \see kjmj_log_to_file * \see kjmj_tog_to_stdout * \see kjmj_log_to_stderr * \see FATAL */ enum kjmjLogLevelEnum { KJMJ_LOG_LEVEL_DISABLED, KJMJ_LOG_LEVEL_FATAL, KJMJ_LOG_LEVEL_ERROR, KJMJ_LOG_LEVEL_WARN, KJMJ_LOG_LEVEL_INFO, KJMJ_LOG_LEVEL_DEBUG, KJMJ_LOG_LEVEL_TRACE, KJMJ_LOG_LEVEL_ALL }; typedef enum kjmjLogLevelEnum kjmjLogLevel; /*! * \def KJMJ_LEVEL_IS_AT_LEAST * * \brief Returns true if a level is at least as important as the * other level. * * \param LEVEL the level to compare against \p OTHER. * \param OTHER the level that LEVEL should be at least as important as. * * \see kjmjLogLevelEnum * * \note Because the log levels are specified from the * most important (fatal) to the least important (trace) * the lower the log level the more important it is. */ #define KJMJ_LEVEL_AT_LEAST( LEVEL, OTHER ) \ ( LEVEL <= OTHER ) /*! * \internal * * \def KJMJ_FNAME * * \brief Returns the filename suffix portion of \c __FILE__. * * \endinternal */ #define KJMJ_FNAME \ ( strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__ ) /*! * \internal * * \def KJMJ_MACRO_START * * \brief Used at the beginning of macro function definitions that contain * more than one line of code. * * Used to wrap the macro function in a do { ... } while(0) block. * * \see KJMJ_MACRO_END * * \endinternal */ #define KJMJ_MACRO_START do { /*! * \internal * * \def KJMJ_MACRO_END * * \brief Used at the end of macro function definitions that contain * more than one line of code. * * Used to wrap the macro function in a
do { ... } while (0)
block. * * \see KJMJ_MACRO_START * * \endinternal */ #define KJMJ_MACRO_END } while (0) /*! * \def TRACE * * \brief Used to log at the trace level. * * Trace is used to describe the inner workings of a function or an * algorithm. It can be useful when trying to find an error in an * algorithm or for corner cases. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef TRACE #define TRACE( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_TRACE, "[TRACE]", ANSI_START_TRACE, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ) #else // TRACE is defined #error "TRACE already defined" #endif /*! * \def DEBUG * * \brief Used to log at the debug level or higher. * * Mainly used for debugging, tracking certain variables or * anything useful to help visualizing what your program is doing. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef DEBUG #define DEBUG( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_DEBUG, "[DEBUG]", ANSI_START_DEBUG, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ) #else #error "DEBUG already defined" #endif /*! * \def INFO * \brief Used to log at the info level. * * Info can be used as a heads up or for normal messages. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef INFO #define INFO( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_INFO, "[INFO]", ANSI_START_INFO, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ) #else #error "INFO already defined" #endif /*! * \def WARN * * \brief Used to log at the warning level. * * Warnings are attributed to all cases where the code can recover from the * condition and they are usually treated by it. In most cases the user can * treat these warnings themselves. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef WARN #define WARN( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_WARN, "[WARN]", ANSI_START_WARN, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ) #else #error "WARN already defined" #endif /*! * \def ERROR * * \brief Used to log at the error level. * * Something really bad happened. Your program will not crash yet but * depending on what comes next, it probably will. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef ERROR #define ERROR( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_ERROR, "[ERROR]", ANSI_START_ERROR, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ) #else #error "ERROR already defined" #endif /*! * \def FATAL * * \brief Used to log at the fatal level. * * This will probably be the last logging message before your program * crashes or goes into madness. Fatal errors are commonly attributed * to dereferencing \c NULL pointers. * * \param FMT a C printf style format string. * \param ... any additional variadic arguments will be used like printf * variadic arguments. */ #ifndef FATAL #define FATAL( FMT, ... ) \ kjmj_log( KJMJ_LOG_LEVEL_FATAL, "[FATAL]", ANSI_START_FATAL, \ __func__, KJMJ_FNAME, __LINE__, FMT, ##__VA_ARGS__ ); #else #error "FATAL already defined" #endif /*! * \internal * * \struct kjmjLogStruct * * \brief The data structure that holds all information neccessary to * determine what to do when a message is logged. * * The one and only variable that uses this struct is the global variable * \c g_kjmj_log. * * \endinternal */ static struct kjmjLogStruct { FILE* file; int file_log_level; int stdout_log_level; int stderr_log_level; } g_kjmj_log = { NULL, KJMJ_LOG_LEVEL_DISABLED, KJMJ_LOG_LEVEL_DISABLED, KJMJ_LOG_LEVEL_DISABLED }; /*! * \fn kjmj_log_init * * \brief Prepares the logging system. Both file and terminal logging is * disabled. * * \note If the logging system is currently setup to log to a file, the * file will be closed. */ static void kjmj_log_init() { if ( g_kjmj_log.file != NULL ) { fclose( g_kjmj_log.file ); } g_kjmj_log.file = NULL, g_kjmj_log.file_log_level = KJMJ_LOG_LEVEL_DISABLED; g_kjmj_log.stdout_log_level = KJMJ_LOG_LEVEL_DISABLED, g_kjmj_log.stderr_log_level = KJMJ_LOG_LEVEL_DISABLED; } /*! * \fn kjmj_log_terminate * * \brief Should be called once to clean up. * * \see kjmj_log_init */ static void kjmj_log_terminate() { kjmj_log_init(); } /*! * \fn kjmj_log_to_file * * \brief Enables logging to \p file at the \p level logging level. * * \param file handle (pointer) to \c FILE. Must be open unless the * \p LEVEL is \c KJMJ_LOG_LEVEL_DISABLED. * \param level the level (or higher) at which logging is enabled. * * \see kjmjLogLevel */ static void kjmj_log_to_file( FILE* file, kjmjLogLevel level ) { assert( ( file != NULL ) || ( KJMJ_LEVEL_AT_LEAST( KJMJ_LOG_LEVEL_TRACE, level ) ) ); assert( KJMJ_LEVEL_AT_LEAST( KJMJ_LOG_LEVEL_DISABLED, level ) ); assert( KJMJ_LEVEL_AT_LEAST( level, KJMJ_LOG_LEVEL_ALL ) ); g_kjmj_log.file = file; g_kjmj_log.file_log_level = level; } /*! * \def kjmj_log_to_stdout * * \brief Enables logging to \c stdout at the \p level logging level. * * \param level the level (or more important) at which logging is enabled. * * \see kjmjLogLevel */ static void kjmj_log_to_stdout( kjmjLogLevel level ) { assert( KJMJ_LEVEL_AT_LEAST( KJMJ_LOG_LEVEL_DISABLED, level ) ); assert( KJMJ_LEVEL_AT_LEAST( level, KJMJ_LOG_LEVEL_ALL ) ); g_kjmj_log.stdout_log_level = level; } /*! * \def kjmj_log_to_stderr * * \brief Enables logging to \c stderr at the \p level logging level. * * \param level the level (or more important) at which logging is enabled. */ static void kjmj_log_to_stderr( kjmjLogLevel level ) { assert( KJMJ_LEVEL_AT_LEAST( KJMJ_LOG_LEVEL_DISABLED, level ) ); assert( KJMJ_LEVEL_AT_LEAST( level, KJMJ_LOG_LEVEL_ALL ) ); g_kjmj_log.stderr_log_level = level; } /*! * \internal * * \fn kjmj_log * * \brief Logs a message to a file, stdout, and/or stderr if enabled. * * \param level the level of the log message. * \param level_name a string representation of the level. * \param ansi_start_level the ANSI string that should be used to start * the log message if log colors are enabled. * \param function_name the name of the function currently executing. * \param filename the name of the file containing this function. * \param line the line number where the message is being * logged from. * \param fmt a C printf style format string. * \param ... any additional variadic arguments will be used * like printf variadic arguments. * * \internal */ void kjmj_log( int level, const char* level_name, const char* ansi_start_level, const char* function_name, const char* filename, unsigned line, const char* fmt, ... ) { assert( KJMJ_LEVEL_AT_LEAST( KJMJ_LOG_LEVEL_DISABLED, level ) ); assert( KJMJ_LEVEL_AT_LEAST( level, KJMJ_LOG_LEVEL_ALL ) ); assert( level_name != NULL ); assert( ansi_start_level != NULL ); assert( function_name != NULL ); assert( filename != NULL ); assert( fmt != NULL ); _Bool log_to_file = ( g_kjmj_log.file != NULL ) && ( KJMJ_LEVEL_AT_LEAST( level, g_kjmj_log.file_log_level ) ); _Bool log_to_stdout = KJMJ_LEVEL_AT_LEAST( level, g_kjmj_log.stdout_log_level ); _Bool log_to_stderr = KJMJ_LEVEL_AT_LEAST( level, g_kjmj_log.stderr_log_level ); if ( !log_to_file && !log_to_stdout && !log_to_stderr ) { return; } // Determine the time string for the time now. time_t now; time( &now ); struct tm* local; local = localtime( &now ); char timestamp[ 0x20 ]; timestamp[ strftime( timestamp, sizeof( timestamp ), "%Y/%m/%d %H:%M:%S", local ) ] = '\0'; // Determine the prefix to be printed before the log message. char msg_prefix[ 0x100 ]; snprintf( msg_prefix, sizeof( msg_prefix ), "%s %-7s %s (%s:%u) ", timestamp, level_name, function_name, filename, line ); // Optionally log to file. if ( log_to_file ) { fprintf( g_kjmj_log.file, msg_prefix ); va_list args; va_start( args, fmt ); vfprintf( g_kjmj_log.file, fmt, args ); va_end( args ); fprintf( g_kjmj_log.file, "\n" ); fflush( g_kjmj_log.file ); } // Optionally log to stdout. if ( log_to_stdout ) { #ifndef KJMJ_LOG_NO_COLOR fprintf( stdout, ansi_start_level ); #endif fprintf( stdout, msg_prefix ); #ifndef KJMJ_LOG_NO_COLOR fprintf( stdout, ANSI_RESET ); #endif va_list args; va_start( args, fmt ); vfprintf( stdout, fmt, args ); va_end( args ); fprintf( stdout, "\n" ); } // Optionally log to stderr. if ( log_to_stderr ) { #ifndef KJMJ_LOG_NO_COLOR fprintf( stderr, ansi_start_level ); #endif fprintf( stderr, msg_prefix ); #ifndef KJMJ_LOG_NO_COLOR fprintf( stderr, ANSI_RESET ); #endif va_list args; va_start( args, fmt ); vfprintf( stderr, fmt, args ); va_end( args ); fprintf( stderr, "\n" ); } } // kjmj_log #ifdef __cplusplus } #endif #endif // LOG_H_
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)