Add a logging system for uniform log and debugging messages

This commit is contained in:
L-diy
2023-10-31 18:51:48 +01:00
parent 5e487e3c1a
commit 5cd054dced
3 changed files with 276 additions and 0 deletions

118
project/Core/Inc/log.h Normal file
View File

@@ -0,0 +1,118 @@
/**
* @file log.h
* @brief Logger header
* @author Lorenz C.
*
* This logging library provides a simple logging interface with different verbosity levels.
* Each tag can have its own log level.
* Only messages with a log level greater or equal to the log level of the tag and the global log level will be printed.
*/
#ifndef LOG_H
#define LOG_H
#include <stdint.h>
/**
* @brief Log levels
*/
typedef enum {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARN,
LOG_LEVEL_ERROR,
} log_level_t;
/* 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.
*/
#define LOGGER_USE_COLOR 0
#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
#else
#define LOG_RESET_COLOR
#define LOG_COLOR_E
#define LOG_COLOR_W
#define LOG_COLOR_I
#define LOG_COLOR_D
#endif
#define LOG_FORMAT(letter, format) LOG_COLOR_##letter #letter " (%lu) %s: " format LOG_RESET_COLOR "\r\n"
#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)
*/
#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

80
project/Core/Src/log.c Normal file
View File

@@ -0,0 +1,80 @@
/**
* @file log.c
* @brief Logger implementation
* @author Lorenz C.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#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;
}
/**
* @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();
}
/**
* @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);
}