text: //
  // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
  //
  // Distributed under the Boost Software License, Version 1.0. (See accompanying
  // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  //
  #pragma once
  
  /*
   * Simple logging.
   *
   * Predefined log levels: FATAL, ERROR, WARNING, INFO, DEBUG
   *
   * LOG(WARNING) << "Hello world!";
   * LOG(INFO) << "Hello " << 1234 << " world!";
   * LOG_IF(INFO, condition) << "Hello world if condition!";
   *
   * Custom log levels may be defined and used using VLOG:
   * int VERBOSITY_NAME(custom) = VERBOSITY_NAME(WARNING);
   * VLOG(custom) << "Hello custom world!"
   *
   * LOG(FATAL) << "Power is off";
   * CHECK(condition) <===> LOG_IF(FATAL, !(condition))
   */
  
  #include "td/utils/common.h"
  #include "td/utils/port/thread_local.h"
  #include "td/utils/Slice.h"
  #include "td/utils/StackAllocator.h"
  #include "td/utils/StringBuilder.h"
  
  #include <atomic>
  #include <type_traits>
  
  #define VERBOSITY_NAME(x) verbosity_##x
  
  #define GET_VERBOSITY_LEVEL() (::td::get_verbosity_level())
  #define SET_VERBOSITY_LEVEL(new_level) (::td::set_verbosity_level(new_level))
  
  #ifndef STRIP_LOG
  #define STRIP_LOG VERBOSITY_NAME(DEBUG)
  #endif
  #define LOG_IS_STRIPPED(strip_level) \
    (::std::integral_constant<int, VERBOSITY_NAME(strip_level)>() > ::std::integral_constant<int, STRIP_LOG>())
  
  #define LOGGER(interface, options, level, comment) ::td::Logger(interface, options, level, __FILE__, __LINE__, comment)
  
  #define LOG_IMPL_FULL(interface, options, strip_level, runtime_level, condition, comment) \
    LOG_IS_STRIPPED(strip_level) || runtime_level > options.get_level() || !(condition)     \
        ? (void)0                                                                           \
        : ::td::detail::Voidify() & LOGGER(interface, options, runtime_level, comment)
  
  #define LOG_IMPL(strip_level, level, condition, comment) \
    LOG_IMPL_FULL(*::td::log_interface, ::td::log_options, strip_level, VERBOSITY_NAME(level), condition, comment)
  
  #define LOG(level) LOG_IMPL(level, level, true, ::td::Slice())
  #define LOG_IF(level, condition) LOG_IMPL(level, level, condition, #condition)
  
  #define VLOG(level) LOG_IMPL(DEBUG, level, true, TD_DEFINE_STR(level))
  #define VLOG_IF(level, condition) LOG_IMPL(DEBUG, level, condition, TD_DEFINE_STR(level) " " #condition)
  
  #define LOG_TAG ::td::Logger::tag_
  #define LOG_TAG2 ::td::Logger::tag2_
  
  #if TD_CLANG
  bool no_return_func() __attribute__((analyzer_noreturn));
  #endif
  
  inline bool no_return_func() {
    return true;
  }
  
  // clang-format off
  #define DUMMY_LOG_CHECK(condition) LOG_IF(NEVER, !(condition))
  
  #ifdef TD_DEBUG
    #if TD_MSVC
      #define LOG_CHECK(condition)        \
        __analysis_assume(!!(condition)); \
        LOG_IMPL(FATAL, FATAL, !(condition), #condition)
    #else
      #define LOG_CHECK(condition) LOG_IMPL(FATAL, FATAL, !(condition) && no_return_func(), #condition)
    #endif
  #else
    #define LOG_CHECK DUMMY_LOG_CHECK
  #endif
  
  #if NDEBUG
    #define LOG_DCHECK DUMMY_LOG_CHECK
  #else
    #define LOG_DCHECK LOG_CHECK
  #endif
  // clang-format on
  
  constexpr int VERBOSITY_NAME(PLAIN) = -1;
  constexpr int VERBOSITY_NAME(FATAL) = 0;
  constexpr int VERBOSITY_NAME(ERROR) = 1;
  constexpr int VERBOSITY_NAME(WARNING) = 2;
  constexpr int VERBOSITY_NAME(INFO) = 3;
  constexpr int VERBOSITY_NAME(DEBUG) = 4;
  constexpr int VERBOSITY_NAME(NEVER) = 1024;
  
  namespace td {
  
  struct LogOptions {
    std::atomic<int> level{VERBOSITY_NAME(DEBUG) + 1};
    bool fix_newlines{true};
    bool add_info{true};
  
    int get_level() const {
      return level.load(std::memory_order_relaxed);
    }
    int set_level(int new_level) {
      return level.exchange(new_level);
    }
  
    static const LogOptions &plain() {
      static LogOptions plain_options{0, false, false};
      return plain_options;
    }
  
    constexpr LogOptions() = default;
    constexpr LogOptions(int level, bool fix_newlines, bool add_info)
        : level(level), fix_newlines(fix_newlines), add_info(add_info) {
    }
  
    LogOptions(const LogOptions &other) : LogOptions(other.level.load(), other.fix_newlines, other.add_info) {
    }
  
    LogOptions &operator=(const LogOptions &other) {
      if (this == &other) {
        return *this;
      }
      level = other.level.load();
      fix_newlines = other.fix_newlines;
      add_info = other.add_info;
      return *this;
    }
    LogOptions(LogOptions &&) = delete;
    LogOptions &operator=(LogOptions &&) = delete;
    ~LogOptions() = default;
  };
  
  extern LogOptions log_options;
  inline int set_verbosity_level(int level) {
    return log_options.set_level(level);
  }
  inline int get_verbosity_level() {
    return log_options.get_level();
  }
  
  class LogInterface {
   public:
    LogInterface() = default;
    LogInterface(const LogInterface &) = delete;
    LogInterface &operator=(const LogInterface &) = delete;
    LogInterface(LogInterface &&) = delete;
    LogInterface &operator=(LogInterface &&) = delete;
    virtual ~LogInterface() = default;
  
    void append(int log_level, CSlice slice);
  
    virtual void after_rotation() {
    }
  
    virtual vector<string> get_file_paths() {
      return {};
    }
  
    virtual void do_append(int log_level, CSlice slice) = 0;
  };
  
  extern LogInterface *const default_log_interface;
  extern LogInterface *log_interface;
  
  using OnFatalErrorCallback = void (*)(CSlice message);
  void set_log_fatal_error_callback(OnFatalErrorCallback callback);
  void set_log_disable_death_handler(bool disabled);
  
  [[noreturn]] void process_fatal_error(CSlice message);
  
  using OnLogMessageCallback = void (*)(int verbosity_level, CSlice message);
  void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback);
  
  class Logger {
    static const size_t BUFFER_SIZE = 128 * 1024;
  
   public:
    Logger(LogInterface &log, const LogOptions &options, int log_level)
        : buffer_(StackAllocator::alloc(BUFFER_SIZE))
        , log_(log)
        , sb_(buffer_.as_slice())
        , options_(options)
        , log_level_(log_level) {
    }
  
    Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num, Slice comment);
  
    template <class T>
    Logger &operator<<(T &&other) {
      sb_ << other;
      return *this;
    }
  
    MutableCSlice as_cslice() {
      return sb_.as_cslice();
    }
    bool is_error() const {
      return sb_.is_error();
    }
    Logger(const Logger &) = delete;
    Logger &operator=(const Logger &) = delete;
    Logger(Logger &&) = delete;
    Logger &operator=(Logger &&) = delete;
    ~Logger();
  
    static TD_THREAD_LOCAL const char *tag_;
    static TD_THREAD_LOCAL const char *tag2_;
  
   private:
    decltype(StackAllocator::alloc(0)) buffer_;
    LogInterface &log_;
    StringBuilder sb_;
    const LogOptions &options_;
    int log_level_;
  };
  
  class ScopedDisableLog {
   public:
    ScopedDisableLog();
    ScopedDisableLog(const ScopedDisableLog &) = delete;
    ScopedDisableLog &operator=(const ScopedDisableLog &) = delete;
    ScopedDisableLog(ScopedDisableLog &&) = delete;
    ScopedDisableLog &operator=(ScopedDisableLog &&) = delete;
    ~ScopedDisableLog();
  };
  
  namespace detail {
  class Voidify {
   public:
    template <class T>
    void operator&(const T &) {
    }
  };
  }  // namespace detail
  
  }  // namespace td
  