Skip to content

Commit d0bd114

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 a4e96ca commit d0bd114

File tree

5 files changed

+109
-105
lines changed

5 files changed

+109
-105
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) {
@@ -706,39 +705,38 @@ void btck_txid_destroy(btck_Txid* txid)
706705
delete txid;
707706
}
708707

709-
void btck_logging_set_options(const btck_LoggingOptions options)
708+
void btck_logging_set_options(btck_LoggingConnection* logger, const btck_LoggingOptions options)
710709
{
710+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
711711
LOCK(cs_main);
712-
LogInstance().m_log_timestamps = options.log_timestamps;
713-
LogInstance().m_log_time_micros = options.log_time_micros;
714-
LogInstance().m_log_threadnames = options.log_threadnames;
715-
LogInstance().m_log_sourcelocations = options.log_sourcelocations;
716-
LogInstance().m_always_print_category_level = options.always_print_category_levels;
712+
log.m_log_timestamps = options.log_timestamps;
713+
log.m_log_time_micros = options.log_time_micros;
714+
log.m_log_threadnames = options.log_threadnames;
715+
log.m_log_sourcelocations = options.log_sourcelocations;
716+
log.m_always_print_category_level = options.always_print_category_levels;
717717
}
718718

719-
void btck_logging_set_level_category(btck_LogCategory category, btck_LogLevel level)
719+
void btck_logging_set_level_category(btck_LoggingConnection* logger, btck_LogCategory category, btck_LogLevel level)
720720
{
721+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
721722
LOCK(cs_main);
722723
if (category == btck_LogCategory_ALL) {
723-
LogInstance().SetLogLevel(get_bclog_level(level));
724+
log.SetLogLevel(get_bclog_level(level));
724725
}
725726

726-
LogInstance().AddCategoryLogLevel(get_bclog_flag(category), get_bclog_level(level));
727-
}
728-
729-
void btck_logging_enable_category(btck_LogCategory category)
730-
{
731-
LogInstance().EnableCategory(get_bclog_flag(category));
727+
log.AddCategoryLogLevel(get_bclog_flag(category), get_bclog_level(level));
732728
}
733729

734-
void btck_logging_disable_category(btck_LogCategory category)
730+
void btck_logging_enable_category(btck_LoggingConnection* logger, btck_LogCategory category)
735731
{
736-
LogInstance().DisableCategory(get_bclog_flag(category));
732+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
733+
log.EnableCategory(get_bclog_flag(category));
737734
}
738735

739-
void btck_logging_disable()
736+
void btck_logging_disable_category(btck_LoggingConnection* logger, btck_LogCategory category)
740737
{
741-
LogInstance().DisableLogging();
738+
BCLog::Logger& log{btck_LoggingConnection::get(logger).m_logger};
739+
log.DisableCategory(get_bclog_flag(category));
742740
}
743741

744742
btck_LoggingConnection* btck_logging_connection_create(btck_LogCallback callback, void* user_data, btck_DestroyCallback user_data_destroy_callback)
@@ -792,6 +790,12 @@ btck_ContextOptions* btck_context_options_create()
792790
return btck_ContextOptions::create();
793791
}
794792

