Shrink the logger

* Move _write function to the log.c
* Change the way to setup the logger
* Update the readme
* Add terminal commands
This commit is contained in:
2023-10-31 21:14:02 +01:00
parent c9df2dee99
commit 11eba9dd84
4 changed files with 130 additions and 181 deletions

View File

@@ -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. This log level filters out messages with a lower priority.
### Custom Log Levels ### Custom Log Levels
Additionally, you have the flexibility to override the log level for individual tags (but not lower than the global log level). before you include `log.h`, you can define custom log levels by defining the following macros:
This can be archived by calling the following function:
```c ```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 ### 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 ### Log Output Format
Each log entry is formatted to include the following information: 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) - Timestamp (in milliseconds since boot)
- Tag - Tag
- The log message - The log message
For instance, a log entry may look like this: 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 ### Example
```c ```c
#define LOGGER_LEVEL_INFO
#include "log.h" #include "log.h"
// Don't use a define for the tag, as the pointer to the tag is used // Don't use a define for the tag, as the pointer to the tag is used
static const char *TAG = "main"; static const char *TAG = "main";
int main(void) { 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_DEBUG(TAG, "This message will not be printed");
LOG_INFO(TAG, "This message will be printed"); LOG_INFO(TAG, "This message will be printed");
@@ -99,10 +117,10 @@ int main(void) {
``` ```
Result: Result:
``` ```
I (2009) main: This message will be printed [Info] (2009) main: This message will be printed
W (2026) main: This message will be printed [Warning] (2026) main: This message will be printed
E (2033) main: This message will be printed [Error] (2033) main: This message will be printed
I (2040) main: Iteration 0 of 3 [Info] (2040) main: Iteration 0 of 3
I (2047) main: Iteration 1 of 3 [Info] (2047) main: Iteration 1 of 3
I (2054) main: Iteration 2 of 3 [Info] (2054) main: Iteration 2 of 3
``` ```

View File

@@ -12,64 +12,64 @@
#define LOG_H #define LOG_H
#include <stdint.h> #include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <sys/unistd.h>
/** #ifdef LOGGER_LEVEL_ERROR
* @brief Log levels #define LOGGER_LEVEL 4
*/ #endif
typedef enum { #ifdef LOGGER_LEVEL_WARN
LOG_LEVEL_DEBUG, #define LOGGER_LEVEL 3
LOG_LEVEL_INFO, #endif
LOG_LEVEL_WARN, #ifdef LOGGER_LEVEL_INFO
LOG_LEVEL_ERROR, #define LOGGER_LEVEL 2
} log_level_t; #endif
#ifdef LOGGER_LEVEL_ALL
#define LOGGER_LEVEL 1
#endif
/* For internal use only. #ifndef LOGGER_LEVEL
* Use the LOG_* macros instead e.g., LOG_DEBUG(TAG, "Debug message"); #define LOGGER_LEVEL 3
*/ #endif
uint32_t logger_get_timestamp(void); #ifndef LOGGER_USE_COLOR
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.
*/
#define LOGGER_USE_COLOR 0 #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 #if LOGGER_USE_COLOR
#define LOG_RESET_COLOR "\033[0m" #define LOG_RESET_COLOR "\033[0m"
#define LOG_COLOR_E "\033[0;31m" // Red #define LOG_COLOR_E "\033[0;31m" // Red
#define LOG_COLOR_W "\033[0;33m" // Brown #define LOG_COLOR_W "\033[0;33m" // Brown
#define LOG_COLOR_I "\033[0;32m" // Green #define LOG_COLOR_I "\033[0;32m" // Green
#define LOG_COLOR_D #define LOG_COLOR_D LOG_RESET_COLOR // Default
#else #else
#define LOG_RESET_COLOR #define LOG_RESET_COLOR
#define LOG_COLOR_E #define LOG_COLOR_E
@@ -78,41 +78,29 @@ log_level_t logger_get_log_level(const char* tag);
#define LOG_COLOR_D #define LOG_COLOR_D
#endif #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, ...) \ /* For internal use only.
do { \ * Use the LOG_* macros instead e.g., LOG_DEBUG(TAG, "Debug message");
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)
*/ */
#define LOG_DEBUG(tag, format, ...) LOGGER_LOG(LOG_LEVEL_DEBUG, tag, format, ##__VA_ARGS__) uint32_t logger_get_timestamp(void);
int _write(int file, char *data, int len);
/**
* @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

View File

@@ -1,7 +1,7 @@
/** /**
* @file log.c * @file log.c
* @brief Logger implementation * @brief Logger implementation
* @author Lorenz C. * @author Lorenz C. && Speetjens S.
*/ */
#include <stdarg.h> #include <stdarg.h>
@@ -10,71 +10,33 @@
#include "stm32f7xx_hal.h" #include "stm32f7xx_hal.h"
#include "log.h" #include "log.h"
/** extern UART_HandleTypeDef huart1;
* @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;
}
/** /**
* @brief Get the current timestamp to be used in the logger * @brief Get the current timestamp to be used in the logger
* *
* @return The current timestamp in milliseconds since boot * @return The current timestamp in milliseconds since boot
*/ */
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;
}
uint32_t logger_get_timestamp(void) { uint32_t logger_get_timestamp(void) {
return HAL_GetTick(); return HAL_GetTick();
} }
/**
* @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);
}

View File

@@ -22,8 +22,7 @@
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
#include <errno.h> #include "log.h"
#include <sys/unistd.h>
/* USER CODE END Includes */ /* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/
@@ -431,24 +430,6 @@ static void MX_GPIO_Init(void)
} }
/* USER CODE BEGIN 4 */ /* 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 */ /* USER CODE END 4 */
/** /**