diff --git a/src/message_logger.c b/src/message_logger.c index a37b22c..cb9da4e 100644 --- a/src/message_logger.c +++ b/src/message_logger.c @@ -2,6 +2,13 @@ // MIT License // Message logger module - Source code. +//! \file message_logger.c +//! \author AndrĂ© Filipe Caldas Laranjeira +//! \brief Message logger module - Source code. +//! +//! The Message Logger module source file contains the implementation of all +//! the functions provided by the Message Logger module and private state +//! variables. // Includes: #include "message_logger.h" @@ -33,6 +40,34 @@ static void print_context(const char*); static void print_formatted_text(const char*, va_list); // Public function implementations: + +//! \fn int configure_log_file(const char *file_name, LogFileMode file_mode) +//! \brief Configure a log file to store the Message Logger's messages. +//! Allocates resources, requiring a call to logger_module_clean_up() +//! afterwards. +//! \param file_name Name of the file used to log messages. +//! \param file_mode Mode used for opening the log file. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function configures the Message Logger module to write any messages to +//! a log file, in adition to priting them on the screen. Each message written +//! on the log file has the message's contents and information on the message's +//! datetime. +//! +//! If an error occurs when configuring the log file, this function will return +//! -1 and the Message Logger will print an error message explaining what went +//! wrong. +//! +//! \b Important: After the Message Logger module is no longer used, the +//! function logger_module_clean_up() must be called to close the log file +//! created. +//! +//! Usage example: +//! \code +//! configure_log_file("logger-test.log", WRITE); +//! // Use the Message Logger normally... +//! logger_module_clean_up(); +//! \endcode int configure_log_file(const char *file_name , LogFileMode file_mode) { // Acquire logger recursive lock if thread safety is enabled: @@ -83,6 +118,37 @@ int configure_log_file(const char *file_name , LogFileMode file_mode) { } +//! \fn int enable_thread_safety() +//! \brief Enable thread safety for the Message Logger's operations. Allocates +//! resources, requiring a call to logger_module_clean_up() afterwards. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function configures the Message Logger module to enable thread safety +//! when logging messages or executing other logger operations. This allows the +//! logger to be utilized in a multi-threaded environment with the pthreads +//! library without the nuissance of race conditions or other problems. +//! +//! If an error occurs when enabling thread safety, this function will return +//! -1 and the Message Logger will print an error message explaining what went +//! wrong. +//! +//! \b Important: After the Message Logger module is no longer used, the +//! function logger_module_clean_up() must be called to release the memory +//! allocated for the thread-safety data structures utilized. +//! +//! Usage example: +//! \code +//! // In the main thread, before creating other threads, enable thread safety. +//! enable_thread_safety(); +//! +//! // ... +//! // Create multiple threads to use the Message Logger... +//! +//! // ... +//! // In the main thread, after other threads have been joined (and the +//! // logger is no longer used), call the clean up function. +//! logger_module_clean_up(); +//! \endcode int enable_thread_safety() { pthread_mutexattr_t logger_mutex_attributes; @@ -108,6 +174,35 @@ int enable_thread_safety() { } +//! \fn int get_logger_msg_colors( +//! DisplayColors* display_colors_destination, +//! MessageCategory requested_category +//! ) +//! \brief Get the display colors in the Message Logger for a message category. +//! \param display_colors_destination Pointer to where the requested display +//! colors are copied to. Must NOT be NULL. +//! \param requested_category Category of message whose display colors will be +//! copied. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function returns the DisplayColors currently assigned to a +//! #MessageCategory in the Message Logger. The DisplayColors information is +//! copied from the logger's LoggerColorPallet to the pointer provided by the +//! user. +//! +//! If an error occurs when getting the display colors, this function will +//! return -1 and the Message Logger will print an error message explaining +//! what went wrong. +//! +//! \b Important: This function MUST receive a non-NULL DisplayColors pointer +//! to copy the information to. This function does NOT allocate memory on the +//! pointer provided by the user. +//! +//! Usage example: +//! \code +//! DisplayColors current_success_message_colors; +//! get_logger_msg_colors(¤t_success_message_colors, SUCCESS_MSG); +//! \endcode int get_logger_msg_colors( DisplayColors* display_colors_destination, MessageCategory requested_category @@ -139,6 +234,34 @@ int get_logger_msg_colors( } +//! \fn int get_logger_tag_colors( +//! DisplayColors* display_colors_destination, +//! TagCategory requested_category +//! ) +//! \brief Get the display colors in the Message Logger for a tag category. +//! \param display_colors_destination Pointer to where the requested display +//! colors are copied to. Must NOT be NULL. +//! \param requested_category Category of tag whose display colors will be +//! copied. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function returns the DisplayColors currently assigned to a +//! #TagCategory in the Message Logger. The DisplayColors information is copied +//! from the logger's LoggerColorPallet to the pointer provided by the user. +//! +//! If an error occurs when getting the display colors, this function will +//! return -1 and the Message Logger will print an error message explaining +//! what went wrong. +//! +//! \b Important: This function MUST receive a non-NULL DisplayColors pointer +//! to copy the information to. This function does NOT allocate memory on the +//! pointer provided by the user. +//! +//! Usage example: +//! \code +//! DisplayColors current_success_tag_colors; +//! get_logger_tag_colors(¤t_success_tag_colors, SUCCESS_TAG); +//! \endcode int get_logger_tag_colors( DisplayColors* display_colors_destination, TagCategory requested_category @@ -170,6 +293,30 @@ int get_logger_tag_colors( } +//! \fn int get_time_format(TimeFormat *time_format_destination) +//! \brief Get the time format used by the Message Logger in log files. +//! \param time_format_destination Pointer to where the requested time format +//! is copied to. Must NOT be NULL. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function returns the TimeFormat currently used by the Message Logger +//! to determine how to format the time information written to log files. The +//! TimeFormat information is copied from the logger's internal variables to +//! the pointer provided by the user. +//! +//! If an error occurs when getting the time format, this function will return +//! -1 and the Message Logger will print an error message explaining what went +//! wrong. +//! +//! \b Important: This function MUST receive a non-NULL TimeFormat pointer +//! to copy the information to. This function does NOT allocate memory on the +//! pointer provided by the user. +//! +//! Usage example: +//! \code +//! TimeFormat logger_time_format; +//! get_time_format(&logger_time_format); +//! \endcode int get_time_format(TimeFormat *time_format_destination) { if(time_format_destination == NULL) { @@ -200,6 +347,36 @@ int get_time_format(TimeFormat *time_format_destination) { } +//! \fn int set_logger_msg_colors( +//! MessageCategory message_category, +//! const DisplayColors *assigned_colors +//! ) +//! \brief Set the display colors used by the Message Logger for a message +//! category. +//! \param message_category Category of message whose display colors are being +//! set. +//! \param assigned_colors Pointer to the display colors being copied to a +//! message category of the Message Logger's LoggerColorPallet. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function sets the display colors for a given #MessageCategory in the +//! Message Logger's LoggerColorPallet. This is done by copying the information +//! from a valid, non-NULL DisplayColors pointer provided by the user to an +//! index of the \link LoggerColorPallet::message_colors message_colors +//! \endlink array. +//! +//! If an error occurs when setting the display colors, this function will +//! return -1 and the Message Logger will print an error message explaining +//! what went wrong. +//! +//! Usage example: +//! \code +//! DisplayColors custom_info_msg_colors = { +//! .text_color = B_WHT, +//! .background_color = CYN +//! }; +//! set_logger_msg_colors(INFO_MSG, &custom_info_msg_colors); +//! \endcode int set_logger_msg_colors( MessageCategory message_category, const DisplayColors *assigned_colors @@ -231,6 +408,34 @@ int set_logger_msg_colors( } +//! \fn int set_logger_tag_colors( +//! TagCategory tag_category, +//! const DisplayColors *assigned_colors +//! ) +//! \brief Set the display colors used by the Message Logger for a tag +//! category. +//! \param tag_category Category of tag whose display colors are being set. +//! \param assigned_colors Pointer to the display colors being copied to a tag +//! category of the Message Logger's LoggerColorPallet. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function sets the display colors for a given #TagCategory in the +//! Message Logger's LoggerColorPallet. This is done by copying the information +//! from a valid, non-NULL DisplayColors pointer provided by the user to an +//! index of the \link LoggerColorPallet::tag_colors tag_colors \endlink array. +//! +//! If an error occurs when setting the display colors, this function will +//! return -1 and the Message Logger will print an error message explaining +//! what went wrong. +//! +//! Usage example: +//! \code +//! DisplayColors custom_info_tag_colors = { +//! .text_color = B_BLA, +//! .background_color = CYN +//! }; +//! set_logger_tag_colors(INFO_TAG, &custom_info_tag_colors); +//! \endcode int set_logger_tag_colors( TagCategory tag_category, const DisplayColors *assigned_colors @@ -262,6 +467,26 @@ int set_logger_tag_colors( } +//! \fn int set_time_format(const char *new_format) +//! \brief Set the time format used by the Message Logger in log files. +//! \param new_format String representation of the time format to be used. Size +//! must be smaller or equal to #TIME_FMT_SIZE. +//! \return Returns 0 when successfully executed and -1 if an error occurs. +//! +//! This function sets the \link TimeFormat::string_representation +//! string_representation \endlink in the Message Logger's TimeFormat, which is +//! used when saving messages to a log file if one is configured. The time +//! format information is copied from a valid, non-NULL pointer provided by the +//! user. +//! +//! If an error occurs when setting the time format, this function will return +//! -1 and the Message Logger will print an error message explaining what went +//! wrong. +//! +//! Usage example: +//! \code +//! set_time_format("%c"); +//! \endcode int set_time_format(const char *new_format) { if(new_format == NULL) { @@ -298,6 +523,20 @@ int set_time_format(const char *new_format) { } +//! \fn void color_background(Color p_color) +//! \brief Changes the terminal text's background color to a specific #Color. +//! \param p_color Color to be applied to the terminal text's background. +//! +//! This function changes the terminal text's background color to a #Color +//! provided by the user. When line breaks occur, the entire line's background +//! is colored, not just the portion with text. If this function is called in +//! the middle of a text line, any existing background color will be cleared +//! after the cursor's position. +//! +//! Usage example: +//! \code +//! color_background(B_GRN); +//! \endcode void color_background(Color p_color) { // Acquire logger recursive lock if thread safety is enabled: @@ -384,6 +623,18 @@ void color_background(Color p_color) { } +//! \fn void color_text(Color p_color) +//! \brief Changes the terminal text's font color to a specific #Color. +//! \param p_color Color to be applied to the terminal text's font. +//! +//! This function changes the terminal text's font color to a #Color provided by +//! the user. Bright colors are accompanied by a bold font weight, while the +//! other font colors are accompanied by a regular font weight. +//! +//! Usage example: +//! \code +//! color_text(BLU); +//! \endcode void color_text(Color p_color) { // Acquire logger recursive lock if thread safety is enabled: @@ -468,6 +719,27 @@ void color_text(Color p_color) { } +//! \fn void error(const char *context, const char *format, ...) +//! \brief Log an error message using the Message Logger. +//! \param context Caller context where message originated. Pass a NULL pointer +//! for an empty context. +//! \param format String formatting for the message's contents before argument +//! substitution takes place. +//! \param ... Arguments used to substitute placeholders in the message's +//! contents. +//! +//! This function logs an error message to the terminal and configured log +//! file. The message contains the caller context that originated the error, a +//! tag that identifies the message as an error and the message's contents. If +//! a log file is configured, the message will also have the timestamp +//! information when written to the log file. +//! +//! Usage example: +//! \code +//! int arg = 0; +//! error("Example", "%d: This is an error message with a context.\n", arg); +//! error(NULL, "%d: This is also an error message.\n", arg+1); +//! \endcode void error(const char *context, const char *format, ...) { const char msg_type[8] = "(Error)"; @@ -512,6 +784,27 @@ void error(const char *context, const char *format, ...) { } +//! \fn void info(const char *context, const char *format, ...) +//! \brief Log an info message using the Message Logger. +//! \param context Caller context where message originated. Pass a NULL pointer +//! for an empty context. +//! \param format String formatting for the message's contents before argument +//! substitution takes place. +//! \param ... Arguments used to substitute placeholders in the message's +//! contents. +//! +//! This function logs an info message to the terminal and configured log file. +//! The message contains the caller context that originated the info message, a +//! tag that identifies the message as an info message and the message's +//! contents. If a log file is configured, the message will also have the +//! timestamp information when written to the log file. +//! +//! Usage example: +//! \code +//! int arg = 0; +//! info("Example", "%d: This is an info message with a context.\n", arg); +//! info(NULL, "%d: This is also an info message.\n", arg+1); +//! \endcode void info(const char *context, const char *format, ...) { const char msg_type[7] = "(Info)"; @@ -556,6 +849,46 @@ void info(const char *context, const char *format, ...) { } +//! \fn void lock_logger_recursive_mutex() +//! \brief Lock the Message Logger's recursive mutex lock, preventing any +//! other thread from using the Message Logger. Thread safety MUST be enabled. +//! +//! This function locks the recursive mutex lock utilized by the Message Logger +//! to ensure thread safety. When the recursive mutex is locked by a thread, +//! the other threads will not be able to lock it as well and will be suspended +//! until the mutex is unlocked. This ensures that no race conditions take +//! place. The recursive nature of the mutex lock allows a thread to lock it +//! more than once without being suspended. +//! +//! Since most of the Message Logger's functions already handle the recursive +//! mutex lock when thread safety is enabled, the main purpose of this function +//! is to allow the user's code to avoid race conditions with the logger. For +//! example, if the user wishes to print information on the terminal without +//! using the Message Logger, this function will guarantee that the logger +//! doesn't try to do the same from another thread while the user's code is +//! running. +//! +//! \b Important: For this function to work properly, the function +//! enable_thread_safety() must have been called to configure the recursive +//! mutex lock. Otherwise, this function will emit a warning and will have NO +//! EFFECT. +//! +//! Usage example: +//! \code +//! // In the main thread, before creating other threads, enable thread safety. +//! enable_thread_safety(); +//! +//! // ... +//! // After creating multiple threads, in one of the threads. +//! lock_logger_recursive_mutex(); +//! printf("This message will NOT interfere with the logger's operations!\n"); +//! unlock_logger_recursive_mutex(); +//! +//! // ... +//! // In the main thread, after other threads have been joined (and the +//! // logger is no longer used), call the clean up function. +//! logger_module_clean_up(); +//! \endcode void lock_logger_recursive_mutex() { if(logger_recursive_mutex != NULL) pthread_mutex_lock(logger_recursive_mutex); @@ -567,6 +900,24 @@ void lock_logger_recursive_mutex() { ); } +//! \fn void logger_module_clean_up() +//! \brief Clean up the resources allocated by the Message Logger. +//! +//! This function cleans up any memory or other resources utilized by the +//! Message Logger. Ideally, it should always be called after the logger is not +//! longer utilized. If this function is called and the logger is utilized +//! afterwards, some configurations such as the log file and thread safety will +//! NOT work. +//! +//! \b Important: This function NEEDS to be called when a log file is +//! configured or when thread safety is enabled. Failure to do so might result +//! in an incomplete log file or memory leaks. +//! +//! Usage example: +//! \code +//! // Use the logger normally... +//! logger_module_clean_up(); +//! \endcode void logger_module_clean_up() { // Clean up the log file: @@ -584,6 +935,27 @@ void logger_module_clean_up() { } +//! \fn void message(const char *context, const char *format, ...) +//! \brief Log a regular message using the Message Logger. +//! \param context Caller context where message originated. Pass a NULL pointer +//! for an empty context. +//! \param format String formatting for the message's contents before argument +//! substitution takes place. +//! \param ... Arguments used to substitute placeholders in the message's +//! contents. +//! +//! This function logs a regular message to the terminal and configured log +//! file. The message contains the caller context that originated the regular +//! message and the message's contents. If a log file is configured, the +//! message will also have the timestamp information when written to the log +//! file. +//! +//! Usage example: +//! \code +//! int arg = 0; +//! message("Example", "%d: This is a normal message with a context.\n", arg); +//! message(NULL, "%d: This is also a normal message.\n", arg+1); +//! \endcode void message(const char *context, const char *format, ...) { va_list arg_list; @@ -622,10 +994,38 @@ void message(const char *context, const char *format, ...) { } +//! \fn void reset_background_color() +//! \brief Reset the terminal's text background color to the default color. +//! +//! Deprecation warning: This function will be removed in the next major +//! release. Use color_background(DFLT) instead. +//! +//! This function resets the terminal's text background color to the default +//! color specified in the terminal's configuration. +//! +//! Usage example: +//! \code +//! // Change the text background color in some way. +//! reset_background_color(); +//! \endcode void reset_background_color() { color_background(DFLT); } +//! \fn void reset_colors() +//! \brief Reset all the terminal's colors and text attributes to their +//! defaults. +//! +//! This function resets all of the terminal's colors and text attributes to +//! the default configurations specified for the terminal. This includes the +//! colors used for text font and text background, the font weight and any +//! other characteristics configured using ANSI escape codes. +//! +//! Usage example: +//! \code +//! // Change various text configurations. +//! reset_colors(); +//! \endcode void reset_colors() { // Acquire logger recursive lock if thread safety is enabled: if(logger_recursive_mutex != NULL) @@ -639,6 +1039,23 @@ void reset_colors() { pthread_mutex_unlock(logger_recursive_mutex); } +//! \fn void reset_logger_colors() +//! \brief Reset the Message Logger's colors to their defaults. +//! +//! This function resets the Message Logger's LoggerColorPallet by setting it's +//! \link LoggerColorPallet::message_colors message_colors \endlink and \link +//! LoggerColorPallet::tag_colors tag_colors \endlink to the default values +//! specifed in the macros #DEFAULT_LOGGER_MESSAGE_COLORS and +//! #DEFAULT_LOGGER_TAG_COLORS. ALL the values are reset, so any changes made +//! with calls to the functions \link set_logger_msg_colors() +//! set_logger_msg_colors \endlink and \link set_logger_tag_colors() +//! set_logger_tag_colors \endlink will be lost after this function is called. +//! +//! Usage example: +//! \code +//! // Change various logger pallet colors. +//! reset_logger_colors(); +//! \endcode void reset_logger_colors() { int i; @@ -666,10 +1083,45 @@ void reset_logger_colors() { pthread_mutex_unlock(logger_recursive_mutex); } +//! \fn void reset_text_color() +//! \brief Reset the terminal's text font color to the default color. +//! +//! Deprecation warning: This function will be removed in the next major +//! release. Use color_text(DFLT) instead. +//! +//! This function resets the terminal's text font color to the default color +//! specified in the terminal's configuration. +//! +//! Usage example: +//! \code +//! // Change the text font color in some way. +//! reset_text_color(); +//! \endcode void reset_text_color() { color_text(DFLT); } +//! \fn void success(const char *context, const char *format, ...) +//! \brief Log a success message using the Message Logger. +//! \param context Caller context where message originated. Pass a NULL pointer +//! for an empty context. +//! \param format String formatting for the message's contents before argument +//! substitution takes place. +//! \param ... Arguments used to substitute placeholders in the message's +//! contents. +//! +//! This function logs a success message to the terminal and configured log +//! file. The message contains the caller context that originated the success +//! message, a tag that identifies the message as a success message and the +//! message's contents. If a log file is configured, the message will also have +//! the timestamp information when written to the log file. +//! +//! Usage example: +//! \code +//! int arg = 0; +//! success("Example", "%d: This is a success message with a context.\n", arg); +//! success(NULL, "%d: This is also a success message.\n", arg+1); +//! \endcode void success(const char *context, const char *format, ...) { const char msg_type[10] = "(Success)"; @@ -716,6 +1168,47 @@ void success(const char *context, const char *format, ...) { } +//! \fn void unlock_logger_recursive_mutex() +//! \brief Unlock the Message Logger's recursive mutex lock, allowing other +//! threads to use the Message Logger if no recursive locks remain. Thread +//! safety MUST be enabled. +//! +//! This function unlocks the recursive mutex lock utilized by the Message +//! Logger to ensure thread safety. When the recursive mutex is locked by a +//! thread, the other threads will not be able to lock it as well and will be +//! suspended until the mutex is unlocked. This ensures that no race conditions +//! take place. The recursive nature of the mutex lock allows a thread to lock +//! it more than once without being suspended. +//! +//! Since most of the Message Logger's functions already handle the recursive +//! mutex lock when thread safety is enabled, the main purpose of this function +//! is to allow the user's code to avoid race conditions with the logger. For +//! example, if the user wishes to print information on the terminal without +//! using the Message Logger, this function will allow the logger to resume +//! it's operations in a thread safe manner after the user prints all +//! information to the screen. +//! +//! \b Important: For this function to work properly, the function +//! enable_thread_safety() must have been called to configure the recursive +//! mutex lock. Otherwise, this function will emit a warning and will have NO +//! EFFECT. +//! +//! Usage example: +//! \code +//! // In the main thread, before creating other threads, enable thread safety. +//! enable_thread_safety(); +//! +//! // ... +//! // After creating multiple threads, in one of the threads. +//! lock_logger_recursive_mutex(); +//! printf("This message will NOT interfere with the logger's operations!\n"); +//! unlock_logger_recursive_mutex(); +//! +//! // ... +//! // In the main thread, after other threads have been joined (and the +//! // logger is no longer used), call the clean up function. +//! logger_module_clean_up(); +//! \endcode void unlock_logger_recursive_mutex() { if(logger_recursive_mutex != NULL) pthread_mutex_unlock(logger_recursive_mutex); @@ -727,6 +1220,27 @@ void unlock_logger_recursive_mutex() { ); } +//! \fn void warning(const char *context, const char *format, ...) +//! \brief Log a warning message using the Message Logger. +//! \param context Caller context where message originated. Pass a NULL pointer +//! for an empty context. +//! \param format String formatting for the message's contents before argument +//! substitution takes place. +//! \param ... Arguments used to substitute placeholders in the message's +//! contents. +//! +//! This function logs a warning message to the terminal and configured log +//! file. The message contains the caller context that originated the warning, +//! a tag that identifies the message as a warning and the message's contents. +//! If a log file is configured, the message will also have the timestamp +//! information when written to the log file. +//! +//! Usage example: +//! \code +//! int arg = 0; +//! warning("Example", "%d: This is a warning message with a context.\n", arg); +//! warning(NULL, "%d: This is also a warning message.\n", arg+1); +//! \endcode void warning(const char *context, const char *format, ...) { const char msg_type[10] = "(Warning)";