diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 74be58155..9b764209b 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -659,6 +659,25 @@ td_api::object_ptr ClientManager::execute(td_api::object_ptr log_message_callback; + +static void log_message_callback_wrapper(int verbosity_level, CSlice message) { + auto callback = log_message_callback.load(std::memory_order_relaxed); + if (callback != nullptr) { + callback(verbosity_level, message.c_str()); + } +} + +void ClientManager::set_log_message_callback(int max_verbosity_level, LogMessageCallbackPtr callback) { + if (callback == nullptr) { + ::td::set_log_message_callback(max_verbosity_level, nullptr); + log_message_callback = nullptr; + } else { + log_message_callback = callback; + ::td::set_log_message_callback(max_verbosity_level, log_message_callback_wrapper); + } +} + ClientManager::~ClientManager() = default; ClientManager::ClientManager(ClientManager &&other) = default; ClientManager &ClientManager::operator=(ClientManager &&other) = default; diff --git a/td/telegram/Client.h b/td/telegram/Client.h index 47b8d67a9..5eaf480d2 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -238,6 +238,26 @@ class ClientManager final { */ static td_api::object_ptr execute(td_api::object_ptr &&request); + /** + * A type of callback function that will be called when a message is added to the internal TDLib log. + * + * \param verbosity_level Log verbosity level with which the message was added. + * \param message Null-terminated string with the logged message. + */ + using LogMessageCallbackPtr = void (*)(int verbosity_level, const char *message); + + /** + * Sets the callback that will be called when a message is added to the internal TDLib log. + * None of the TDLib methods can be called from the callback. + * If message verbosity level is 0, then TDLib will crash as soon as callback returns. + * By default the callback is not set. + * + * \param[in] max_verbosity_level Maximum verbosity level of messages for which the callback will be called. + * \param[in] callback Callback that will be called when a message is added to the internal TDLib log. + * Pass nullptr to remove the callback. + */ + static void set_log_message_callback(int max_verbosity_level, LogMessageCallbackPtr callback); + /** * Destroys the client manager and all TDLib client instances managed by it. */ diff --git a/td/telegram/ClientJson.h b/td/telegram/ClientJson.h index cb23b3fff..9ee879315 100644 --- a/td/telegram/ClientJson.h +++ b/td/telegram/ClientJson.h @@ -18,6 +18,7 @@ namespace td { +// TODO can be made private in TDLib 2.0 class ClientJson final { public: void send(Slice request); diff --git a/td/telegram/Log.cpp b/td/telegram/Log.cpp index add941b2c..365bf6bbf 100644 --- a/td/telegram/Log.cpp +++ b/td/telegram/Log.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/Log.h" +#include "td/telegram/Client.h" #include "td/telegram/Logging.h" #include "td/telegram/td_api.h" @@ -23,9 +24,13 @@ static string log_file_path; static int64 max_log_file_size = 10 << 20; static Log::FatalErrorCallbackPtr fatal_error_callback; -static void fatal_error_callback_wrapper(CSlice message) { - CHECK(fatal_error_callback != nullptr); - fatal_error_callback(message.c_str()); +static void fatal_error_callback_wrapper(int verbosity_level, const char *message) { + if (verbosity_level == 0) { + auto callback = fatal_error_callback; + if (callback != nullptr) { + callback(message); + } + } } bool Log::set_file_path(string file_path) { @@ -59,11 +64,11 @@ void Log::set_verbosity_level(int new_verbosity_level) { void Log::set_fatal_error_callback(FatalErrorCallbackPtr callback) { std::lock_guard lock(log_mutex); if (callback == nullptr) { + ClientManager::set_log_message_callback(0, nullptr); fatal_error_callback = nullptr; - set_log_fatal_error_callback(nullptr); } else { fatal_error_callback = callback; - set_log_fatal_error_callback(fatal_error_callback_wrapper); + ClientManager::set_log_message_callback(0, fatal_error_callback_wrapper); } } diff --git a/td/telegram/Log.h b/td/telegram/Log.h index f204ece7d..7573c5b8f 100644 --- a/td/telegram/Log.h +++ b/td/telegram/Log.h @@ -76,6 +76,7 @@ class Log { * The TDLib will crash as soon as callback returns. * By default the callback is not set. * + * \deprecated Use ClientManager::set_log_message_callback instead. * \param[in] callback Callback that will be called when a fatal error happens. * Pass nullptr to remove the callback. */ diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index de337d418..92395d8cf 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -13,7 +13,7 @@ #include "td/net/HttpReader.h" #include "td/telegram/ClientActor.h" -#include "td/telegram/Log.h" +#include "td/telegram/Client.h" #include "td/telegram/Td.h" // for VERBOSITY_NAME(td_requests) #include "td/telegram/td_api_json.h" @@ -4366,8 +4366,11 @@ static void fail_signal(int sig) { } } -static void on_fatal_error(const char *error) { - std::cerr << "Fatal error: " << error << std::endl; +static void on_log_message(int verbosity_level, const char *message) { + if (verbosity_level == 0) { + std::cerr << "Fatal error: " << message; + } + std::cerr << "Log message: " << message; } void main(int argc, char **argv) { @@ -4376,7 +4379,7 @@ void main(int argc, char **argv) { ignore_signal(SignalType::Pipe).ensure(); set_signal_handler(SignalType::Error, fail_signal).ensure(); set_signal_handler(SignalType::Abort, fail_signal).ensure(); - Log::set_fatal_error_callback(on_fatal_error); + ClientManager::set_log_message_callback(0, on_log_message); init_openssl_threads(); const char *locale_name = (std::setlocale(LC_ALL, "fr-FR") == nullptr ? "C" : "fr-FR"); diff --git a/td/telegram/td_json_client.cpp b/td/telegram/td_json_client.cpp index 1a426e6d0..fa897d638 100644 --- a/td/telegram/td_json_client.cpp +++ b/td/telegram/td_json_client.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/td_json_client.h" +#include "td/telegram/Client.h" #include "td/telegram/ClientJson.h" #include "td/utils/Slice.h" @@ -45,3 +46,7 @@ const char *td_receive(double timeout) { const char *td_execute(const char *request) { return td::json_execute(td::Slice(request == nullptr ? "" : request)); } + +void td_set_log_message_callback(int max_verbosity_level, td_log_message_callback_ptr callback) { + td::ClientManager::set_log_message_callback(max_verbosity_level, callback); +} diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index d72ff4c79..f2b8e09e2 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -156,6 +156,26 @@ TDJSON_EXPORT const char *td_receive(double timeout); */ TDJSON_EXPORT const char *td_execute(const char *request); +/** + * A type of callback function that will be called when a message is added to the internal TDLib log. + * + * \param verbosity_level Log verbosity level with which the message was added. + * \param message Null-terminated string with the logged message. + */ +typedef void (*td_log_message_callback_ptr)(int verbosity_level, const char *message); + +/** + * Sets the callback that will be called when a message is added to the internal TDLib log. + * None of the TDLib methods can be called from the callback. + * If message verbosity level is 0, then TDLib will crash as soon as callback returns. + * By default the callback is not set. + * + * \param[in] max_verbosity_level Maximum verbosity level of messages for which the callback will be called. + * \param[in] callback Callback that will be called when a message is added to the internal TDLib log. + * Pass nullptr to remove the callback. + */ +TDJSON_EXPORT void td_set_log_message_callback(int max_verbosity_level, td_log_message_callback_ptr callback); + #ifdef __cplusplus } // extern "C" #endif diff --git a/td/telegram/td_log.h b/td/telegram/td_log.h index 24db9e9c9..a1a4bde30 100644 --- a/td/telegram/td_log.h +++ b/td/telegram/td_log.h @@ -71,6 +71,7 @@ typedef void (*td_log_fatal_error_callback_ptr)(const char *error_message); * The TDLib will crash as soon as callback returns. * By default the callback is not set. * + * \deprecated Use td_set_log_message_callback instead. * \param[in] callback Callback that will be called when a fatal error happens. * Pass NULL to remove the callback. */ diff --git a/tdclientjson_export_list b/tdclientjson_export_list index d70976f7d..51f370e6f 100644 --- a/tdclientjson_export_list +++ b/tdclientjson_export_list @@ -11,3 +11,4 @@ _td_create_client_id _td_send _td_receive _td_execute +_td_set_log_message_callback diff --git a/tdutils/td/utils/FileLog.cpp b/tdutils/td/utils/FileLog.cpp index 23f1a257b..e5f9a56d8 100644 --- a/tdutils/td/utils/FileLog.cpp +++ b/tdutils/td/utils/FileLog.cpp @@ -74,14 +74,14 @@ void FileLog::do_append(int log_level, CSlice slice) { if (size_ > rotate_threshold_ || want_rotate_.load(std::memory_order_relaxed)) { auto status = rename(path_, PSLICE() << path_ << ".old"); if (status.is_error()) { - process_fatal_error(PSLICE() << status.error() << " in " << __FILE__ << " at " << __LINE__); + process_fatal_error(PSLICE() << status.error() << " in " << __FILE__ << " at " << __LINE__ << '\n'); } do_after_rotation(); } while (!slice.empty()) { auto r_size = fd_.write(slice); if (r_size.is_error()) { - process_fatal_error(PSLICE() << r_size.error() << " in " << __FILE__ << " at " << __LINE__); + process_fatal_error(PSLICE() << r_size.error() << " in " << __FILE__ << " at " << __LINE__ << '\n'); } auto written = r_size.ok(); size_ += static_cast(written); @@ -107,7 +107,7 @@ void FileLog::do_after_rotation() { fd_.close(); auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write); if (r_fd.is_error()) { - process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__); + process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__ << '\n'); } fd_ = r_fd.move_as_ok(); if (!Stderr().empty() && redirect_stderr_) { diff --git a/tdutils/td/utils/check.cpp b/tdutils/td/utils/check.cpp index 3d78954f8..f7f60c15b 100644 --- a/tdutils/td/utils/check.cpp +++ b/tdutils/td/utils/check.cpp @@ -16,7 +16,7 @@ namespace detail { void process_check_error(const char *message, const char *file, int line) { ::td::Logger(*log_interface, log_options, VERBOSITY_NAME(FATAL), Slice(file), line, Slice()) << "Check `" << message << "` failed"; - ::td::process_fatal_error(PSLICE() << "Check `" << message << "` failed in " << file << " at " << line); + ::td::process_fatal_error(PSLICE() << "Check `" << message << "` failed in " << file << " at " << line << '\n'); } } // namespace detail diff --git a/tdutils/td/utils/logging.cpp b/tdutils/td/utils/logging.cpp index 14cfe2ba0..6fdba8fe6 100644 --- a/tdutils/td/utils/logging.cpp +++ b/tdutils/td/utils/logging.cpp @@ -32,25 +32,28 @@ namespace td { LogOptions log_options; -static std::atomic max_callback_verbosity_level = 0; -static std::atomic on_log_message_callback = nullptr; +static std::atomic max_callback_verbosity_level{-2}; +static std::atomic on_log_message_callback{nullptr}; void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback) { + if (callback == nullptr) { + max_verbosity_level = -2; + } + max_callback_verbosity_level = max_verbosity_level; on_log_message_callback = callback; } void LogInterface::append(int log_level, CSlice slice) { do_append(log_level, slice); - if (log_level <= max_callback_verbosity_level.load(std::memory_order_relaxed)) { + if (log_level == VERBOSITY_NAME(FATAL)) { + process_fatal_error(slice); + } else if (log_level <= max_callback_verbosity_level.load(std::memory_order_relaxed)) { auto callback = on_log_message_callback.load(std::memory_order_relaxed); if (callback != nullptr) { callback(log_level, slice); } } - if (log_level == VERBOSITY_NAME(FATAL)) { - process_fatal_error(slice); - } } TD_THREAD_LOCAL const char *Logger::tag_ = nullptr; @@ -286,17 +289,14 @@ static DefaultLog default_log; LogInterface *const default_log_interface = &default_log; LogInterface *log_interface = default_log_interface; -static OnFatalErrorCallback on_fatal_error_callback = nullptr; - -void set_log_fatal_error_callback(OnFatalErrorCallback callback) { - on_fatal_error_callback = callback; -} - void process_fatal_error(CSlice message) { - auto callback = on_fatal_error_callback; - if (callback) { - callback(message); + if (0 <= max_callback_verbosity_level.load(std::memory_order_relaxed)) { + auto callback = on_log_message_callback.load(std::memory_order_relaxed); + if (callback != nullptr) { + callback(0, message); + } } + std::abort(); } diff --git a/tdutils/td/utils/logging.h b/tdutils/td/utils/logging.h index 6e5f3cc65..89ed51a2a 100644 --- a/tdutils/td/utils/logging.h +++ b/tdutils/td/utils/logging.h @@ -187,10 +187,6 @@ extern LogInterface *log_interface; [[noreturn]] void process_fatal_error(CSlice message); -// deprecated in favor of set_log_message_callback -using OnFatalErrorCallback = void (*)(CSlice message); -void set_log_fatal_error_callback(OnFatalErrorCallback callback); - using OnLogMessageCallback = void (*)(int verbosity_level, CSlice message); void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback);