Files
hammy/third_party/concord/logmod.h
T
2026-08-28 23:31:23 +02:00

1097 lines
39 KiB
C

#ifndef LOGMOD_H
#define LOGMOD_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Define symbol visibility for LogMod API
*
* Define LOGMOD_STATIC to make all symbols static (for single-file inclusion)
*/
#ifdef LOGMOD_STATIC
#define LOGMOD_API static
#else
#define LOGMOD_API extern
#endif /* LOGMOD_STATIC */
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
/**
* @brief Format string checking attribute for printf-like functions
*
* This macro adds format string checking when supported by the compiler,
* helping catch format string errors at compile time.
*
* @param a Format string parameter position
* @param b First variadic argument position
*/
#if defined(__MINGW32__) \
|| (defined(__GNUC__) && __GNUC__ > 4 ? true : __GNUC_PATCHLEVEL__ >= 4) \
|| defined(__USE_MINGW_ANSI_STDIO)
#define LOGMOD_PRINTF_LIKE(a, b) __attribute__((format(gnu_printf, a, b)))
#else
#define LOGMOD_PRINTF_LIKE(a, b)
#endif
/**
* @brief Default application ID for the fallback logger
*
* Can be overridden by defining this macro before including logmod.h
*/
#ifndef LOGMOD_FALLBACK_APPLICATION_ID
#define LOGMOD_FALLBACK_APPLICATION_ID "APPLICATION"
#endif /* LOGMOD_FALLBACK_APPLICATION_ID */
/**
* @brief Default context ID for the fallback logger
*
* Can be overridden by defining this macro before including logmod.h
*/
#ifndef LOGMOD_FALLBACK_CONTEXT_ID
#define LOGMOD_FALLBACK_CONTEXT_ID "GLOBAL"
#endif /* LOGMOD_FALLBACK_CONTEXT_ID */
/**
* @brief Log levels supported by LogMod
*
* Built-in log levels ordered by severity (lowest to highest)
* Custom log levels should start at LOGMOD_LEVEL_CUSTOM
*/
enum logmod_levels {
LOGMOD_LEVEL_TRACE = 0, /**< Most detailed logging for tracing execution */
LOGMOD_LEVEL_DEBUG, /**< Debug information */
LOGMOD_LEVEL_INFO, /**< Informational messages */
LOGMOD_LEVEL_WARN, /**< Warning messages */
LOGMOD_LEVEL_ERROR, /**< Error messages */
LOGMOD_LEVEL_FATAL, /**< Fatal errors that cause termination */
__LOGMOD_LEVEL_MAX, /**< Internal marker for built-in level count */
/** user defined log level must start with this value */
LOGMOD_LEVEL_CUSTOM = __LOGMOD_LEVEL_MAX
};
/**
* @brief Error codes returned by LogMod functions
*/
typedef enum {
LOGMOD_ERRNO = -2, /**< System error, check errno */
LOGMOD_BAD_PARAMETER = -1, /**< Invalid parameter passed to function */
LOGMOD_OK = 0, /**< Success */
LOGMOD_OK_CONTINUE, /**< Success, continue with default behavior */
LOGMOD_OK_SKIPPED /**< Success, logger has been skipped from logging due to
being disabled by user */
} logmod_err;
/**
* @brief Configuration options for a logger
*/
struct logmod_options {
FILE *logfile; /**< File to write logs to, or NULL for no file output */
int quiet; /**< If 1, suppress console output */
int color; /**< If 1, enable ANSI colors on console output */
int hide_context_id; /**< If 1, suppress context ID in log messages */
int show_application_id; /**< If 1, show application ID in log messages */
unsigned level; /**< Minimum level to log (suppress messages below this) */
int suppress_time; /**< If 1, suppress time in log messages */
int hide_counter; /**< If 1, hide message counter in log messages */
};
/**
* @brief Label properties for a log level
*
* @see @ref LOGMOD_LABEL_COLOR
*/
struct logmod_label {
const char *const name; /**< Display name of the log level */
const char *const style; /**< ANSI style code for this label */
const char *const visibility; /**< ANSI visibility code for this label */
const char *const color; /**< ANSI color code for this label */
const int output; /**< Output stream: 0 = stdout, 1 = stderr */
};
/**
* @brief Helper macro to define label color, style, and visibility at once
*
* @param _style Style name (e.g., BOLD, REGULAR)
* @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND)
* @param _color Color name (e.g., RED, GREEN)
*/
#define LOGMOD_LABEL_COLOR(_style, _visibility, _color) \
LOGMOD_STYLE_##_style, LOGMOD_VISIBILITY_##_visibility, \
LOGMOD_COLOR_##_color
/**
* @brief Information about a log entry
*/
struct logmod_info {
const unsigned line; /**< Source line number */
const char *const filename; /**< Source filename */
const unsigned level; /**< Log level */
const struct logmod_label *const label; /**< Label for this log level */
const struct tm time; /**< Time for when entry has been triggered */
};
/* forward declaration */
struct logmod_logger;
struct tm;
/**/
/**
* @brief Lock function type for thread safety
*
* @param logger The logger instance
* @param should_lock 1 to acquire lock, 0 to release
*/
typedef void (*logmod_lock)(const struct logmod_logger *logger,
int should_lock);
/**
* @brief Callback function type for custom log handling
*
* @param logger The logger instance
* @param info Information about the log entry
* @param fmt Format string
* @param args Variable arguments list
* @return LOGMOD_OK to skip default handling, LOGMOD_OK_CONTINUE to also
* perform default handling
*/
typedef logmod_err (*logmod_callback)(const struct logmod_logger *logger,
const struct logmod_info *info,
const char *fmt,
va_list args);
/**
* @brief ANSI text style values
*/
#define LOGMOD_STYLE_REGULAR "0" /**< Regular text style */
#define LOGMOD_STYLE_BOLD "1" /**< Bold text style */
#define LOGMOD_STYLE_UNDERLINE "4" /**< Underlined text style */
#define LOGMOD_STYLE_STRIKETHROUGH "9" /**< Strikethrough text style */
/**
* @brief ANSI text visibility mode values
*/
#define LOGMOD_VISIBILITY_FOREGROUND "3" /**< Foreground color */
#define LOGMOD_VISIBILITY_BACKGROUND "4" /**< Background color */
#define LOGMOD_VISIBILITY_INTENSITY "9" /**< Intensity (bright) foreground */
#define LOGMOD_VISIBILITY_BACKGROUND_INTENSITY \
"10" /**< Intensity (bright) background */
/**
* @brief ANSI color values
*/
#define LOGMOD_COLOR_BLACK "0" /**< Black color */
#define LOGMOD_COLOR_RED "1" /**< Red color */
#define LOGMOD_COLOR_GREEN "2" /**< Green color */
#define LOGMOD_COLOR_YELLOW "3" /**< Yellow color */
#define LOGMOD_COLOR_BLUE "4" /**< Blue color */
#define LOGMOD_COLOR_MAGENTA "5" /**< Magenta color */
#define LOGMOD_COLOR_CYAN "6" /**< Cyan color */
#define LOGMOD_COLOR_WHITE "7" /**< White color */
/**
* @brief Internal macro for defining logger attributes
*
* Used to define both const and non-const versions of the logger structure
* with the same fields.
*
* @param _qualifier Qualifier to apply to mutable fields (const or empty)
*/
#define __LOGMOD_LOGGER_ATTRS(_qualifier) \
const char *context_id; \
_qualifier struct logmod_options options; \
const long *counter; \
_qualifier logmod_callback callback; \
void *user_data; \
const struct logmod_label *_qualifier custom_labels; \
_qualifier size_t num_custom_labels; \
_qualifier int disabled
#define __BLANK
/**
* @brief Mutable version of the logger structure
*
* This version allows modification of the logger properties
*/
struct logmod_mut_logger {
__LOGMOD_LOGGER_ATTRS(__BLANK);
};
#undef __BLANK
/**
* @brief Immutable logger structure
*
* This is the public-facing version of the logger with const-qualified fields
*/
struct logmod_logger {
__LOGMOD_LOGGER_ATTRS(const);
};
#undef __LOGMOD_LOGGER_ATTRS
/**
* @brief Main logging context structure
*
* Contains the global state for a logging context
*/
struct logmod {
const char *application_id; /**< Application identifier */
const struct logmod_logger *loggers; /**< Array of loggers */
const size_t length; /**< Current number of loggers */
const size_t real_length; /**< Maximum capacity of loggers array */
long counter; /**< Global log message counter */
const struct logmod_options
default_options; /**< Default options for new loggers */
logmod_lock lock; /**< Lock function for thread safety */
};
/**
* @brief Initialize the logging context
*
* @param logmod Pointer to the logging context structure
* @param application_id Application identifier string
* @param table Array to store loggers (must be pre-allocated)
* @param length Capacity of the table array
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_init(struct logmod *logmod,
const char *const application_id,
struct logmod_logger table[],
unsigned length);
/**
* @brief Clean up the logging context
*
* @param logmod Pointer to the logging context structure
* @return LOGMOD_OK on success
*/
LOGMOD_API logmod_err logmod_cleanup(struct logmod *logmod);
/**
* @brief Set the lock function for thread safety
*
* @param logmod Pointer to the logging context structure
* @param lock Lock function to use
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_set_lock(struct logmod *logmod, logmod_lock lock);
/**
* @brief Set default options for all new loggers
*
* @param logmod Pointer to the logging context structure
* @param options Default options to use for new loggers
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_set_options(struct logmod *logmod,
struct logmod_options options);
/**
* @brief Toggle a specific logger from logging (default is true)
*
* @param logmod Pointer to the logging context structure
* @param context_id The context id of the logger to be enabled or disabled
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_toggle_logger(struct logmod *logmod,
const char *const context_id);
/**
* @brief Set user data for a logger
*
* @param logger Pointer to the logger
* @param user_data Pointer to user-defined data
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_data(struct logmod_logger *logger,
void *user_data);
/**
* @brief Set callback function and custom labels for a logger
*
* @param logger Pointer to the logger
* @param custom_labels Array of custom log level labels or NULL
* @param num_custom_labels Number of custom labels in the array
* @param callback Callback function for log processing
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err
logmod_logger_set_callback(struct logmod_logger *logger,
const struct logmod_label *const custom_labels,
const size_t num_custom_labels,
logmod_callback callback);
/**
* @brief Set all options for a logger at once
*
* @param logger Pointer to the logger
* @param options Options structure
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_options(struct logmod_logger *logger,
struct logmod_options options);
/**
* @brief Set visibility of application ID and context ID in log messages
*
* @param logger Pointer to the logger
* @param show_app_id 1 to show application ID, 0 to hide
* @param show_context_id 1 to show context ID, 0 to hide
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_id_visibility(
struct logmod_logger *logger, int show_app_id, int show_context_id);
/**
* @brief Set quiet mode for a logger
*
* @param logger Pointer to the logger
* @param quiet 1 to enable quiet mode (suppress console output), 0 to disable
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_quiet(struct logmod_logger *logger,
int quiet);
/**
* @brief Set color mode for a logger
*
* @param logger Pointer to the logger
* @param color 1 to enable colored output, 0 to disable
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_color(struct logmod_logger *logger,
int color);
/**
* @brief Set minimum log level for a logger
*
* @param logger Pointer to the logger
* @param level Minimum level to log (messages below this will be suppressed)
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_level(struct logmod_logger *logger,
unsigned level);
/**
* @brief Set logfile for a logger
*
* @param logger Pointer to the logger
* @param logfile File pointer to write logs to, or NULL to disable
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_logfile(struct logmod_logger *logger,
FILE *logfile);
/**
* @brief Set time display for a logger
*
* @param logger Pointer to the logger
* @param show_time 0 to suppress time in log messages, 1 to show it
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_time(struct logmod_logger *logger,
int show_time);
/**
* @brief Set counter display for a logger
*
* @param logger Pointer to the logger
* @param show_counter 0 to hide counter in log messages, 1 to show it
* @return LOGMOD_OK on success, error code on failure
*/
LOGMOD_API logmod_err logmod_logger_set_counter(struct logmod_logger *logger,
int show_counter);
/**
* @brief Get or create a logger by context ID
*
* @param logmod Pointer to the logging context
* @param context_id Context identifier string
* @return Pointer to the logger, or NULL if the table is full
*/
LOGMOD_API struct logmod_logger *logmod_get_logger(
struct logmod *logmod, const char *const context_id);
/**
* @brief Get the label for a specific log level
*
* @param logger Pointer to the logger
* @param level Log level to get the label for
* @return Pointer to the label structure, or NULL if invalid
*/
LOGMOD_API const struct logmod_label *logmod_logger_get_label(
const struct logmod_logger *logger, const unsigned level);
/**
* @brief Get the level value for a label name
*
* @param logger Pointer to the logger
* @param label Label name to look up
* @return Level value, or LOGMOD_BAD_PARAMETER if invalid
*/
LOGMOD_API long logmod_logger_get_level(const struct logmod_logger *logger,
const char *const label);
/**
* @brief Get the global message counter for a logger
*
* @param logger Pointer to the logger
* @return Current counter value, or -1 if logger is NULL
*/
LOGMOD_API long logmod_logger_get_counter(const struct logmod_logger *logger);
/**
* @brief Log a message (C89 compatible version)
*
* @param _level Log level (without LOGMOD_LEVEL_ prefix)
* @param _logger Logger to use, or NULL for default
* @param _parenthesized_params Format and arguments in parentheses
* @param num_params Number of arguments in the format string
*/
#define logmod_nlog(_level, _logger, _parenthesized_params, num_params) \
_logmod_log(_logger, __LINE__, __FILE__, LOGMOD_LEVEL_##_level, \
LOGMOD_SPREAD_TUPLE_##num_params _parenthesized_params)
#if __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
/**
* @brief Internal helper macro for C99 variable arguments processing
*
* @param _level The log level
* @param _logger The logger instance
* @param _line Source line number
* @param _file Source file path
* @param _fmt Format string
* @param ... Additional format arguments
*/
#define _logmod_log_permissive(_level, _logger, _line, _file, _fmt, ...) \
_logmod_log(_logger, _line, _file, _level, _fmt "%s", __VA_ARGS__)
/**
* @brief Log a message with specified level (C99 version with variadic macro
* support)
*
* @param _level Log level (e.g., INFO, DEBUG, ERROR)
* @param _logger The logger instance or NULL for default logger
* @param ... Format string followed by format arguments
*/
#define logmod_log(_level, _logger, ...) \
_logmod_log_permissive(LOGMOD_LEVEL_##_level, _logger, __LINE__, \
__FILE__, __VA_ARGS__, "")
#else
/**
* @brief Alias to logmod_nlog for C89 compatibility
*/
#define logmod_log logmod_nlog
#endif /* __STDC_VERSION__ */
/**
* @brief Internal logging implementation function
*
* @param logger The logger instance
* @param line Source line number
* @param filename Source file path
* @param level Log level
* @param fmt Format string
* @param ... Format arguments
* @return logmod_err LOGMOD_OK on success, or error code on failure
*/
LOGMOD_API logmod_err _logmod_log(const struct logmod_logger *logger,
const unsigned line,
const char *const filename,
const unsigned level,
const char *fmt,
...) LOGMOD_PRINTF_LIKE(5, 6);
/**
* @brief Encodes text with ANSI colors and styles
*
* @param _style Style code (LOGMOD_STYLE_* macros)
* @param _visibility Visibility code (LOGMOD_VISIBILITY_* macros)
* @param _color Color code (LOGMOD_COLOR_* macros)
* @param buf Text buffer to encode
*/
#define LOGMOD_ENCODE(_style, _visibility, _color, buf) \
"\x1b[" _style ";" _visibility _color "m" buf "\x1b[0m"
/**
* @brief Static encoding of text with ANSI colors (compile-time)
*
* @param _style Style name (e.g., BOLD, REGULAR)
* @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND)
* @param _color Color name (e.g., RED, GREEN)
* @param buf The text to encode
*/
#define LOGMOD_ENCODE_STATIC(_style, _visibility, _color, buf) \
LOGMOD_ENCODE(LOGMOD_STYLE_##_style, LOGMOD_VISIBILITY_##_visibility, \
LOGMOD_COLOR_##_color, buf)
/**
* @brief Dynamic encoding of text with ANSI colors (respects toggle)
*
* @param _toggle Toggle to enable or disable color encoding
* @param _style Style name (e.g., BOLD, REGULAR)
* @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND)
* @param _color Color name (e.g., RED, GREEN)
* @param buf The text to encode
* @return Colored text if toggle is enabled, or original text otherwise
*/
#define LOGMOD_ENCODE_TOGGLE(_toggle, _style, _visibility, _color, buf) \
((_toggle) ? LOGMOD_ENCODE_STATIC(_style, _visibility, _color, buf) : buf)
/**
* @brief Dynamic encoding of text with ANSI colors (respects logger color
* setting)
*
* @param _logger The logger instance to check for color enabled setting
* @param _style Style name (e.g., BOLD, REGULAR)
* @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND)
* @param _color Color name (e.g., RED, GREEN)
* @param buf The text to encode
* @return Colored text if colors enabled, or original text otherwise
*/
#define LOGMOD_ENCODE_LOGGER(_logger, _style, _visibility, _color, buf) \
LOGMOD_ENCODE_TOGGLE((_logger)->options.color, _style, _visibility, \
_color, buf)
/** @brief Shorthand for LOGMOD_ENCODE_STATIC */
#define LMS LOGMOD_ENCODE_STATIC
/** @brief Shorthand for LOGMOD_ENCODE_TOGGLE */
#define LMT LOGMOD_ENCODE_TOGGLE
/** @brief Shorthand for LOGMOD_ENCODE_LOGGER */
#define LML LOGMOD_ENCODE_LOGGER
/** @brief Shorthand for LOGMOD_ENCODE */
#define LME LOGMOD_ENCODE
#define LOGMOD_SPREAD_TUPLE_0(_fmt) _fmt
#define LOGMOD_SPREAD_TUPLE_1(_fmt, _1) _fmt, _1
#define LOGMOD_SPREAD_TUPLE_2(_fmt, _1, _2) _fmt, _1, _2
#define LOGMOD_SPREAD_TUPLE_3(_fmt, _1, _2, _3) _fmt, _1, _2, _3
#define LOGMOD_SPREAD_TUPLE_4(_fmt, _1, _2, _3, _4) _fmt, _2, _3, _4
#define LOGMOD_SPREAD_TUPLE_5(_fmt, _1, _2, _3, _4, _5) \
_fmt, _1, _2, _3, _4, _5
#define LOGMOD_SPREAD_TUPLE_6(_fmt, _1, _2, _3, _4, _5, _6) \
_fmt, _1, _2, _3, _4, _6
#define LOGMOD_SPREAD_TUPLE_7(_fmt, _1, _2, _3, _4, _5, _6, _7) \
_fmt, _1, _2, _3, _4, _5, _6, _7
#define LOGMOD_SPREAD_TUPLE_8(_fmt, _1, _2, _3, _4, _5, _6, _7, _8) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8
#define LOGMOD_SPREAD_TUPLE_9(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9
#define LOGMOD_SPREAD_TUPLE_10(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10
#define LOGMOD_SPREAD_TUPLE_11(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11
#define LOGMOD_SPREAD_TUPLE_12(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12
#define LOGMOD_SPREAD_TUPLE_13(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13
#define LOGMOD_SPREAD_TUPLE_14(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14
#define LOGMOD_SPREAD_TUPLE_15(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15
#define LOGMOD_SPREAD_TUPLE_16(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16) \
_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16
#ifndef LOGMOD_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include "logmod.h"
static const struct logmod_label default_labels[__LOGMOD_LEVEL_MAX] = {
/*[LOGMOD_LEVEL_TRACE]:*/
{ "TRACE", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND_INTENSITY, BLUE), 0 },
/*[LOGMOD_LEVEL_DEBUG]:*/
{ "DEBUG", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, CYAN), 0 },
/*[LOGMOD_LEVEL_INFO]: */
{ "INFO", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, GREEN), 0 },
/*[LOGMOD_LEVEL_WARN]: */
{ "WARN", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, YELLOW), 1 },
/*[LOGMOD_LEVEL_ERROR]:*/
{ "ERROR", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, RED), 1 },
/*[LOGMOD_LEVEL_FATAL]:*/
{ "FATAL", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, MAGENTA), 1 },
};
/** @brief Get @ref logmod from any @ref logmod_logger */
#define LOGMOD_FROM_LOGGER(_ctx) \
((struct logmod *)((char *)(_ctx)->counter \
- offsetof(struct logmod, counter)))
#define _LOGMOD_EXPECT(_cond, _error, _return) \
do { \
if (!(_cond)) { \
switch ((_error)) { \
case LOGMOD_BAD_PARAMETER: \
logmod_nlog(ERROR, NULL, ("Bad parameter: %s", #_cond), 1); \
break; \
case LOGMOD_ERRNO: \
logmod_nlog(ERROR, NULL, \
("System error (check errno): %s", #_cond), 1); \
break; \
default: \
logmod_nlog(ERROR, NULL, ("Unknown error: %s", #_cond), 1); \
break; \
} \
return (_return); \
} \
} while (0)
/**
* @brief Check if a condition is met and log an error message if not
*
* @param _cond Condition to check
* @param _error Error code to return if the condition is false
*/
#define LOGMOD_EXPECT(_cond, _error) _LOGMOD_EXPECT(_cond, _error, _error)
static void
_logmod_lock_noop(const struct logmod_logger *_, int __)
{
(void)_;
(void)__;
}
LOGMOD_API logmod_err
logmod_init(struct logmod *logmod,
const char *const application_id,
struct logmod_logger table[],
unsigned length)
{
size_t *mut_real_length = (size_t *)&logmod->real_length;
LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER);
LOGMOD_EXPECT(application_id && *application_id, LOGMOD_BAD_PARAMETER);
LOGMOD_EXPECT(table != NULL, LOGMOD_BAD_PARAMETER);
LOGMOD_EXPECT(length > 0, LOGMOD_BAD_PARAMETER);
memset(logmod, 0, sizeof *logmod);
memset(table, 0, length * sizeof *table);
logmod->application_id = application_id;
logmod->loggers = table;
*mut_real_length = length;
logmod->lock = _logmod_lock_noop;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_cleanup(struct logmod *logmod)
{
LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER);
if (logmod->loggers)
memset((void *)logmod->loggers, 0,
logmod->real_length * sizeof *logmod->loggers);
memset(logmod, 0, sizeof *logmod);
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_set_lock(struct logmod *logmod, logmod_lock lock)
{
LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER);
logmod->lock = lock;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_set_options(struct logmod *logmod, struct logmod_options options)
{
struct logmod_options *mut_default_options =
(struct logmod_options *)&logmod->default_options;
LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER);
*mut_default_options = options;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_toggle_logger(struct logmod *logmod, const char *const context_id)
{
struct logmod_mut_logger *mut_logger =
(struct logmod_mut_logger *)logmod_get_logger(logmod, context_id);
LOGMOD_EXPECT(mut_logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->disabled = !mut_logger->disabled;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_id_visibility(struct logmod_logger *logger,
int show_app_id,
int show_context_id)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.show_application_id = show_app_id;
mut_logger->options.hide_context_id = !show_context_id;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_data(struct logmod_logger *logger, void *user_data)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->user_data = user_data;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_callback(struct logmod_logger *logger,
const struct logmod_label *const custom_labels,
const size_t num_custom_labels,
logmod_callback callback)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->callback = callback;
if (custom_labels != NULL) {
LOGMOD_EXPECT(num_custom_labels > 0, LOGMOD_BAD_PARAMETER);
mut_logger->custom_labels = custom_labels;
mut_logger->num_custom_labels = num_custom_labels;
}
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_options(struct logmod_logger *logger,
struct logmod_options options)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options = options;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_quiet(struct logmod_logger *logger, int quiet)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.quiet = quiet;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_color(struct logmod_logger *logger, int color)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.color = color;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_level(struct logmod_logger *logger, unsigned level)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.level = level;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_logfile(struct logmod_logger *logger, FILE *logfile)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.logfile = logfile;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_time(struct logmod_logger *logger, int show_time)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.suppress_time = !show_time;
return LOGMOD_OK;
}
LOGMOD_API logmod_err
logmod_logger_set_counter(struct logmod_logger *logger, int show_counter)
{
struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
mut_logger->options.hide_counter = !show_counter;
return LOGMOD_OK;
}
LOGMOD_API const struct logmod_label *
logmod_logger_get_label(const struct logmod_logger *logger,
const unsigned level)
{
static const struct logmod_label unknown_label = {
"#UNKNOWN LABEL!!",
LOGMOD_LABEL_COLOR(STRIKETHROUGH, BACKGROUND_INTENSITY, YELLOW), 1
};
if (level < LOGMOD_LEVEL_CUSTOM) {
return &default_labels[level];
}
if (logger && level >= LOGMOD_LEVEL_CUSTOM
&& level < (LOGMOD_LEVEL_CUSTOM + logger->num_custom_labels))
{
return &logger->custom_labels[level - LOGMOD_LEVEL_CUSTOM];
}
logmod_nlog(ERROR, NULL,
("Invalid log level %u for logger %s", level,
logger ? logger->context_id : "NULL"),
2);
return &unknown_label;
}
LOGMOD_API long
logmod_logger_get_level(const struct logmod_logger *logger,
const char *const label)
{
size_t i;
LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER);
LOGMOD_EXPECT(label != NULL, LOGMOD_BAD_PARAMETER);
for (i = 0; i < __LOGMOD_LEVEL_MAX; ++i) {
if (strcmp(label, default_labels[i].name) == 0) {
return (long)i;
}
}
for (i = 0; i < logger->num_custom_labels; ++i) {
if (strcmp(label, logger->custom_labels[i].name) == 0) {
return (long)(i + LOGMOD_LEVEL_CUSTOM);
}
}
return LOGMOD_BAD_PARAMETER;
}
LOGMOD_API long
logmod_logger_get_counter(const struct logmod_logger *logger)
{
struct logmod *logmod = LOGMOD_FROM_LOGGER(logger);
long counter;
LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER);
logmod->lock(logger, 1);
counter = logmod->counter;
logmod->lock(logger, 0);
return counter;
}
LOGMOD_API struct logmod_logger *
logmod_get_logger(struct logmod *logmod, const char *const context_id)
{
size_t i;
_LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER, NULL);
_LOGMOD_EXPECT(logmod->loggers != NULL, LOGMOD_BAD_PARAMETER, NULL);
_LOGMOD_EXPECT(context_id != NULL, LOGMOD_BAD_PARAMETER, NULL);
logmod->lock(NULL, 1);
for (i = 0; i < logmod->length; ++i) {
if (0 == strcmp(logmod->loggers[i].context_id, context_id)) {
struct logmod_logger *logger =
(struct logmod_logger *)&logmod->loggers[i];
logmod->lock(logger, 0);
return logger;
}
}
if (logmod->length < logmod->real_length) {
size_t *mut_length = (size_t *)&logmod->length;
struct logmod_mut_logger *mut_logger =
(struct logmod_mut_logger *)&logmod->loggers[logmod->length];
memset(mut_logger, 0, sizeof *mut_logger);
mut_logger->context_id = context_id;
mut_logger->counter = &logmod->counter;
mut_logger->options = logmod->default_options;
++*mut_length;
logmod->lock((struct logmod_logger *)mut_logger, 0);
return (struct logmod_logger *)mut_logger;
}
logmod->lock(NULL, 0);
return NULL;
}
static logmod_err
_logmod_print(const struct logmod_logger *logger,
const struct logmod_info *info,
const char *fmt,
va_list args,
const int color,
FILE *output)
{
if (!logger->options.hide_counter) {
LOGMOD_EXPECT(fprintf(output,
LMT(color, BOLD, FOREGROUND, WHITE, "%-3ld "),
logmod_logger_get_counter(logger))
>= 0,
LOGMOD_ERRNO);
}
if (!logger->options.suppress_time) {
LOGMOD_EXPECT(
fprintf(output,
LMT(color, UNDERLINE, FOREGROUND, WHITE, "%02d:%02d:%02d"),
info->time.tm_hour, info->time.tm_min, info->time.tm_sec)
>= 0,
LOGMOD_ERRNO);
LOGMOD_EXPECT(putc(' ', output) != EOF, LOGMOD_ERRNO);
}
if (logger->options.show_application_id) {
const struct logmod *logmod = LOGMOD_FROM_LOGGER(logger);
LOGMOD_EXPECT(fprintf(output,
LMT(color, BOLD, FOREGROUND, BLACK, "%s"),
logmod->application_id)
>= 0,
LOGMOD_ERRNO);
LOGMOD_EXPECT(fputs(LMT(color, BOLD, FOREGROUND, BLACK, " » "), output)
!= EOF,
LOGMOD_ERRNO);
}
if (!logger->options.hide_context_id) {
LOGMOD_EXPECT(fprintf(output,
LMT(color, BOLD, FOREGROUND, WHITE, "%s"),
logger->context_id)
>= 0,
LOGMOD_ERRNO);
LOGMOD_EXPECT(fputs(LMT(color, BOLD, FOREGROUND, WHITE, " » "), output)
!= EOF,
LOGMOD_ERRNO);
}
if (color) {
LOGMOD_EXPECT(fprintf(output,
LME("%s", "%s", "%s", "%s") " " LMS(
REGULAR, FOREGROUND, YELLOW,
"%s") LMS(BOLD, FOREGROUND, WHITE, ":")
LMS(REGULAR, FOREGROUND, WHITE, "%d") ": ",
info->label->style, info->label->visibility,
info->label->color, info->label->name,
info->filename, info->line)
>= 0,
LOGMOD_ERRNO);
}
else {
LOGMOD_EXPECT(fprintf(output, "%s %s:%d: ", info->label->name,
info->filename, info->line)
>= 0,
LOGMOD_ERRNO);
}
LOGMOD_EXPECT(vfprintf(output, fmt, args) >= 0, LOGMOD_ERRNO);
LOGMOD_EXPECT(putc('\n', output) != EOF, LOGMOD_ERRNO);
LOGMOD_EXPECT(fflush(output) != EOF, LOGMOD_ERRNO);
return LOGMOD_OK;
}
static struct logmod g_logmod;
/** global logger used as a fallback */
static struct logmod_logger g_loggers[] = {
{
LOGMOD_FALLBACK_CONTEXT_ID,
{ NULL, 0, 1 },
&g_logmod.counter,
NULL,
NULL,
default_labels,
0,
},
};
/** global logmod used as a fallback */
static struct logmod g_logmod = {
LOGMOD_FALLBACK_APPLICATION_ID,
g_loggers,
sizeof(g_loggers) / sizeof *g_loggers,
sizeof(g_loggers) / sizeof *g_loggers,
0,
{ NULL, 0, 1 },
_logmod_lock_noop,
};
static struct logmod_info
_logmod_info_populate(const struct logmod_logger *logger,
const unsigned line,
const char *const filename,
const unsigned level)
{
const struct logmod *logmod = LOGMOD_FROM_LOGGER(logger);
const time_t time_raw = time(NULL);
struct logmod_info info;
unsigned *mut_line = (unsigned *)&info.line;
const char **mut_filename = (const char **)&info.filename;
unsigned *mut_level = (unsigned *)&info.level;
const struct logmod_label **mut_label =
(const struct logmod_label **)&info.label;
struct tm *mut_time = (struct tm *)&info.time;
*mut_line = line;
*mut_filename = filename;
*mut_level = level;
*mut_label = logmod_logger_get_label(logger, level);
logmod->lock(logger, 1);
*mut_time = *localtime(&time_raw);
logmod->lock(logger, 0);
return info;
}
LOGMOD_API logmod_err
_logmod_log(const struct logmod_logger *logger,
const unsigned line,
const char *const filename,
const unsigned level,
const char *fmt,
...)
{
struct logmod *logmod =
LOGMOD_FROM_LOGGER(!logger ? (logger = &g_loggers[0]) : logger);
logmod_err code = LOGMOD_OK_SKIPPED;
if (!logger->disabled) {
const struct logmod_info info =
_logmod_info_populate(logger, line, filename, level);
va_list args;
code = LOGMOD_OK_CONTINUE;
if (logger->callback) {
va_start(args, fmt);
if ((code = logger->callback(logger, &info, fmt, args))
< LOGMOD_OK)
{
goto _end;
}
va_end(args);
}
if (level >= logger->options.level && code == LOGMOD_OK_CONTINUE) {
if (!logger->options.quiet || level == LOGMOD_LEVEL_FATAL) {
va_start(args, fmt);
if ((code = _logmod_print(
logger, &info, fmt, args, logger->options.color,
info.label->output == 0 ? stdout : stderr))
!= LOGMOD_OK)
{
goto _end;
}
va_end(args);
}
if (logger->options.logfile) {
va_start(args, fmt);
code = _logmod_print(logger, &info, fmt, args, 0,
logger->options.logfile);
}
}
_end:
logmod->lock(logger, 1);
++logmod->counter;
logmod->lock(logger, 0);
va_end(args);
}
return code;
}
#undef LOGMOD_EXPECT
#endif /* LOGMOD_HEADER */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LOGMOD_H */