diff --git a/README.md b/README.md index c428fdb..be300da 100644 --- a/README.md +++ b/README.md @@ -52,38 +52,56 @@ You can control the verbosity of the logging output by setting a global log leve This log level filters out messages with a lower priority. ### Custom Log Levels -Additionally, you have the flexibility to override the log level for individual tags (but not lower than the global log level). -This can be archived by calling the following function: +before you include `log.h`, you can define custom log levels by defining the following macros: ```c -logger_set_log_level(LOG_TAG, LOG_LEVEL); +// All log messages will be printed +#define LOGGER_LEVEL_ALL +#include "log.h" +``` + +```c +// Info and higher priority messages will be printed +#define LOGGER_LEVEL_INFO +#include "log.h" +``` + +```c +// Only warnings and errors will be printed +#define LOGGER_LEVEL_WARN +#include "log.h" +``` + +```c +// Only log messages with level ERROR will be printed +#define LOGGER_LEVEL_ERROR +#include "log.h" ``` -If the log level for a tag is not set, the global log level is used. ### Colorful Log Messages -For improved readability, log messages can be printed in color by configuring `LOGGER_USE_COLOR` to `1` in `log.h`. +For improved readability, log messages can be printed in color by configuring `LOGGER_USE_COLOR` to `1` in `log.h` or before you include `log.h`. \ +Default is `0` ### Log Output Format Each log entry is formatted to include the following information: - - Log level (D for Debug, I for Info, W for Warning, E for Error) + - Log level ([Debug], [Info], [Warn], [Error]) - Timestamp (in milliseconds since boot) - Tag - The log message For instance, a log entry may look like this: -`I (2009) LTDC: This is a log message` +`[Info] (2009) [LTDC]: This is a log message` ### Example ```c +#define LOGGER_LEVEL_INFO #include "log.h" // Don't use a define for the tag, as the pointer to the tag is used static const char *TAG = "main"; int main(void) { - // Set log level for tag "main" to LOG_LEVEL_INFO - logger_set_log_level(TAG, LOG_LEVEL_INFO); LOG_DEBUG(TAG, "This message will not be printed"); LOG_INFO(TAG, "This message will be printed"); @@ -99,10 +117,10 @@ int main(void) { ``` Result: ``` -I (2009) main: This message will be printed -W (2026) main: This message will be printed -E (2033) main: This message will be printed -I (2040) main: Iteration 0 of 3 -I (2047) main: Iteration 1 of 3 -I (2054) main: Iteration 2 of 3 +[Info] (2009) main: This message will be printed +[Warning] (2026) main: This message will be printed +[Error] (2033) main: This message will be printed +[Info] (2040) main: Iteration 0 of 3 +[Info] (2047) main: Iteration 1 of 3 +[Info] (2054) main: Iteration 2 of 3 ``` diff --git a/project/Core/Inc/log.h b/project/Core/Inc/log.h index cf7f4ed..ddb3588 100644 --- a/project/Core/Inc/log.h +++ b/project/Core/Inc/log.h @@ -12,64 +12,64 @@ #define LOG_H #include +#include +#include +#include +#include +#include -/** - * @brief Log levels - */ -typedef enum { - LOG_LEVEL_DEBUG, - LOG_LEVEL_INFO, - LOG_LEVEL_WARN, - LOG_LEVEL_ERROR, -} log_level_t; +#ifdef LOGGER_LEVEL_ERROR +#define LOGGER_LEVEL 4 +#endif +#ifdef LOGGER_LEVEL_WARN +#define LOGGER_LEVEL 3 +#endif +#ifdef LOGGER_LEVEL_INFO +#define LOGGER_LEVEL 2 +#endif +#ifdef LOGGER_LEVEL_ALL +#define LOGGER_LEVEL 1 +#endif -/* For internal use only. - * Use the LOG_* macros instead e.g., LOG_DEBUG(TAG, "Debug message"); - */ -uint32_t logger_get_timestamp(void); -void logger_write(const char* format, ...); - -/** - * @brief Set the log level for a tag - * If the tag is not already in the list, it will be added. - * - * @param[in] tag The tag to set the log level for - * @param[in] level The log level to set (member of @ref log_level_t) - */ -void logger_set_log_level(const char* tag, log_level_t level); - -/** - * @brief Get the log level for a tag - * - * @param[in] tag The tag to get the log level for - * @return The log level for the tag (member of @ref log_level_t) - */ -log_level_t logger_get_log_level(const char* tag); - -/** - * @brief The global minimum log level. - * Messages with a log level lower than this will not be logged. - * And log statements with a log level lower than this will optimized away by the compiler. - */ -#define LOGGER_MIN_LOG_LEVEL LOG_LEVEL_DEBUG - -/** - * @brief The maximum number of tags that can be used. - */ -#define LOGGER_MAX_TAG_ENTRIES 20 - -/** - * @brief Whether to use color in the log output. - * This is only supported in terminals that support ANSI escape codes. - */ +#ifndef LOGGER_LEVEL +#define LOGGER_LEVEL 3 +#endif +#ifndef LOGGER_USE_COLOR #define LOGGER_USE_COLOR 0 +#endif + +#define ANSI_ESC "\x1B" +#define ANSI_CSI "\x9B" +#define ANSI_DCS "\x90" +#define ANSI_OSC "\x9D" + +#define CLEAR_SCREEN "\033c" + +#define CURSOR_RESET ANSI_ESC "[H" +#define CURSOR_UP(n) ANSI_ESC "[" #n "A" +#define CURSOR_DOWN(n) ANSI_ESC "[" #n "B" +#define CURSOR_RIGHT(n) ANSI_ESC "[" #n "C" +#define CURSOR_LEFT(n) ANSI_ESC "[" #n "D" +#define CURSOR_NEXT_N_LINES(n) ANSI_ESC "[" #n "E" +#define CURSOR_PREV_N_LINES(n) ANSI_ESC "[" #n "F" +#define CURSOR_COL(n) ANSI_ESC "[" #n "G" +#define CURSOR_POS ANSI_ESC "[" #n ";" #n "H" +#define CURSOR_SAVE ANSI_ESC "7" +#define CURSOR_RESTORE ANSI_ESC "8" + +#define ERASE_FROM_CURSOR_TO_END ANSI_ESC "[0J" +#define ERASE_FROM_CURSOR_TO_BEGINNING ANSI_ESC "[1J" +#define ERASE_ENTIRE_SCREEN ANSI_ESC "[2J" +#define ERASE_FROM_CURSOR_TO_END_LINE ANSI_ESC "[0K" +#define ERASE_FROM_CURSOR_TO_BEGINNING_LINE ANSI_ESC "[1K" +#define ERASE_ENTIRE_LINE ANSI_ESC "[2K" #if LOGGER_USE_COLOR #define LOG_RESET_COLOR "\033[0m" #define LOG_COLOR_E "\033[0;31m" // Red #define LOG_COLOR_W "\033[0;33m" // Brown #define LOG_COLOR_I "\033[0;32m" // Green -#define LOG_COLOR_D +#define LOG_COLOR_D LOG_RESET_COLOR // Default #else #define LOG_RESET_COLOR #define LOG_COLOR_E @@ -78,41 +78,29 @@ log_level_t logger_get_log_level(const char* tag); #define LOG_COLOR_D #endif -#define LOG_FORMAT(letter, format) LOG_COLOR_##letter #letter " (%lu) %s: " format LOG_RESET_COLOR "\r\n" +#if LOGGER_LEVEL <= 1 +#define LOG_DEBUG(tag, fmt, ...) printf(LOG_Color_D"[Debug] (%lu) [%s]: "fmt LOG_RESET_COLOR, logger_get_timestamp(), tag, ##__VA_ARGS__) +#else +#define LOG_DEBUG(tag, fmt, ...) +#endif +#if LOGGER_LEVEL <= 2 +#define LOG_INFO(tag, fmt, ...) printf(LOG_COLOR_I"[Info] (%lu) [%s]: "fmt LOG_RESET_COLOR, logger_get_timestamp(), tag, ##__VA_ARGS__) +#else +#define LOG_INFO(tag, fmt, ...) +#endif +#if LOGGER_LEVEL <= 3 +#define LOG_WARN(tag, fmt, ...) printf(LOG_COLOR_W"[Warning] (%lu) [%s]: "fmt LOG_RESET_COLOR, logger_get_timestamp(), tag, ##__VA_ARGS__) +#else +#define LOG_WARN(tag, fmt, ...) +#endif +#if LOGGER_LEVEL <= 4 +#define LOG_ERROR(tag, fmt, ...) printf(LOG_COLOR_E"[Error] (%lu) [%s]: "fmt LOG_RESET_COLOR, logger_get_timestamp(), tag, ##__VA_ARGS__) +#else +#define LOG_ERROR(tag, fmt, ...) +#endif -#define LOGGER_LOG(level, tag, format, ...) \ - do { \ - if (level >= LOGGER_MIN_LOG_LEVEL && level >= logger_get_log_level(tag)) { \ - if (level == LOG_LEVEL_DEBUG) { \ - logger_write(LOG_FORMAT(D, format), logger_get_timestamp(), tag, ##__VA_ARGS__); \ - } else if (level == LOG_LEVEL_INFO) { \ - logger_write(LOG_FORMAT(I, format), logger_get_timestamp(), tag, ##__VA_ARGS__); \ - } else if (level == LOG_LEVEL_WARN) { \ - logger_write(LOG_FORMAT(W, format), logger_get_timestamp(), tag, ##__VA_ARGS__); \ - } else if (level == LOG_LEVEL_ERROR) { \ - logger_write(LOG_FORMAT(E, format), logger_get_timestamp(), tag, ##__VA_ARGS__); \ - } \ - } \ - } while (0) - -/** - * @brief Macro to log a debug message (LOG_LEVEL_DEBUG) +/* For internal use only. + * Use the LOG_* macros instead e.g., LOG_DEBUG(TAG, "Debug message"); */ -#define LOG_DEBUG(tag, format, ...) LOGGER_LOG(LOG_LEVEL_DEBUG, tag, format, ##__VA_ARGS__) - -/** - * @brief Macro to log an info message (LOG_LEVEL_INFO) - */ -#define LOG_INFO(tag, format, ...) LOGGER_LOG(LOG_LEVEL_INFO, tag, format, ##__VA_ARGS__) - -/** - * @brief Macro to log a warning message (LOG_LEVEL_WARN) - */ -#define LOG_WARN(tag, format, ...) LOGGER_LOG(LOG_LEVEL_WARN, tag, format, ##__VA_ARGS__) - -/** - * @brief Macro to log an error message (LOG_LEVEL_ERROR) - */ -#define LOG_ERROR(tag, format, ...) LOGGER_LOG(LOG_LEVEL_ERROR, tag, format, ##__VA_ARGS__) - -#endif // LOG_H +uint32_t logger_get_timestamp(void); +int _write(int file, char *data, int len); \ No newline at end of file diff --git a/project/Core/Src/log.c b/project/Core/Src/log.c index 1d90d5d..6370454 100644 --- a/project/Core/Src/log.c +++ b/project/Core/Src/log.c @@ -1,7 +1,7 @@ /** * @file log.c * @brief Logger implementation - * @author Lorenz C. + * @author Lorenz C. && Speetjens S. */ #include @@ -10,71 +10,33 @@ #include "stm32f7xx_hal.h" #include "log.h" -/** - * @brief Entry in the tag list containing the tag and the log level - */ -typedef struct { - const char* tag; - log_level_t level; -} tag_entry_t; - -static tag_entry_t tag_entries[LOGGER_MAX_TAG_ENTRIES]; -static size_t tag_entries_count = 0; -static const char* TAG = "logger"; - - -void logger_set_log_level(const char* tag, log_level_t level) { - // Check if the tag is already in the list - for (int i = 0; i < tag_entries_count; i++) { - if (tag_entries[i].tag == tag) { - tag_entries[i].level = level; - return; - } - } - - // The tag is not in the list yet, so add it - if (tag_entries_count < LOGGER_MAX_TAG_ENTRIES) { - tag_entries[tag_entries_count].tag = tag; - tag_entries[tag_entries_count].level = level; - tag_entries_count++; - } else { - LOG_WARN(TAG, "Could not add tag %s to list: List is full. Try increasing LOGGER_MAX_TAG_ENTRIES", tag); - } -} - -log_level_t logger_get_log_level(const char* tag) { - log_level_t level = LOGGER_MIN_LOG_LEVEL; - - // Try to find the tag in the list - for (int i = 0; i < tag_entries_count; i++) { - if (tag_entries[i].tag == tag) { - level = tag_entries[i].level; - break; - } - } - - return level; -} +extern UART_HandleTypeDef huart1; /** * @brief Get the current timestamp to be used in the logger * * @return The current timestamp in milliseconds since boot */ -uint32_t logger_get_timestamp(void) { - return HAL_GetTick(); + +int _write(int file, char *data, int len) { + HAL_StatusTypeDef status; + + switch (file) { + case STDOUT_FILENO: + case STDERR_FILENO: + status = HAL_UART_Transmit(&huart1, (uint8_t*)data, len, HAL_MAX_DELAY); + if (status != HAL_OK) { + errno = EIO; + return -1; + } + break; + default: + errno = EBADF; + return -1; + } + return len; } -/** - * @brief Write a log message to the console - * For now, this is just a wrapper around printf. - * - * @param[in] format The format string (see printf) - * @param[in] ... - */ -void logger_write(const char* format, ...) { - va_list args; - va_start(args, format); - vprintf(format, args); - va_end(args); -} +uint32_t logger_get_timestamp(void) { + return HAL_GetTick(); +} \ No newline at end of file diff --git a/project/Core/Src/main.c b/project/Core/Src/main.c index 24aa7f4..e48dc63 100644 --- a/project/Core/Src/main.c +++ b/project/Core/Src/main.c @@ -22,8 +22,7 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ -#include -#include +#include "log.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -431,24 +430,6 @@ static void MX_GPIO_Init(void) } /* USER CODE BEGIN 4 */ -int _write(int file, char *data, int len) { - HAL_StatusTypeDef status; - - switch (file) { - case STDOUT_FILENO: - case STDERR_FILENO: - status = HAL_UART_Transmit(&huart1, (uint8_t*)data, len, HAL_MAX_DELAY); - if (status != HAL_OK) { - errno = EIO; - return -1; - } - break; - default: - errno = EBADF; - return -1; - } - return len; -} /* USER CODE END 4 */ /**