/// Internal shared header file. Not intended for external use. #pragma once #include #include "shared.h" #if __cplusplus >= 201703L #include #include #endif #include #include #include #include #include #include #include #include #include namespace tsdb_shared { static void throw_errno(string_view msg = "errno") { throw std::runtime_error(fmt::format("{}: {}", msg, strerror(errno))); } static void check_errno(bool condition, string_view msg = "errno") { if (!condition) { throw_errno(msg); } } /// @brief RAII wrapper for a binary file. Better than std::fstream. struct bin_file { bin_file() : file_(nullptr) {} bin_file(std::nullptr_t) : file_(nullptr) {} bin_file(const std::string& path, const char* mode) { file_ = fopen(path.c_str(), mode); if (!file_) { throw_errno(fmt::format("Failed to open file `{}` with mode {}", path, mode)); } } ~bin_file() { if (file_) { // Failures are ignored here fclose(file_); } } bin_file(const bin_file&) = delete; bin_file(bin_file&& other) : file_(other.file_) { other.file_ = nullptr; } bin_file& operator=(bin_file other) { std::swap(file_, other.file_); return *this; } operator bool() const { return file_ != nullptr; } void close() { // NOTE: C++11 does not support std::exchange auto file = file_; file_ = nullptr; if (file) { // check_errno(fclose(file) == 0, "Failed to close file"); if (fclose(file) != 0) { fprintf(stderr, "[ERROR] File %p closed with errno=%d\n", (void*)file, errno); } } } bool is_open() const { return file_ != nullptr; } size_t tell() const { auto pos = ftell(file_); check_errno(pos != -1, "Failed to get file position"); return pos; } void seek(ssize_t pos = 0, int whence = SEEK_SET) { check_errno(fseek(file_, pos, whence) == 0, "Failed to seek in file"); } void rewind() { check_errno(fseek(file_, 0, SEEK_SET) == 0, "Failed to rewind file"); } size_t read(void* data, size_t size) { auto count = fread(data, 1, size, file_); if (ferror(file_)) { throw_errno("Failed to read from file"); } return count; } void read_exact(void* data, size_t size) { check_errno(fread(data, 1, size, file_) == size, "Failed to read from file"); } void write(const void* data, size_t size) { check_errno(fwrite(data, 1, size, file_) == size, "Failed to write to file"); } void flush() { check_errno(fflush(file_) == 0, "Failed to flush file"); } void lock() { check_errno(flock(fileno(file_), LOCK_EX) == 0, "Failed to lock file"); } void unlock() { check_errno(flock(fileno(file_), LOCK_UN) == 0, "Failed to unlock file"); } void try_lock() { check_errno(flock(fileno(file_), LOCK_EX | LOCK_NB) == 0, "Failed to try lock file"); } private: FILE* file_; }; struct tsdb_exception : std::runtime_error { using std::runtime_error::runtime_error; tsdb_exception(const std::string& what, std::exception_ptr nested_exception) : std::runtime_error(what), nested_exception_(nested_exception) {} std::exception_ptr nested_exception() const noexcept { return nested_exception_; } private: std::exception_ptr nested_exception_ = nullptr; }; struct mylogger { enum class loglevel { trace, debug, info, warn, error, critical }; mylogger(std::string name) : mylogger(std::move(name), loglevel::info) {} mylogger(std::string name, loglevel level) : name_(std::move(name)), level_(level) {} mylogger(std::string name, loglevel level, bin_file log_file, bin_file err_file) : name_(std::move(name)), level_(level), log_file_(std::move(log_file)), err_file_(std::move(err_file)) {} static inline string_view to_string(loglevel lvl, bool can_color = false) { switch (lvl) { case loglevel::trace: return can_color ? "\033[37mtrace\033[0m" : "trace"; // white case loglevel::debug: return can_color ? "\033[36mdebug\033[0m" : "debug"; // cyan case loglevel::info: return can_color ? "\033[32minfo\033[0m" : "info"; // green case loglevel::warn: return can_color ? "\033[33mwarn\033[0m" : "warn"; // yellow case loglevel::error: return can_color ? "\033[31merror\033[0m" : "error"; // red case loglevel::critical: return can_color ? "\033[35mcritical\033[0m" : "critical"; // magenta default: return "unknown"; } } bool should_log(loglevel lvl) const { return lvl >= level_; } template void trace(fmt::format_string fmt, Args&&... args) { log_(loglevel::trace, fmt, std::forward(args)...); } template void debug(fmt::format_string fmt, Args&&... args) { log_(loglevel::debug, fmt, std::forward(args)...); } template void info(fmt::format_string fmt, Args&&... args) { log_(loglevel::info, fmt, std::forward(args)...); } template void warn(fmt::format_string fmt, Args&&... args) { log_(loglevel::warn, fmt, std::forward(args)...); } template void error(fmt::format_string fmt, Args&&... args) { log_(loglevel::error, fmt, std::forward(args)...); } template void critical(fmt::format_string fmt, Args&&... args) { log_(loglevel::critical, fmt, std::forward(args)...); } private: template void log_(loglevel lvl, fmt::string_view fmt, Args&&... args) { if (!should_log(lvl)) { return; } auto now = std::chrono::system_clock::now(); // auto now_time_t = std::chrono::system_clock::to_time_t(now); // auto now_ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; auto msg = fmt::format("[{:%Y-%m-%d %H:%M:%S}] [{}] [{}] ", now, to_string(lvl), name_); fmt::vformat_to(std::back_insert_iterator(msg), fmt, fmt::make_format_args(args...)); msg += '\n'; fprintf(stdout, "%s", msg.c_str()); if (log_file_) { log_file_.write(msg.data(), msg.size()); log_file_.flush(); } if (err_file_ && lvl >= loglevel::error) { err_file_.write(msg.data(), msg.size()); err_file_.flush(); } } std::string name_; loglevel level_ = loglevel::info; bin_file log_file_; bin_file err_file_; }; void init_tsdb_env(); mylogger& logger(); std::string random_uuid(); template typename std::decay()())>::type ex_log_boundary(F&& f, ErrRecoverFn&& err_recover_fn) try { return f(); #if __cplusplus >= 201703L } catch (const clickhouse::Error& e) { logger().error("Unhandled stck exception: {}", e.what()); return err_recover_fn(); // throw tsdb_exception(e.what(), std::current_exception()); #endif } catch (const std::exception& e) { logger().error("Unhandled generic exception: {}", e.what()); return err_recover_fn(); // throw; } } // namespace tsdb_shared #if __cplusplus < 201703L // Support libfmt template <> struct fmt::formatter : formatter { template auto format(tsdb_shared::string_view sv, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(fmt::string_view(sv.data(), sv.size()), ctx); } }; #endif // Preludes using tsdb_shared::bin_file; using tsdb_shared::check_errno; using tsdb_shared::ex_log_boundary; using tsdb_shared::logger; using tsdb_shared::throw_errno;