793+
void btck_context_options_set_logger(btck_ContextOptions* options, btck_LoggingConnection* logger)
794+
{
795+
LOCK(btck_ContextOptions::get(options).m_mutex);
796+
btck_ContextOptions::get(options).m_logger = &btck_LoggingConnection::get(logger).m_logger;
797+
}
798+
795799
void btck_context_options_set_chainparams(btck_ContextOptions* options, const btck_ChainParameters* chain_parameters)
796800
{
797801
// Copy the chainparams, so the caller can free it again

src/kernel/bitcoinkernel.h

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,6 @@ typedef struct btck_TransactionOutput btck_TransactionOutput;
120120

121121
/**
122122
* Opaque data structure for holding a logging connection.
123-
*
124-
* The logging connection can be used to manually stop logging.
125-
*
126-
* Messages that were logged before a connection is created are buffered in a
127-
* 1MB buffer. Logging can alternatively be permanently disabled by calling
128-
* @ref btck_logging_disable. Functions changing the logging settings are
129-
* global and change the settings for all existing btck_LoggingConnection
130-
* instances.
131123
*/
132124
typedef struct btck_LoggingConnection btck_LoggingConnection;
133125

@@ -690,31 +682,23 @@ BITCOINKERNEL_API void btck_transaction_output_destroy(btck_TransactionOutput* t
690682
///@{
691683

692684
/**
693-
* @brief This disables the global internal logger. No log messages will be
694-
* buffered internally anymore once this is called and the buffer is cleared.
695-
* This function should only be called once and is not thread or re-entry safe.
696-
* Log messages will be buffered until this function is called, or a logging
697-
* connection is created. This must not be called while a logging connection
698-
* already exists.
699-
*/
700-
BITCOINKERNEL_API void btck_logging_disable();
701-
702-
/**
703-
* @brief Set some options for the global internal logger. This changes global
685+
* @brief Set some options for the logger. Currently, this changes global
704686
* settings and will override settings for all existing @ref
705687
* btck_LoggingConnection instances.
706688
*
689+
* @param[in] logger Non-null.
707690
* @param[in] options Sets formatting options of the log messages.
708691
*/
709-
BITCOINKERNEL_API void btck_logging_set_options(const btck_LoggingOptions options);
692+
BITCOINKERNEL_API void btck_logging_set_options(btck_LoggingConnection* logger, const btck_LoggingOptions options) BITCOINKERNEL_ARG_NONNULL(1);
710693

711694
/**
712-
* @brief Set the log level of the global internal logger. This does not
695+
* @brief Set the log level of the logger. This does not
713696
* enable the selected categories. Use @ref btck_logging_enable_category to
714-
* start logging from a specific, or all categories. This changes a global
697+
* start logging from a specific, or all categories. Currently, this changes a global
715698
* setting and will override settings for all existing
716699
* @ref btck_LoggingConnection instances.
717700
*
701+
* @param[in] logger Non-null.
718702
* @param[in] category If btck_LogCategory_ALL is chosen, sets both the global fallback log level
719703
* used by all categories that don't have a specific level set, and also
720704
* sets the log level for messages logged with the btck_LogCategory_ALL category itself.
@@ -723,25 +707,27 @@ BITCOINKERNEL_API void btck_logging_set_options(const btck_LoggingOptions option
723707
724708
* @param[in] level Log level at which the log category is set.
725709
*/
726-
BITCOINKERNEL_API void btck_logging_set_level_category(btck_LogCategory category, btck_LogLevel level);
710+
BITCOINKERNEL_API void btck_logging_set_level_category(btck_LoggingConnection* logger, btck_LogCategory category, btck_LogLevel level) BITCOINKERNEL_ARG_NONNULL(1);
727711

728712
/**
729-
* @brief Enable a specific log category for the global internal logger. This
713+
* @brief Enable a specific log category for the logger. Currently, this
730714
* changes a global setting and will override settings for all existing @ref
731715
* btck_LoggingConnection instances.
732716
*
717+
* @param[in] logger Non-null.
733718
* @param[in] category If btck_LogCategory_ALL is chosen, all categories will be enabled.
734719
*/
735-
BITCOINKERNEL_API void btck_logging_enable_category(btck_LogCategory category);
720+
BITCOINKERNEL_API void btck_logging_enable_category(btck_LoggingConnection* logger, btck_LogCategory category) BITCOINKERNEL_ARG_NONNULL(1);
736721

737722
/**
738-
* @brief Disable a specific log category for the global internal logger. This
723+
* @brief Disable a specific log category for the logger. Currently, this
739724
* changes a global setting and will override settings for all existing @ref
740725
* btck_LoggingConnection instances.
741726
*
727+
* @param[in] logger Non-null.
742728
* @param[in] category If btck_LogCategory_ALL is chosen, all categories will be disabled.
743729
*/
744-
BITCOINKERNEL_API void btck_logging_disable_category(btck_LogCategory category);
730+
BITCOINKERNEL_API void btck_logging_disable_category(btck_LoggingConnection* logger, btck_LogCategory category) BITCOINKERNEL_ARG_NONNULL(1);
745731

746732
/**
747733
* @brief Start logging messages through the provided callback. Log messages
@@ -806,6 +792,16 @@ BITCOINKERNEL_API void btck_chain_parameters_destroy(btck_ChainParameters* chain
806792
*/
807793
BITCOINKERNEL_API btck_ContextOptions* BITCOINKERNEL_WARN_UNUSED_RESULT btck_context_options_create();
808794

795+
/**
796+
* @brief Sets log instance for the kernel context to use.
797+
*
798+
* @param[in] context_options Non-null, previously created by @ref btck_context_options_create.
799+
* @param[in] logger Is set to the context options.
800+
*/
801+
BITCOINKERNEL_API void btck_context_options_set_logger(
802+
btck_ContextOptions* context_options,
803+
btck_LoggingConnection* logger) BITCOINKERNEL_ARG_NONNULL(1, 2);
804+
809805
/**
810806
* @brief Sets the chain params for the context options. The context created
811807
* 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:
@@ -874,6 +873,12 @@ class ContextOptions : public UniqueHandle<btck_ContextOptions, btck_context_opt
874873
public:
875874
ContextOptions() : UniqueHandle{btck_context_options_create()} {}
876875

876+
template <Log T>
877+
void SetLogger(Logger<T>& logger)
878+
{
879+
btck_context_options_set_logger(get(), logger.get());
880+
}
881+
877882
void SetChainParams(ChainParams& chain_params)
878883
{
879884
btck_context_options_set_chainparams(get(), chain_params.get());

src/test/kernel/test_kernel.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -534,20 +534,19 @@ BOOST_AUTO_TEST_CASE(logging_tests)
534534
.always_print_category_levels = true,
535535
};
536536

537-
logging_set_options(logging_options);
538-
logging_set_level_category(LogCategory::BENCH, LogLevel::TRACE_LEVEL);
539-
logging_disable_category(LogCategory::BENCH);
540-
logging_enable_category(LogCategory::VALIDATION);
541-
logging_disable_category(LogCategory::VALIDATION);
537+
Logger logger{std::make_unique<TestLog>()};
538+
logging_set_options(logger, logging_options);
539+
logging_set_level_category(logger, LogCategory::BENCH, LogLevel::TRACE_LEVEL);
540+
logging_disable_category(logger, LogCategory::BENCH);
541+
logging_enable_category(logger, LogCategory::VALIDATION);
542+
logging_disable_category(logger, LogCategory::VALIDATION);
542543

543-
// Check that connecting, connecting another, and then disconnecting and connecting a logger again works.
544+
// Check that setting options on a different logger works.
544545
{
545-
logging_set_level_category(LogCategory::KERNEL, LogLevel::TRACE_LEVEL);
546-
logging_enable_category(LogCategory::KERNEL);
547-
Logger logger{std::make_unique<TestLog>()};
548546
Logger logger_2{std::make_unique<TestLog>()};
547+
logging_set_level_category(logger_2, LogCategory::KERNEL, LogLevel::TRACE_LEVEL);
548+
logging_enable_category(logger_2, LogCategory::KERNEL);
549549
}
550-
Logger logger{std::make_unique<TestLog>()};
551550
}
552551

553552
BOOST_AUTO_TEST_CASE(btck_context_tests)

0 commit comments

Comments
 (0)