Skip to content

Commit 168128b

Browse files
committed
kernel: Simplify logging API
Current kernel logging API is too complicated and too restrictive. This PR tries to improves it. I'd expect an API that supported logging to allow creating log streams with different options and providing some way to specify which library operations should be logged to which streams. By contrast, the current API allows creating multiple log streams, but all the streams receive the same messages because options can only be set globally and the stream objects can't be passed to any kernel API functions. They are not even referenced by the `btck_Context` struct which holds other shared state. If no log streams are created, log messages are generated anyway, but they are stored in a 1MB buffer and not sent anywhere, unless a log stream is created later, at which point they are sent in bulk to that stream. More log streams can be created after that, but they only receive new messages, not the buffered ones. If log output is not needed, a btck_logging_disable() call is required to prevent log messages from accumulating in the 1MB buffer. Calling this will abort the program if any log streams were created before it was called, and also abort the program if any new log streams are created later. None of these behaviors seem necessary or ideal, and it would be better to provide a simpler logging API that allows creating a log stream, setting options on it, registering it with the `btck_Context` instance and receiving log messages from it. Another advantage of this approach is that it could allow (with #30342) different log streams to be used for different purposes, which would be not be possible with the current interface.
1 parent 597b8be commit 168128b

File tree

5 files changed

+118
-108
lines changed

5 files changed

+118
-108
lines changed

src/bitcoin-chainstate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,12 @@ int main(int argc, char* argv[])
151151
.always_print_category_levels = true,
152152
};
153153

154-
logging_set_options(logging_options);
155-
156154
Logger logger{std::make_unique<KernelLog>()};
155+
logging_set_options(logger, logging_options);
157156

158157
ContextOptions options{};
159158
ChainParams params{ChainType::MAINNET};
159+
options.SetLogger(logger);
160160
options.SetChainParams(params);
161161

162162
options.SetNotifications(std::make_shared<TestKernelNotifications>());

src/kernel/bitcoinkernel.cpp

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ btck_Warning cast_btck_warning(kernel::Warning warning)
226226
}
227227

