diff options
author | David Robillard <d@drobilla.net> | 2021-01-13 20:00:25 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-14 01:13:45 -0500 |
commit | 0825ceb561b2f52cfa253cb8bb0613896f903363 (patch) | |
tree | 461d338415debf2fa9b489b53db738fbf7f8ddd8 /include/serd | |
parent | af81ace5f5a8f4bb0df93dd937395c65e92a5b6a (diff) | |
download | serd-0825ceb561b2f52cfa253cb8bb0613896f903363.tar.gz serd-0825ceb561b2f52cfa253cb8bb0613896f903363.tar.bz2 serd-0825ceb561b2f52cfa253cb8bb0613896f903363.zip |
Add extensible logging API
Diffstat (limited to 'include/serd')
-rw-r--r-- | include/serd/serd.h | 216 |
1 files changed, 193 insertions, 23 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h index b0892573..403e637c 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -66,6 +66,14 @@ SERD_API \ SERD_MALLOC_FUNC +#if defined(__MINGW32__) +# define SERD_LOG_FUNC(fmt, a) __attribute__((format(gnu_printf, fmt, a))) +#elif defined(__GNUC__) +# define SERD_LOG_FUNC(fmt, a) __attribute__((format(printf, fmt, a))) +#else +# define SERD_LOG_FUNC(fmt, a) +#endif + #ifdef __cplusplus extern "C" { # if defined(__GNUC__) @@ -1183,23 +1191,6 @@ serd_statement_equals(const SerdStatement* SERD_NULLABLE a, /// Global library state typedef struct SerdWorldImpl SerdWorld; -/// An error description -typedef struct { - SerdStatus status; ///< Error code - const SerdCaret* SERD_NULLABLE caret; ///< File origin of error - const char* SERD_NONNULL fmt; ///< Printf-style format string - va_list* SERD_NONNULL args; ///< Arguments for fmt -} SerdError; - -/** - Callback function for errors. - - @param handle Handle for user data. - @param error Error description. -*/ -typedef SerdStatus (*SerdErrorFunc)(void* SERD_NULLABLE handle, - const SerdError* SERD_NONNULL error); - /** Create a new Serd World. @@ -1237,16 +1228,195 @@ const SerdNode* SERD_NONNULL serd_world_get_blank(SerdWorld* SERD_NONNULL world); /** - Set a function to be called when errors occur. + @} + @defgroup serd_logging Logging + @{ +*/ + +/// Log entry level, compatible with syslog +typedef enum { + SERD_LOG_LEVEL_EMERGENCY, ///< Emergency, system is unusable + SERD_LOG_LEVEL_ALERT, ///< Action must be taken immediately + SERD_LOG_LEVEL_CRITICAL, ///< Critical condition + SERD_LOG_LEVEL_ERROR, ///< Error + SERD_LOG_LEVEL_WARNING, ///< Warning + SERD_LOG_LEVEL_NOTICE, ///< Normal but significant condition + SERD_LOG_LEVEL_INFO, ///< Informational message + SERD_LOG_LEVEL_DEBUG ///< Debug message +} SerdLogLevel; + +/** + A structured log field. + + Fields are used to add metadata to log messages. Syslog-compatible keys + should be used where possible, otherwise, keys should be namespaced to + prevent clashes. + + Serd itself uses the following keys: - The `error_func` will be called with `handle` as its first argument. If - no error function is set, errors are printed to stderr. + - ERRNO - The `errno` of the original system error if any (decimal string) + - SERD_COL - The 1-based column number in the file (decimal string) + - SERD_FILE - The file which caused this message (string) + - SERD_LINE - The 1-based line number in the file (decimal string) +*/ +typedef struct { + const char* SERD_NONNULL key; ///< Field name + const char* SERD_NONNULL value; ///< Field value +} SerdLogField; + +/** + Function for handling log messages. + + By default, the log is printed to `stderr`. This can be overridden by + passing a function of this type to serd_set_log_func(). + + @param handle Pointer to opaque user data. + @param level Log level. + @param n_fields Number of entries in `fields`. + @param fields An array of `n_fields` extra log fields. + @param message Log message. +*/ +typedef SerdStatus (*SerdLogFunc)(void* SERD_NULLABLE handle, + SerdLogLevel level, + size_t n_fields, + const SerdLogField* SERD_NULLABLE fields, + SerdStringView message); + +/// A #SerdLogFunc that does nothing (for suppressing log output) +SERD_CONST_API +SerdStatus +serd_quiet_log_func(void* SERD_NULLABLE handle, + SerdLogLevel level, + size_t n_fields, + const SerdLogField* SERD_NULLABLE fields, + SerdStringView message); + +/** + Set a function to be called with log messages (typically errors). + + If no custom logging function is set, then messages are printed to stderr. + + @param world World that will send log entries to the given function. + + @param log_func Log function to call for every log message. Each call to + this function represents a complete log message with an implicit trailing + newline. + + @param handle Opaque handle that will be passed to every invocation of + `log_func`. */ SERD_API void -serd_world_set_error_func(SerdWorld* SERD_NONNULL world, - SerdErrorFunc SERD_NULLABLE error_func, - void* SERD_NULLABLE handle); +serd_set_log_func(SerdWorld* SERD_NONNULL world, + SerdLogFunc SERD_NULLABLE log_func, + void* SERD_NULLABLE handle); + +/** + Write a message to the log with a `va_list`. + + This is the fundamental and most powerful function for writing entries to + the log, the others are convenience wrappers that ultimately call this. + + This writes a single complete entry to the log, and so may not be used to + print parts of a line like a more general printf-like function. There + should be no trailing newline in `fmt`. Arguments following `fmt` should + correspond to conversion specifiers in the format string as in printf from + the standard C library. + + @param world World to log to. + @param level Log level. + @param n_fields Number of entries in `fields`. + @param fields An array of `n_fields` extra log fields. + @param fmt Format string. + @param args Arguments for `fmt`. + + @return A status code, which is always #SERD_SUCCESS with the default log + function. If a custom log function is set with serd_set_log_func() and it + returns an error, then that error is returned here. +*/ +SERD_API +SERD_LOG_FUNC(5, 0) +SerdStatus +serd_vxlogf(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + size_t n_fields, + const SerdLogField* SERD_NULLABLE fields, + const char* SERD_NONNULL fmt, + va_list args); + +/** + Write a message to the log with extra fields. + + This is a convenience wrapper for serd_vxlogf() that takes the format + arguments directly. +*/ +SERD_API +SERD_LOG_FUNC(5, 6) +SerdStatus +serd_xlogf(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + size_t n_fields, + const SerdLogField* SERD_NULLABLE fields, + const char* SERD_NONNULL fmt, + ...); + +/** + Write a simple message to the log. + + This is a convenience wrapper for serd_vxlogf() which sets no extra fields. +*/ +SERD_API +SERD_LOG_FUNC(3, 0) +SerdStatus +serd_vlogf(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + const char* SERD_NONNULL fmt, + va_list args); + +/** + Write a simple message to the log. + + This is a convenience wrapper for serd_vlogf() that takes the format + arguments directly. +*/ +SERD_API +SERD_LOG_FUNC(3, 4) +SerdStatus +serd_logf(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + const char* SERD_NONNULL fmt, + ...); + +/** + Write a message to the log with a caret position. + + This is a convenience wrapper for serd_vxlogf() which sets `SERD_FILE`, + `SERD_LINE`, and `SERD_COL` to the position of the given caret. Entries are + typically printed with a GCC-style prefix like "file.ttl:16:4". +*/ +SERD_API +SERD_LOG_FUNC(4, 0) +SerdStatus +serd_vlogf_at(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + const SerdCaret* SERD_NULLABLE caret, + const char* SERD_NONNULL fmt, + va_list args); + +/** + Write a message to the log with a caret position. + + This is a convenience wrapper for serd_vlogf_at() that takes the format + arguments directly. +*/ +SERD_API +SERD_LOG_FUNC(4, 5) +SerdStatus +serd_logf_at(const SerdWorld* SERD_NONNULL world, + SerdLogLevel level, + const SerdCaret* SERD_NULLABLE caret, + const char* SERD_NONNULL fmt, + ...); /** @} |