aboutsummaryrefslogtreecommitdiff
path: root/any_log.h
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-03-21 11:11:57 +0100
committerFederico Angelilli <code@fedang.net>2024-03-21 11:11:57 +0100
commit2f7bf739f50c680d2e599d3fee6aec8a515b58e0 (patch)
tree1c3f051bd9d5a4dfe27d794ae26bde633e7eebc1 /any_log.h
parent7ce815bb11eef55531e594bfc934a9b96a43b381 (diff)
Add documentation
Diffstat (limited to 'any_log.h')
-rw-r--r--any_log.h165
1 files changed, 134 insertions, 31 deletions
diff --git a/any_log.h b/any_log.h
index dd865a9..7633091 100644
--- a/any_log.h
+++ b/any_log.h
@@ -68,27 +68,108 @@ typedef enum {
#define ANY_LOG_FUNC __func__
#endif
-// Panic has special handling
+// log_panic is implemented with the function any_log_panic, which takes
+// some extra parameters compared with the other log levels. This way we can
+// include as many information as possible for identifying fatal errors.
+//
+// You can change the format string and exit function in the implementation
+// (see ANY_LOG_EXIT, ANY_LOG_PANIC_BEFORE and ANY_LOG_PANIC_AFTER).
+//
+// NOTE: log_panic will always terminate the program and should be used only
+// for non recoverable situations! For normal errors just use log_error
+//
#define log_panic(...) any_log_panic(__FILE__, __LINE__, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
-// Normal logging functions
+// log_[level] provide normal printf style logging.
+//
+// The logs will be filtered according to the global log level. See any_log_level.
+//
+// You should invoke log_[level] with a format string and any number of
+// matched arguments. For example
+//
+// log_error("This is an error");
+// log_debug("The X is %d (padding %d)", X, 10);
+//
+// log_trace can be disabled completely (to avoid debug overhead) by defining
+// ANY_LOG_NO_TRACE. It is recommended to define this at a compiler level.
+//
#define log_error(...) any_log_format(ANY_LOG_ERROR, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
#define log_warn(...) any_log_format(ANY_LOG_WARN, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
#define log_info(...) any_log_format(ANY_LOG_INFO, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
#define log_debug(...) any_log_format(ANY_LOG_DEBUG, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
-// Structured logging functions
+#ifdef ANY_LOG_NO_TRACE
+#define log_trace(...)
+#else
+#define log_trace(...) any_log_format(ANY_LOG_TRACE, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
+#endif
+
+// log_value_[level] provide structured logging.
+//
+// The logs will be filtered according to the global log level. See any_log_level.
+//
+// You should always pass a message string (printf style specifiers are ignored)
+// and some key-value pairs.
+//
+// The key are simply strings. It is advised to pass only literals for security.
+//
+// The value can be of type int, unsigned int, pointer (void *), double and
+// string (char *).
+//
+// The value type is specified by a type specifier at the start of the key
+// string and should be like so
+//
+// key = (type_specifier ANY_LOG_VALUE_TYPE_SEP)? ...
+//
+// By default ANY_LOG_VALUE_TYPE_SEP is the character ':'.
+//
+// The possible type_specifiers are
+// d (or i): int
+// x (or u): unsigned int
+// p: void *
+// f: double
+// s: char *
+//
+// If no type specifier is given the function will assume the type given
+// by ANY_LOG_VALUE_DEFAULT_TYPE (by default string).
+//
+// For example
+//
+// log_value_info("Created graphical context",
+// "d:width", width,
+// "d:height", height,
+// "p:window", window_handle,
+// "f:scale", scale_factor_dpi,
+// "appname", "nice app");
+//
+// In the implementation you can customize the format of every key-value pair
+// and of the message. This is useful if you want to adhere to a structured
+// logging format like JSON. For example
+//
+// #define ANY_LOG_IMPLEMENT
+// #define ANY_LOG_VALUE_BEFORE(level, module, func, message) \
+// "{\"module\": \"%s\", \"function\": \"%s\", \"level\": \"%s\", \"message\": \"%s\"", \
+// module, func, any_log_level_strings[level], message
+//
+// #define ANY_LOG_VALUE_INT(key, value) "\"%s\": %d", key, value
+// #define ANY_LOG_VALUE_HEX(key, value) "\"%s\": %u", key, value
+// #define ANY_LOG_VALUE_PTR(key, value) "\"%s\": \"%p\"", key, value
+// #define ANY_LOG_VALUE_DOUBLE(key, value) "\"%s\": %lf", key, value
+// #define ANY_LOG_VALUE_STRING(key, value) "\"%s \": \"%s\"", key, value
+// #define ANY_LOG_VALUE_AFTER(level, module, func, message) "}\n"
+// #include "any_log.h"
+//
+// As with log_trace, log_value_trace can be disabled completely by defining
+// ANY_LOG_NO_TRACE.
+//
#define log_value_error(...) any_log_value(ANY_LOG_ERROR, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
#define log_value_warn(...) any_log_value(ANY_LOG_WARN, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
#define log_value_info(...) any_log_value(ANY_LOG_INFO, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
#define log_value_debug(...) any_log_value(ANY_LOG_DEBUG, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
-// Optional trace log level
#ifdef ANY_LOG_NO_TRACE
-#define log_trace(...)
#define log_value_trace(...)
#else
-#define log_trace(...) any_log_format(ANY_LOG_TRACE, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__)
#define log_value_trace(...) any_log_value(ANY_LOG_TRACE, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
#endif
@@ -98,8 +179,22 @@ typedef enum {
#define ANY_LOG_ATTRIBUTE(...)
#endif
+// All log functions will ignore the message if the level is below the
+// threshold specified in any_log_level.
+//
+// To modify the log level you can assign a any_log_level_t to this global.
+//
+// By default it has value ANY_LOG_LEVEL_DEFAULT (see implementation).
+//
extern any_log_level_t any_log_level;
+// An array containing the strings corresponding to the log levels.
+//
+// Can be modified in the implementation by defining the macros [level]_STRING.
+//
+// The functions any_log_level_to_string and any_log_level_from_string are
+// provided for easy conversion.
+//
extern const char *any_log_level_strings[ANY_LOG_ALL];
ANY_LOG_ATTRIBUTE(pure)
@@ -108,15 +203,21 @@ const char *any_log_level_to_string(any_log_level_t level);
ANY_LOG_ATTRIBUTE(pure)
any_log_level_t any_log_level_from_string(const char *string);
+// NOTE: You should never call the functions below directly!
+// See the above explanations on how to use logging.
+
ANY_LOG_ATTRIBUTE(format(printf, 4, 5))
+ANY_LOG_ATTRIBUTE(nonnull(4))
void any_log_format(any_log_level_t level, const char *module,
const char *func, const char *format, ...);
+ANY_LOG_ATTRIBUTE(nonnull(4))
void any_log_value(any_log_level_t level, const char *module,
const char *func, const char *message, ...);
ANY_LOG_ATTRIBUTE(noreturn)
ANY_LOG_ATTRIBUTE(format(printf, 5, 6))
+ANY_LOG_ATTRIBUTE(nonnull(1, 4))
void any_log_panic(const char *file, int line, const char *module,
const char *func, const char *format, ...);
@@ -129,32 +230,29 @@ void any_log_panic(const char *file, int line, const char *module,
#include <stdlib.h>
#include <string.h>
+// The default value for any_log_level
#ifndef ANY_LOG_LEVEL_DEFAULT
#define ANY_LOG_LEVEL_DEFAULT ANY_LOG_INFO
#endif
any_log_level_t any_log_level = ANY_LOG_LEVEL_DEFAULT;
+// Log level strings
#ifndef ANY_LOG_PANIC_STRING
#define ANY_LOG_PANIC_STRING "panic"
#endif
-
#ifndef ANY_LOG_ERROR_STRING
#define ANY_LOG_ERROR_STRING "error"
#endif
-
#ifndef ANY_LOG_WARN_STRING
#define ANY_LOG_WARN_STRING "warn"
#endif
-
#ifndef ANY_LOG_INFO_STRING
#define ANY_LOG_INFO_STRING "info"
#endif
-
#ifndef ANY_LOG_DEBUG_STRING
#define ANY_LOG_DEBUG_STRING "debug"
#endif
-
#ifndef ANY_LOG_TRACE_STRING
#define ANY_LOG_TRACE_STRING "trace"
#endif
@@ -184,10 +282,12 @@ any_log_level_t any_log_level_from_string(const char *string)
return ANY_LOG_ALL;
}
+// Format for any_log_format (used at the start)
#ifndef ANY_LOG_FORMAT_BEFORE
#define ANY_LOG_FORMAT_BEFORE(level, module, func) "[%s %s] %s: ", module, func, any_log_level_strings[level]
#endif
+// Format for any_log_format (used at the end)
#ifndef ANY_LOG_FORMAT_AFTER
#define ANY_LOG_FORMAT_AFTER(level, module, func) "\n"
#endif
@@ -213,58 +313,59 @@ void any_log_format(any_log_level_t level, const char *module,
(void)func;
}
-// NOTE: Must be a character
-#ifndef ANY_LOG_VALUE_TYPE_SEP
-#define ANY_LOG_VALUE_TYPE_SEP ':'
-#endif
-
+// Format for any_log_value (used at the start)
#ifndef ANY_LOG_VALUE_BEFORE
#define ANY_LOG_VALUE_BEFORE(level, module, func, message) "[%s %s] %s: %s [", module, func, any_log_level_strings[level], message
#endif
+// Format for any_log_value (used at the end)
+#ifndef ANY_LOG_VALUE_AFTER
+#define ANY_LOG_VALUE_AFTER(level, module, func, message) "]\n"
+#endif
+
+// This is used in the parsing of the type specifier from the key
+//
+// NOTE: It must be a character
+#ifndef ANY_LOG_VALUE_TYPE_SEP
+#define ANY_LOG_VALUE_TYPE_SEP ':'
+#endif
+
+// Format for pairs with an int value
#ifndef ANY_LOG_VALUE_INT
#define ANY_LOG_VALUE_INT(key, value) "%s=%d", key, value
#endif
+// Format for pairs with an unsinged int value (hex by default)
#ifndef ANY_LOG_VALUE_HEX
#define ANY_LOG_VALUE_HEX(key, value) "%s=%#x", key, value
#endif
+// Format for pairs with a pointer value
#ifndef ANY_LOG_VALUE_PTR
#define ANY_LOG_VALUE_PTR(key, value) "%s=%p", key, value
#endif
+// Format for pairs with a double value
#ifndef ANY_LOG_VALUE_DOUBLE
#define ANY_LOG_VALUE_DOUBLE(key, value) "%s=%lf", key, value
#endif
+// Format for pairs with a string value
#ifndef ANY_LOG_VALUE_STRING
#define ANY_LOG_VALUE_STRING(key, value) "%s=\"%s\"", key, value
#endif
+// The default is to use string
#ifndef ANY_LOG_VALUE_DEFAULT
-#define ANY_LOG_VALUE_DEFAULT ANY_LOG_VALUE_STRING
+#define ANY_LOG_VALUE_DEFAULT(key, value) ANY_LOG_VALUE_STRING(key, value)
#define ANY_LOG_VALUE_DEFAULT_TYPE char *
#endif
+// This is used as a separator between different pairs
#ifndef ANY_LOG_VALUE_PAIR_SEP
#define ANY_LOG_VALUE_PAIR_SEP ", "
#endif
-#ifndef ANY_LOG_VALUE_AFTER
-#define ANY_LOG_VALUE_AFTER(level, module, func, message) "]\n"
-#endif
-
-// Possible value type specifier
-//
-// d (or i): int
-// x (or u): unsigned int
-// p: void *
-// f: double
-// s: string
-//
-// NOTE: If no type specifier is given the function will assume type string
-//
void any_log_value(any_log_level_t level, const char *module,
const char *func, const char *message, ...)
{
@@ -339,10 +440,12 @@ tdefault:
#define ANY_LOG_EXIT(file, line, module, func) abort()
#endif
+// Format for any_log_panic (used at the start)
#ifndef ANY_LOG_PANIC_BEFORE
#define ANY_LOG_PANIC_BEFORE(file, line, module, func) "[%s %s] %s: ", module, func, any_log_level_strings[ANY_LOG_PANIC]
#endif
+// Format for any_log_panic (used at the start)
#ifndef ANY_LOG_PANIC_AFTER
#define ANY_LOG_PANIC_AFTER(file, line, module, func) "\npanic was invoked from %s:%d (%s)\n", file, line, module
#endif