228228
struct LoggingConnection {
229+
// Reference to global log instance. This could be replaced with a
230+
// per-connection instance (#30342) to give clients more granular control
231+
// over logging.
232+
BCLog::Logger& m_logger{LogInstance()};
229233
std::unique_ptr<std::list<std::function<void(const std::string&)>>::iterator> m_connection;
230234
void* m_user_data;
231235
std::function<void(void* user_data)> m_deleter;
@@ -234,18 +238,7 @@ struct LoggingConnection {
234238
{
235239
LOCK(cs_main);
236240

237-
auto connection{LogInstance().PushBackCallback([callback, user_data](const std::string& str) { callback(user_data, str.c_str(), str.length()); })};
238-
239-
// Only start logging if we just added the connection.
240-
if (LogInstance().NumConnections() == 1 && !LogInstance().StartLogging()) {
241-
LogError("Logger start failed.");
242-
LogInstance().DeleteCallback(connection);
243-
if (user_data && user_data_destroy_callback) {
244-
user_data_destroy_callback(user_data);
245-
}
246-
throw std::runtime_error("Failed to start logging");
247-
}
248-
241+
auto connection{m_logger.PushBackCallback([callback, user_data](const std::string& str) { callback(user_data, str.c_str(), str.length()); })};
249242
m_connection = std::make_unique<std::list<std::function<void(const std::string&)>>::iterator>(connection);
250243
m_user_data = user_data;
251244
m_deleter = user_data_destroy_callback;
@@ -257,15 +250,7 @@ struct LoggingConnection {
257250
{
258251
LOCK(cs_main);
259252
LogDebug(BCLog::KERNEL, "Logger disconnecting.");
260-
261-
// Switch back to buffering by calling DisconnectTestLogger if the
262-
// connection that we are about to remove is the last one.
263-
if (LogInstance().NumConnections() == 1) {
264-
LogInstance().DisconnectTestLogger();
265-
} else {
266-
LogInstance().DeleteCallback(*m_connection);
267-
}
268-
253+
m_logger.DeleteCallback(*m_connection);
269254
m_connection.reset();
270255
if (m_user_data && m_deleter) {
271256
m_deleter(m_user_data);
@@ -380,6 +365,7 @@ class KernelValidationInterface final : public CValidationInterface
380365

381366
struct ContextOptions {
382367
mutable Mutex m_mutex;
368+
BCLog::Logger* m_logger GUARDED_BY(m_mutex) {nullptr};
383369
std::unique_ptr<const CChainParams> m_chainparams GUARDED_BY(m_mutex);
384370
std::shared_ptr<KernelNotifications> m_notifications GUARDED_BY(m_mutex);
385371
std::shared_ptr<KernelValidationInterface> m_validation_interface GUARDED_BY(m_mutex);
@@ -388,6 +374,8 @@ struct ContextOptions {
388374
class Context
389375
{
390376
public:
377+
BCLog::Logger* m_logger;
378+
391379
std::unique_ptr<kernel::Context> m_context;
392380

393381
std::shared_ptr<KernelNotifications> m_notifications;
@@ -401,9 +389,20 @@ class Context
401389
std::shared_ptr<KernelValidationInterface> m_validation_interface;
402390

403391
Context(const ContextOptions* options, bool& sane)
404-
: m_context{std::make_unique<kernel::Context>()},
392+
: m_logger{options && options->m_logger ? options->m_logger : nullptr},
393+
m_context{std::make_unique<kernel::Context>()},
405394
m_interrupt{std::make_unique<util::SignalInterrupt>()}
406395
{
396+
if (!m_logger) {
397+
// For efficiency, disable logging globally instead of writing log
398+
// messages to temporary buffer if no log callbacks are connected.
399+
if (BCLog::Logger& logger{LogInstance()}; logger.NumConnections() == 0 && logger.Enabled()) {
400+
logger.DisableLogging();
401+
}
402+
} else if (!m_logger->StartLogging()) {
403+
throw std::runtime_error("Failed to start logging");
404+
}
405+
407406
if (options) {
408407
LOCK(options->m_mutex);
409408
if (options->m_chainparams) {
@@ -712,39 +711,38 @@ void btck_txid_destroy(btck_Txid* txid)
712711
delete txid;
713712
}
714713

715-
void btck_logging_set_options(const btck_LoggingOptions options)
714+
void btck_logging_set_options(btck_LoggingConnection* logger, const btck_LoggingOptions options)
716715
{
716+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
717717
LOCK(cs_main);
718-
LogInstance().m_log_timestamps = options.log_timestamps;
719-
LogInstance().m_log_time_micros = options.log_time_micros;
720-
LogInstance().m_log_threadnames = options.log_threadnames;
721-
LogInstance().m_log_sourcelocations = options.log_sourcelocations;
722-
LogInstance().m_always_print_category_level = options.always_print_category_levels;
718+
log.m_log_timestamps = options.log_timestamps;
719+
log.m_log_time_micros = options.log_time_micros;
720+
log.m_log_threadnames = options.log_threadnames;
721+
log.m_log_sourcelocations = options.log_sourcelocations;
722+
log.m_always_print_category_level = options.always_print_category_levels;
723723
}
724724

725-
void btck_logging_set_level_category(btck_LogCategory category, btck_LogLevel level)
725+
void btck_logging_set_level_category(btck_LoggingConnection* logger, btck_LogCategory category, btck_LogLevel level)
726726
{
727+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
727728
LOCK(cs_main);
728729
if (category == btck_LogCategory_ALL) {
729-
LogInstance().SetLogLevel(get_bclog_level(level));
730+
log.SetLogLevel(get_bclog_level(level));
730731
}
731732

732-
LogInstance().AddCategoryLogLevel(get_bclog_flag(category), get_bclog_level(level));
733-
}
734-
735-
void btck_logging_enable_category(btck_LogCategory category)
736-
{
737-
LogInstance().EnableCategory(get_bclog_flag(category));
733+
log.AddCategoryLogLevel(get_bclog_flag(category), get_bclog_level(level));
738734
}
739735

740-
void btck_logging_disable_category(btck_LogCategory category)
736+
void btck_logging_enable_category(btck_LoggingConnection* logger, btck_LogCategory category)
741737
{
742-
LogInstance().DisableCategory(get_bclog_flag(category));
738+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
739+
log.EnableCategory(get_bclog_flag(category));
743740
}
744741

745-
void btck_logging_disable()
742+
void btck_logging_disable_category(btck_LoggingConnection* logger, btck_LogCategory category)
746743
{
747-
LogInstance().DisableLogging();
744+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
745+
log.DisableCategory(get_bclog_flag(category));
748746
}
749747

750748
btck_LoggingConnection* btck_logging_connection_create(btck_LogCallback callback, void* user_data, btck_DestroyCallback user_data_destroy_callback)
@@ -798,6 +796,12 @@ btck_ContextOptions* btck_context_options_create()
798796
return btck_ContextOptions::create();
799797
}
800798

799+
void btck_context_options_set_logger(btck_ContextOptions* options, btck_LoggingConnection* logger)
800+
{
801+
LOCK(btck_ContextOptions::get(options).m_mutex);
802+
btck_ContextOptions::get(options).m_logger = &btck_LoggingConnection::get(logger).m_logger;
803+
}
804+
801805
void btck_context_options_set_chainparams(btck_ContextOptions* options, const btck_ChainParameters* chain_parameters)
802806
{
803807
// Copy the chainparams, so the caller can free it again

src/kernel/bitcoinkernel.h

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,6 @@ typedef struct btck_TransactionOutput btck_TransactionOutput;
131131

132132
/**
133133
* Opaque data structure for holding a logging connection.
134-
*
135-
* The logging connection can be used to manually stop logging.
136-
*
137-
* Messages that were logged before a connection is created are buffered in a
138-
* 1MB buffer. Logging can alternatively be permanently disabled by calling
139-
* @ref btck_logging_disable. Functions changing the logging settings are
140-
* global and change the settings for all existing btck_LoggingConnection
141-
* instances.
142134
*/
143135
typedef struct btck_LoggingConnection btck_LoggingConnection;
144136

@@ -701,31 +693,23 @@ BITCOINKERNEL_API void btck_transaction_output_destroy(btck_TransactionOutput* t
701693
///@{
702694

703695
/**
704-
* @brief This disables the global internal logger. No log messages will be
705-
* buffered internally anymore once this is called and the buffer is cleared.
706-
* This function should only be called once and is not thread or re-entry safe.
707-
* Log messages will be buffered until this function is called, or a logging
708-
* connection is created. This must not be called while a logging connection
709-
* already exists.
710-
*/
711-
BITCOINKERNEL_API void btck_logging_disable();
712-
713-
/**
714-
* @brief Set some options for the global internal logger. This changes global
696+
* @brief Set some options for the logger. Currently, this changes global
715697
* settings and will override settings for all existing @ref
716698
* btck_LoggingConnection instances.
717699
*
700+
* @param[in] logger Non-null.
718701
* @param[in] options Sets formatting options of the log messages.
719702
*/
720-
BITCOINKERNEL_API void btck_logging_set_options(const btck_LoggingOptions options);
703+
BITCOINKERNEL_API void btck_logging_set_options(btck_LoggingConnection* logger, const btck_LoggingOptions options) BITCOINKERNEL_ARG_NONNULL(1);
721704

722705
/**
723-
* @brief Set the log level of the global internal logger. This does not
706+
* @brief Set the log level of the logger. This does not
724707
* enable the selected categories. Use @ref btck_logging_enable_category to
725-
* start logging from a specific, or all categories. This changes a global
708+
* start logging from a specific, or all categories. Currently, this changes a global
726709
* setting and will override settings for all existing
727710
* @ref btck_LoggingConnection instances.
728711
*
712+
* @param[in] logger Non-null.
729713
* @param[in] category If btck_LogCategory_ALL is chosen, sets both the global fallback log level
730714
* used by all categories that don't have a specific level set, and also
731715
* sets the log level for messages logged with the btck_LogCategory_ALL category itself.
@@ -734,30 +718,38 @@ BITCOINKERNEL_API void btck_logging_set_options(const btck_LoggingOptions option
734718
735719
* @param[in] level Log level at which the log category is set.
736720
*/
737-
BITCOINKERNEL_API void btck_logging_set_level_category(btck_LogCategory category, btck_LogLevel level);
721+
BITCOINKERNEL_API void btck_logging_set_level_category(btck_LoggingConnection* logger, btck_LogCategory category, btck_LogLevel level) BITCOINKERNEL_ARG_NONNULL(1);
738722

739723
/**
740-
* @brief Enable a specific log category for the global internal logger. This
724+
* @brief Enable a specific log category for the logger. Currently, this
741725
* changes a global setting and will override settings for all existing @ref
742726
* btck_LoggingConnection instances.
743727
*
728+
* @param[in] logger Non-null.
744729
* @param[in] category If btck_LogCategory_ALL is chosen, all categories will be enabled.
745730
*/
746-
BITCOINKERNEL_API void btck_logging_enable_category(btck_LogCategory category);
731+
BITCOINKERNEL_API void btck_logging_enable_category(btck_LoggingConnection* logger, btck_LogCategory category) BITCOINKERNEL_ARG_NONNULL(1);
747732

748733
/**
749-
* @brief Disable a specific log category for the global internal logger. This
734+
* @brief Disable a specific log category for the logger. Currently, this
750735
* changes a global setting and will override settings for all existing @ref
751736
* btck_LoggingConnection instances.
752737
*
738+
* @param[in] logger Non-null.
753739
* @param[in] category If btck_LogCategory_ALL is chosen, all categories will be disabled.
754740
*/
755-
BITCOINKERNEL_API void btck_logging_disable_category(btck_LogCategory category);
741+
BITCOINKERNEL_API void btck_logging_disable_category(btck_LoggingConnection* logger, btck_LogCategory category) BITCOINKERNEL_ARG_NONNULL(1);
756742

757743
/**
758-
* @brief Start logging messages through the provided callback. Log messages
759-
* produced before this function is first called are buffered and on calling this
760-
* function are logged immediately.
744+
* @brief Register logging callback that can receive log messages and return new
745+
* @ref btck_LoggingConnection instance representing a log stream. The returned
746+
* handle can be passed to functions like @ref btck_logging_set_options to set
747+
* log options and @ref btck_context_options_set_logger to receive log messages
748+
* generated in a particular context.
749+
*
750+
* Currently, most log state is global, so log options applied to other streams
751+
* may effect this stream, and if there are multiple contexts, log messages from
752+
* other contexts may be received. These behaviors can be improved internally.
761753
*
762754
* @param[in] log_callback Non-null, function through which messages will be logged.
763755
* @param[in] user_data Nullable, holds a user-defined opaque structure. Is passed back
@@ -817,6 +809,16 @@ BITCOINKERNEL_API void btck_chain_parameters_destroy(btck_ChainParameters* chain
817809
*/
818810
BITCOINKERNEL_API btck_ContextOptions* BITCOINKERNEL_WARN_UNUSED_RESULT btck_context_options_create();
819811

812+
/**
813+
* @brief Sets log instance for the kernel context to use.
814+
*
815+
* @param[in] context_options Non-null, previously created by @ref btck_context_options_create.
816+
* @param[in] logger Is set to the context options.
817+
*/
818+
BITCOINKERNEL_API void btck_context_options_set_logger(
819+
btck_ContextOptions* context_options,
820+
btck_LoggingConnection* logger) BITCOINKERNEL_ARG_NONNULL(1, 2);
821+
820822
/**
821823
* @brief Sets the chain params for the context options. The context created
822824
* with the options will be configured for these chain parameters.

src/kernel/bitcoinkernel_wrapper.h

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -735,38 +735,13 @@ class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy>
735735
}
736736
};
737737

738-
inline void logging_disable()
739-
{
740-
btck_logging_disable();
741-
}
742-
743-
inline void logging_set_options(const btck_LoggingOptions& logging_options)
744-
{
745-
btck_logging_set_options(logging_options);
746-
}
747-
748-
inline void logging_set_level_category(LogCategory category, LogLevel level)
749-
{
750-
btck_logging_set_level_category(static_cast<btck_LogCategory>(category), static_cast<btck_LogLevel>(level));
751-
}
752-
753-
inline void logging_enable_category(LogCategory category)
754-
{
755-
btck_logging_enable_category(static_cast<btck_LogCategory>(category));
756-
}
757-
758-
inline void logging_disable_category(LogCategory category)
759-
{
760-
btck_logging_disable_category(static_cast<btck_LogCategory>(category));
761-
}
762-
763738
template <typename T>
764739
concept Log = requires(T a, std::string_view message) {
765740
{ a.LogMessage(message) } -> std::same_as<void>;
766741
};
767742

768743
template <Log T>
769-
class Logger : UniqueHandle<btck_LoggingConnection, btck_logging_connection_destroy>
744+
class Logger : public UniqueHandle<btck_LoggingConnection, btck_logging_connection_destroy>
770745
{
771746
public:
772747
Logger(std::unique_ptr<T> log)
@@ -778,6 +753,30 @@ class Logger : UniqueHandle<btck_LoggingConnection, btck_logging_connection_dest
778753
}
779754
};
780755

756+
template <Log T>
757+
void logging_set_options(Logger<T>& logger, const btck_LoggingOptions& logging_options)
758+
{
759+
btck_logging_set_options(logger.get(), logging_options);
760+
}
761+
762+
template <Log T>
763+
void logging_set_level_category(Logger<T>& logger, LogCategory category, LogLevel level)
764+
{
765+
btck_logging_set_level_category(logger.get(), static_cast<btck_LogCategory>(category), static_cast<btck_LogLevel>(level));
766+
}
767+
768+
template <Log T>
769+
void logging_enable_category(Logger<T>& logger, LogCategory category)
770+
{
771+
btck_logging_enable_category(logger.get(), static_cast<btck_LogCategory>(category));
772+
}
773+
774+
template <Log T>
775+
void logging_disable_category(Logger<T>& logger, LogCategory category)
776+
{
777+
btck_logging_disable_category(logger.get(), static_cast<btck_LogCategory>(category));
778+
}
779+
781780
class BlockTreeEntry : public View<btck_BlockTreeEntry>
782781
{
783782
public:
@@ -879,6 +878,12 @@ class ContextOptions : public UniqueHandle<btck_ContextOptions, btck_context_opt
879878
public:
880879
ContextOptions() : UniqueHandle{btck_context_options_create()} {}
881880

881+
template <Log T>
882+
void SetLogger(Logger<T>& logger)
883+
{
884+
btck_context_options_set_logger(get(), logger.get());
885+
}
886+
882887
void SetChainParams(ChainParams& chain_params)
883888
{
884889
btck_context_options_set_chainparams(get(), chain_params.get());

0 commit comments

Comments
 (0)