Add set_fatal_error_callback to public Log interface.

GitOrigin-RevId: 3e7c3bb97d86fa753deae864fdf574de1de1aee4
This commit is contained in:
levlam 2018-01-24 18:45:57 +03:00
parent 644a4ffbea
commit 18e7b0816d
11 changed files with 112 additions and 28 deletions

View File

@ -652,7 +652,7 @@ if (NOT CMAKE_CROSSCOMPILING)
target_include_directories(tg_cli SYSTEM PRIVATE ${READLINE_INCLUDE_DIR})
target_compile_definitions(tg_cli PRIVATE -DUSE_READLINE=1)
endif()
target_link_libraries(tg_cli PRIVATE memprof tdcore tdtl)
target_link_libraries(tg_cli PRIVATE memprof tdclient tdcore tdtl)
add_dependencies(tg_cli tl_generate_json)
endif()

View File

@ -17,6 +17,12 @@ namespace td {
static FileLog file_log;
static TsLog ts_log(&file_log);
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());
}
void Log::set_file_path(string file_path) {
if (file_path.empty()) {
@ -37,4 +43,14 @@ void Log::set_verbosity_level(int new_verbosity_level) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity_level);
}
void Log::set_fatal_error_callback(FatalErrorCallbackPtr callback) {
if (callback == 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);
}
}
} // namespace td

View File

@ -56,6 +56,24 @@ class Log {
* value greater than 5 and up to 1024 can be used to enable even more logging.
*/
static void set_verbosity_level(int new_verbosity_level);
/**
* A type of callback function that will be called when a fatal error happens.
*
* \param error_message Null-terminated string with a description of a happened fatal error.
*/
using FatalErrorCallbackPtr = void (*)(const char *error_message);
/**
* Sets the callback that will be called when a fatal error happens.
* None of the TDLib methods can be called from the callback.
* The TDLib will crash as soon as callback returns.
* By default the callback is not set.
*
* \param[in] callback Callback that will be called when a fatal error happens.
* Pass nullptr to remove the callback.
*/
static void set_fatal_error_callback(FatalErrorCallbackPtr callback);
};
} // namespace td

View File

