mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			350 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
    This file is part of TON Blockchain Library.
 | 
						|
 | 
						|
    TON Blockchain Library is free software: you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU Lesser General Public License as published by
 | 
						|
    the Free Software Foundation, either version 2 of the License, or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    TON Blockchain Library is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public License
 | 
						|
    along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    Copyright 2017-2020 Telegram Systems LLP
 | 
						|
*/
 | 
						|
#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 PSTR_IMPL() ::td::Logger(::td::NullLog().ref(), ::td::LogOptions::plain(), 0)
 | 
						|
#define PSLICE() ::td::detail::Slicify() & PSTR_IMPL()
 | 
						|
#define PSTRING() ::td::detail::Stringify() & PSTR_IMPL()
 | 
						|
#define PSLICE_SAFE() ::td::detail::SlicifySafe() & PSTR_IMPL()
 | 
						|
#define PSTRING_SAFE() ::td::detail::StringifySafe() & PSTR_IMPL()
 | 
						|
 | 
						|
#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_ROTATE() ::td::log_interface->rotate()
 | 
						|
 | 
						|
#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 {
 | 
						|
// TODO Not part of utils. Should be in some separate file
 | 
						|
extern int VERBOSITY_NAME(mtproto);
 | 
						|
extern int VERBOSITY_NAME(raw_mtproto);
 | 
						|
extern int VERBOSITY_NAME(dc);
 | 
						|
extern int VERBOSITY_NAME(fd);
 | 
						|
extern int VERBOSITY_NAME(net_query);
 | 
						|
extern int VERBOSITY_NAME(td_requests);
 | 
						|
extern int VERBOSITY_NAME(actor);
 | 
						|
extern int VERBOSITY_NAME(files);
 | 
						|
extern int VERBOSITY_NAME(sqlite);
 | 
						|
 | 
						|
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 LogOptions plain() {
 | 
						|
    return LogOptions{0, false, false};
 | 
						|
  }
 | 
						|
 | 
						|
  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) {
 | 
						|
    level = other.level.load();
 | 
						|
    fix_newlines = other.fix_newlines;
 | 
						|
    add_info = other.add_info;
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
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 ScopedDisableLog {
 | 
						|
 public:
 | 
						|
  ScopedDisableLog();
 | 
						|
  ~ScopedDisableLog();
 | 
						|
};
 | 
						|
 | 
						|
class LogInterface {
 | 
						|
 public:
 | 
						|
  LogInterface() = default;
 | 
						|
  LogInterface(const LogInterface &) = delete;
 | 
						|
  LogInterface &operator=(const LogInterface &) = delete;
 | 
						|
  LogInterface(LogInterface &&) = delete;
 | 
						|
  LogInterface &operator=(LogInterface &&) = delete;
 | 
						|
  virtual ~LogInterface() = default;
 | 
						|
  virtual void append(CSlice slice) {
 | 
						|
    append(slice, -1);
 | 
						|
  }
 | 
						|
  virtual void append(CSlice slice, int /*log_level*/) {
 | 
						|
    append(slice);
 | 
						|
  }
 | 
						|
  virtual void rotate() {
 | 
						|
  }
 | 
						|
  virtual vector<string> get_file_paths() {
 | 
						|
    return {};
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class NullLog : public LogInterface {
 | 
						|
 public:
 | 
						|
  void append(CSlice /*slice*/, int /*log_level*/) override {
 | 
						|
  }
 | 
						|
  void rotate() override {
 | 
						|
  }
 | 
						|
  NullLog &ref() {
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
extern LogInterface *const default_log_interface;
 | 
						|
extern LogInterface *log_interface;
 | 
						|
 | 
						|
using OnFatalErrorCallback = void (*)(CSlice message);
 | 
						|
void set_log_fatal_error_callback(OnFatalErrorCallback callback);
 | 
						|
 | 
						|
[[noreturn]] void process_fatal_error(CSlice message);
 | 
						|
 | 
						|
#define TC_RED "\x1b[1;31m"
 | 
						|
#define TC_BLUE "\x1b[1;34m"
 | 
						|
#define TC_CYAN "\x1b[1;36m"
 | 
						|
#define TC_GREEN "\x1b[1;32m"
 | 
						|
#define TC_YELLOW "\x1b[1;33m"
 | 
						|
#define TC_EMPTY "\x1b[0m"
 | 
						|
 | 
						|
class TsCerr {
 | 
						|
 public:
 | 
						|
  TsCerr();
 | 
						|
  TsCerr(const TsCerr &) = delete;
 | 
						|
  TsCerr &operator=(const TsCerr &) = delete;
 | 
						|
  TsCerr(TsCerr &&) = delete;
 | 
						|
  TsCerr &operator=(TsCerr &&) = delete;
 | 
						|
  ~TsCerr();
 | 
						|
  TsCerr &operator<<(Slice slice);
 | 
						|
 | 
						|
 private:
 | 
						|
  using Lock = std::atomic_flag;
 | 
						|
  static Lock lock_;
 | 
						|
 | 
						|
  void enterCritical();
 | 
						|
  void exitCritical();
 | 
						|
};
 | 
						|
 | 
						|
class Logger {
 | 
						|
 public:
 | 
						|
  static const int BUFFER_SIZE = 128 * 1024;
 | 
						|
  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<<(const 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_;
 | 
						|
};
 | 
						|
 | 
						|
namespace detail {
 | 
						|
class Voidify {
 | 
						|
 public:
 | 
						|
  template <class T>
 | 
						|
  void operator&(const T &) {
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class Slicify {
 | 
						|
 public:
 | 
						|
  CSlice operator&(Logger &logger) {
 | 
						|
    return logger.as_cslice();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class Stringify {
 | 
						|
 public:
 | 
						|
  string operator&(Logger &logger) {
 | 
						|
    return logger.as_cslice().str();
 | 
						|
  }
 | 
						|
};
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
class TsLog : public LogInterface {
 | 
						|
 public:
 | 
						|
  explicit TsLog(LogInterface *log) : log_(log) {
 | 
						|
  }
 | 
						|
  void init(LogInterface *log) {
 | 
						|
    enter_critical();
 | 
						|
    log_ = log;
 | 
						|
    exit_critical();
 | 
						|
  }
 | 
						|
  void append(CSlice slice, int level) override {
 | 
						|
    enter_critical();
 | 
						|
    log_->append(slice, level);
 | 
						|
    exit_critical();
 | 
						|
  }
 | 
						|
  void rotate() override {
 | 
						|
    enter_critical();
 | 
						|
    log_->rotate();
 | 
						|
    exit_critical();
 | 
						|
  }
 | 
						|
  vector<string> get_file_paths() override {
 | 
						|
    enter_critical();
 | 
						|
    auto result = log_->get_file_paths();
 | 
						|
    exit_critical();
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  LogInterface *log_ = nullptr;
 | 
						|
  std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
 | 
						|
  void enter_critical() {
 | 
						|
    while (lock_.test_and_set(std::memory_order_acquire)) {
 | 
						|
      // spin
 | 
						|
    }
 | 
						|
  }
 | 
						|
  void exit_critical() {
 | 
						|
    lock_.clear(std::memory_order_release);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace td
 |