tl;dr: kernel logging is cumbersome. This PR delivers log entries as a struct instead of a formatted string, and simplifies the kernel logging interface. Closes #34062.
Motivation
The bitcoinkernel library (#27587) exposes functionality to interface with the kernel logging. This includes registering callbacks for log statements, level/category filtering, string formatting options, and more.
Kernel logging has a few problems:
- callbacks operate on formatted strings, so users need to parse the string to get the timestamp, category, level, ... based on which options are set. This is cumbersome, brittle, and inefficient.
- the filtering interface is not really intuitive, requiring users to call combinations of
btck_logging_set_level_categoryandbtck_logging_enable_categorywhen they want to producedebugortracelogs. The level/category system makes sense for node, because it directly controls what gets written to disk and stdout, and there are quite a lot more categories producing logs. Kernel doesn't really need this - users control what happens to the logs, and can do any filtering/manipulation in the callback they provide. - the node logging infrastructure has quite a bit more functionality than is necessary for a library, including ratelimiting, log formatting, outputting, buffering, ... This introduces unnecessary code and interface complexity.
Approach
Log generation (util/log.h: macros, util::log::Entry, and the ShouldDebugLog/ShouldTraceLog/Log hooks an application must provide) was already separated from log handling (logging.h) in #34465 and subsequent PRs. This PR gives bitcoinkernel its own implementation of those hooks, so it no longer depends on logging.cpp, and upgrades the C API to deliver struct-based entries. Node logging is not changed.
- Preparatory work: expose the missing levels and categories (
WARNING,ERROR,TXPACKAGES,LOCK) and thebtck_LogEntrystruct, addName()helpers to the C++ wrapper, and removebtck_logging_set_optionsandbtck_logging_disable(with struct-based entries there is no kernel-side format to configure, and without buffering there is nothing to disable). - Add
KernelLogger: a kernel-owned backend that holds the registered callbacks and the minimum level, and converts eachutil::log::Entryinto abtck_LogEntry. Introduced in a separate commit to keep the scope of the behaviour-changing commit smaller. - Update the bitcoinkernel C API:
btck_LogCallbackreceives abtck_LogEntryinstead of a string, theutil::loghooks are implemented onKernelLogger,logging.cppis dropped from the kernel build, and the logging configuration interface is reduced tobtck_logging_set_min_level().
Behaviour changes for consumers (also described in each commit message):
- Logging callbacks deliver structs instead of formatted strings, and the entire logging interface is simplified.
- Filtering is levels-based only and can also filter
Infoand above.Debug/Traceapply to all categories, consumers can filter onbtck_LogEntry::category(but string formatting is done for the entire level, instead of per enabled category). - Entries logged before the first connection are dropped instead of buffered.
- Messages are delivered unescaped and without a trailing newline.
Appendix
bitcoinkernel C logging interface
typedef struct {
const char* message; //!< Log message text (not null-terminated).
size_t message_len;
const char* thread_name; //!< Name of the thread that produced the log message.
size_t thread_name_len;
int64_t timestamp_ns; //!< Timestamp in nanoseconds since the Unix epoch.
int64_t mocktime; //!< Mock time in seconds since the Unix epoch, or 0 if not set.
const char* file_name; //!< Source file name.
size_t file_name_len;
const char* function_name; //!< Source function name.
size_t function_name_len;
uint32_t line; //!< Source line number.
btck_LogLevel level; //!< Log severity level.
btck_LogCategory category; //!< Log category.
} btck_LogEntry;
typedef void (*btck_LogCallback)(void* user_data, const btck_LogEntry* entry);
BITCOINKERNEL_API void btck_logging_set_min_level(btck_LogLevel level);
BITCOINKERNEL_API btck_LoggingConnection* BITCOINKERNEL_WARN_UNUSED_RESULT btck_logging_connection_create(
btck_LogCallback log_callback,
void* user_data,
btck_DestroyCallback user_data_destroy_callback) BITCOINKERNEL_ARG_NONNULL(1);
BITCOINKERNEL_API void btck_logging_connection_destroy(btck_LoggingConnection* logging_connection);