@ -5,6 +5,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ClientActor.h"
#include "td/telegram/Log.h"
#include "td/telegram/td_api_json.h"
@ -42,6 +43,7 @@
#include <cstdlib>
#include <cstring> // for strcmp
#include <ctime>
#include <iostream>
#include <limits>
#include <memory>
#include <queue>
@ -56,7 +58,7 @@
namespace td {
void dump_memory_usage() {
static void dump_memory_usage() {
if (is_memprof_on()) {
LOG(WARNING) << "memory_dump";
clear_thread_locals();
@ -87,7 +89,7 @@ static int32 saved_point;
static string saved_line;
static std::atomic_flag readline_lock = ATOMIC_FLAG_INIT;
void deactivate_readline() {
static void deactivate_readline() {
while (readline_lock.test_and_set(std::memory_order_acquire)) {
// spin
}
@ -100,7 +102,7 @@ void deactivate_readline() {
rl_redisplay();
}
void reactivate_readline() {
static void reactivate_readline() {
rl_set_prompt(prompt);
rl_replace_line(saved_line.c_str(), 0);
rl_point = saved_point;
@ -109,7 +111,7 @@ void reactivate_readline() {
readline_lock.clear(std::memory_order_release);
}
char *command_generator(const char *text, int state) {
static char *command_generator(const char *text, int state) {
static vector<CSlice> commands{"GetContacts",
"GetChats",
"GetHistory",
@ -162,7 +164,7 @@ char *command_generator(const char *text, int state) {
return nullptr;
}
char **tg_cli_completion(const char *text, int start, int end) {
static char **tg_cli_completion(const char *text, int start, int end) {
char **matches = nullptr;
if (start == 0) {
matches = rl_completion_matches(text, command_generator);
@ -2751,6 +2753,10 @@ class CliClient final : public Actor {
quit();
} else if (op == "dnq" || op == "DumpNetQueries") {
dump_pending_network_queries();
} else if (op == "fatal") {
LOG(FATAL) << "Fatal!";
} else if (op == "unreachable") {
UNREACHABLE();
} else {
op_not_found_count++;
}
@ -2864,26 +2870,31 @@ class CliClient final : public Actor {
};
CliClient *CliClient::instance_ = nullptr;
void quit() {
static void quit() {
CliClient::quit_instance();
}
void fail_signal(int sig) {
static void fail_signal(int sig) {
signal_safe_write_signal_number(sig);
while (true) {
// spin forever to allow debugger to attach
}
}
void usage() {
static void usage() {
//TODO:
}
static void on_fatal_error(const char *error) {
std::cerr << "Fatal error: " << error << std::endl;
}
void main(int argc, char **argv) {
ignore_signal(SignalType::HangUp).ensure();
ignore_signal(SignalType::Pipe).ensure();
set_signal_handler(SignalType::Error, fail_signal).ensure();
set_signal_handler(SignalType::Abort, fail_signal).ensure();
td::Log::set_fatal_error_callback(on_fatal_error);
CliLog cli_log;
log_interface = &cli_log;

View File

@ -8,6 +8,8 @@
#include "td/telegram/Log.h"
#include <cstdint>
void td_set_log_file_path(const char *file_path) {
td::Log::set_file_path(file_path == nullptr ? "" : file_path);
}
@ -19,3 +21,7 @@ void td_set_log_max_file_size(long long max_file_size) {
void td_set_log_verbosity_level(int new_verbosity_level) {
td::Log::set_verbosity_level(new_verbosity_level);
}
void td_set_log_fatal_error_callback(td_log_fatal_error_callback_ptr callback) {
td::Log::set_fatal_error_callback(callback);
}

View File

@ -23,8 +23,8 @@ extern "C" {
* By default TDLib writes logs to stderr or an OS specific log.
* Use this method to write the log to a file instead.
*
* \param[in] file_path Path to a file where the internal TDLib log will be written. Use an empty path to
* switch back to the default logging behaviour.
* \param[in] file_path Null-terminated path to a file where the internal TDLib log will be written.
* Use an empty path to switch back to the default logging behaviour.
*/
TDJSON_EXPORT void td_set_log_file_path(const char *file_path);
@ -52,6 +52,24 @@ TDJSON_EXPORT void td_set_log_max_file_size(long long max_file_size);
*/
TDJSON_EXPORT void td_set_log_verbosity_level(int new_verbosity_level);
/**
* A type of callback function that will be called when a fatal error happens.
*
* \param error_message Null-terminated string with a description of a happened fatal error.
*/
using td_log_fatal_error_callback_ptr = void (*)(const char *error_message);
/**
* Sets the callback that will be called when a fatal error happens.
* None of the TDLib methods can be called from the callback.
* The TDLib will crash as soon as callback returns.
* By default the callback is not set.
*
* \param[in] callback Callback that will be called when a fatal error happens.
* Pass NULL to remove the callback.
*/
TDJSON_EXPORT void td_set_log_fatal_error_callback(td_log_fatal_error_callback_ptr callback);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -6,3 +6,4 @@ _td_json_client_execute
_td_set_log_file_path
_td_set_log_max_file_size
_td_set_log_verbosity_level
_td_set_log_fatal_error_callback

View File

@ -13,7 +13,6 @@
#include "td/utils/port/path.h"
#include "td/utils/Slice.h"
#include <cstdlib>
#include <limits>
namespace td {
@ -23,25 +22,25 @@ class FileLog : public LogInterface {
static constexpr int DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
public:
void append(CSlice xslice, int log_level) override {
Slice slice = xslice;
void append(CSlice cslice, int log_level) override {
Slice slice = cslice;
while (!slice.empty()) {
auto r_size = fd_.write(slice);
if (r_size.is_error()) {
std::abort();
process_fatal_error(r_size.error().message());
}
auto written = r_size.ok();
size_ += static_cast<int64>(written);
slice.remove_prefix(written);
}
if (log_level == VERBOSITY_NAME(FATAL)) {
std::abort();
process_fatal_error(cslice);
}
if (size_ > rotate_threshold_) {
auto status = rename(path_, path_ + ".old");
if (status.is_error()) {
std::abort();
process_fatal_error(status.message());
}
do_rotate();
}
@ -83,7 +82,7 @@ class FileLog : public LogInterface {
fd_.close();
auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write);
if (r_fd.is_error()) {
std::abort();
process_fatal_error(r_fd.error().message());
}
fd_ = r_fd.move_as_ok();
Fd::duplicate(fd_.get_fd(), Fd::Stderr()).ignore();

View File

@ -57,6 +57,10 @@ class MemoryLog : public LogInterface {
size_t printed = std::snprintf(&buffer_[start_pos + 1], magic_size - 1, "LOG:%08x: ", real_pos);
CHECK(printed == magic_size - 2);
buffer_[start_pos + magic_size - 1] = ' ';
if (log_level == VERBOSITY_NAME(FATAL)) {
process_fatal_error(new_slice);
}
}
void rotate() override {

View File

@ -216,11 +216,7 @@ class DefaultLog : public LogInterface {
TsCerr() << slice;
#endif
if (log_level == VERBOSITY_NAME(FATAL)) {
auto f = default_log_on_fatal_error;
if (f) {
f(slice);
}
std::abort();
process_fatal_error(slice);
}
}
void rotate() override {
@ -230,6 +226,19 @@ static DefaultLog default_log;
LogInterface *const default_log_interface = &default_log;
LogInterface *log_interface = default_log_interface;
OnFatalErrorF default_log_on_fatal_error = nullptr;
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);
}
std::abort();
}
} // namespace td

View File

@ -30,7 +30,6 @@
#include "td/utils/StringBuilder.h"
#include <atomic>
#include <cstdlib>
#include <type_traits>
#define PSTR_IMPL(...) ::td::Logger(::td::NullLog().ref(), 0, true).printf(__VA_ARGS__)
@ -98,7 +97,7 @@ inline bool no_return_func() {
#define UNREACHABLE(...) \
LOG(FATAL, __VA_ARGS__); \
std::abort()
::td::process_fatal_error("Unreachable in " __FILE__ " at " TD_DEFINE_STR(__LINE__))
constexpr int VERBOSITY_NAME(PLAIN) = -1;
constexpr int VERBOSITY_NAME(FATAL) = 0;
@ -148,8 +147,11 @@ class NullLog : public LogInterface {
extern LogInterface *const default_log_interface;
extern LogInterface *log_interface;
typedef void (*OnFatalErrorF)(CSlice msg);
extern OnFatalErrorF default_log_on_fatal_error;
using OnFatalErrorCallback = void (*)(CSlice message);
void set_log_fatal_error_callback(OnFatalErrorCallback callback);
[[noreturn]] void process_fatal_error(CSlice message);
#define TC_RED "\e[1;31m"
#define TC_BLUE "\e[1;34m"