Browse Source

Moved to report to spdlog allowing asyncronous console and file logging

develop
Eyck Jentzsch 4 months ago
parent
commit
5087ea5124
79 changed files with 17677 additions and 65 deletions
  1. 28
    5
      incl/scc/report.h
  2. 93
    0
      incl/spdlog/async.h
  3. 92
    0
      incl/spdlog/async_logger-inl.h
  4. 68
    0
      incl/spdlog/async_logger.h
  5. 57
    0
      incl/spdlog/common-inl.h
  6. 246
    0
      incl/spdlog/common.h
  7. 74
    0
      incl/spdlog/details/backtracer-inl.h
  8. 46
    0
      incl/spdlog/details/backtracer.h
  9. 119
    0
      incl/spdlog/details/circular_q.h
  10. 32
    0
      incl/spdlog/details/console_globals.h
  11. 133
    0
      incl/spdlog/details/file_helper-inl.h
  12. 60
    0
      incl/spdlog/details/file_helper.h
  13. 111
    0
      incl/spdlog/details/fmt_helper.h
  14. 34
    0
      incl/spdlog/details/log_msg-inl.h
  15. 35
    0
      incl/spdlog/details/log_msg.h
  16. 60
    0
      incl/spdlog/details/log_msg_buffer-inl.h
  17. 33
    0
      incl/spdlog/details/log_msg_buffer.h
  18. 120
    0
      incl/spdlog/details/mpmc_blocking_q.h
  19. 49
    0
      incl/spdlog/details/null_mutex.h
  20. 465
    0
      incl/spdlog/details/os-inl.h
  21. 98
    0
      incl/spdlog/details/os.h
  22. 1317
    0
      incl/spdlog/details/pattern_formatter-inl.h
  23. 99
    0
      incl/spdlog/details/pattern_formatter.h
  24. 49
    0
      incl/spdlog/details/periodic_worker-inl.h
  25. 40
    0
      incl/spdlog/details/periodic_worker.h
  26. 284
    0
      incl/spdlog/details/registry-inl.h
  27. 109
    0
      incl/spdlog/details/registry.h
  28. 24
    0
      incl/spdlog/details/synchronous_factory.h
  29. 127
    0
      incl/spdlog/details/thread_pool-inl.h
  30. 120
    0
      incl/spdlog/details/thread_pool.h
  31. 175
    0
      incl/spdlog/fmt/bin_to_hex.h
  32. 27
    0
      incl/spdlog/fmt/bundled/LICENSE.rst
  33. 829
    0
      incl/spdlog/fmt/bundled/chrono.h
  34. 585
    0
      incl/spdlog/fmt/bundled/color.h
  35. 466
    0
      incl/spdlog/fmt/bundled/compile.h
  36. 1414
    0
      incl/spdlog/fmt/bundled/core.h
  37. 1000
    0
      incl/spdlog/fmt/bundled/format-inl.h
  38. 3600
    0
      incl/spdlog/fmt/bundled/format.h
  39. 77
    0
      incl/spdlog/fmt/bundled/locale.h
  40. 136
    0
      incl/spdlog/fmt/bundled/ostream.h
  41. 311
    0
      incl/spdlog/fmt/bundled/posix.h
  42. 715
    0
      incl/spdlog/fmt/bundled/printf.h
  43. 288
    0
      incl/spdlog/fmt/bundled/ranges.h
  44. 293
    0
      incl/spdlog/fmt/bundled/safe-duration-cast.h
  45. 27
    0
      incl/spdlog/fmt/fmt.h
  46. 18
    0
      incl/spdlog/fmt/ostr.h
  47. 18
    0
      incl/spdlog/formatter.h
  48. 246
    0
      incl/spdlog/logger-inl.h
  49. 383
    0
      incl/spdlog/logger.h
  50. 119
    0
      incl/spdlog/sinks/android_sink.h
  51. 136
    0
      incl/spdlog/sinks/ansicolor_sink-inl.h
  52. 113
    0
      incl/spdlog/sinks/ansicolor_sink.h
  53. 63
    0
      incl/spdlog/sinks/base_sink-inl.h
  54. 46
    0
      incl/spdlog/sinks/base_sink.h
  55. 43
    0
      incl/spdlog/sinks/basic_file_sink-inl.h
  56. 58
    0
      incl/spdlog/sinks/basic_file_sink.h
  57. 185
    0
      incl/spdlog/sinks/daily_file_sink.h
  58. 93
    0
      incl/spdlog/sinks/dist_sink.h
  59. 94
    0
      incl/spdlog/sinks/dup_filter_sink.h
  60. 48
    0
      incl/spdlog/sinks/msvc_sink.h
  61. 44
    0
      incl/spdlog/sinks/null_sink.h
  62. 50
    0
      incl/spdlog/sinks/ostream_sink.h
  63. 130
    0
      incl/spdlog/sinks/rotating_file_sink-inl.h
  64. 78
    0
      incl/spdlog/sinks/rotating_file_sink.h
  65. 25
    0
      incl/spdlog/sinks/sink-inl.h
  66. 35
    0
      incl/spdlog/sinks/sink.h
  67. 38
    0
      incl/spdlog/sinks/stdout_color_sinks-inl.h
  68. 45
    0
      incl/spdlog/sinks/stdout_color_sinks.h
  69. 94
    0
      incl/spdlog/sinks/stdout_sinks-inl.h
  70. 76
    0
      incl/spdlog/sinks/stdout_sinks.h
  71. 108
    0
      incl/spdlog/sinks/syslog_sink.h
  72. 98
    0
      incl/spdlog/sinks/systemd_sink.h
  73. 175
    0
      incl/spdlog/sinks/wincolor_sink-inl.h
  74. 92
    0
      incl/spdlog/sinks/wincolor_sink.h
  75. 115
    0
      incl/spdlog/spdlog-inl.h
  76. 342
    0
      incl/spdlog/spdlog.h
  77. 136
    0
      incl/spdlog/tweakme.h
  78. 10
    0
      incl/spdlog/version.h
  79. 158
    60
      src/report.cpp

+ 28
- 5
incl/scc/report.h View File

@@ -25,18 +25,40 @@
#include <util/logging.h>
#include <util/ities.h>

namespace logging {
class SystemC {};
class STDIO {};
}
namespace scc {

/**
* initializes the SystemC logging system to use logging::Logger with a particular logging level
* initializes the SystemC logging system with a particular logging level
*
* @param level the logging level
* @param type_field_width width of the message type field in output, setting to zero suppresses the message type
* @param print_time wheter to print the system time stamp
*/
void init_logging(logging::log_level level = logging::WARNING, unsigned type_field_width = 24, bool print_time = false);
/**
* the configuration class for the logging setup
*/
struct LogConfig {
logging::log_level level{logging::WARNING};
unsigned msg_type_field_width{24};
bool print_sys_time{false};
bool print_sim_time{true};
bool print_severity{true};
std::string log_file_name{""};

LogConfig& setLogLevel(logging::log_level);
LogConfig& setFieldWidth( unsigned);
LogConfig& setPrintSysTime(bool);
LogConfig& setPrintSimTime(bool);
LogConfig& setPrintSeverity(bool);
LogConfig& setLogFileName(std::string&&);
};
/**
* initializes the SystemC logging system with a particular configuration
*
* @param log_config the logging configuration
*/
void init_logging(const LogConfig& log_config);
/**
* the logger class
*/
@@ -203,6 +225,7 @@ public:
void reset();
protected:
std::streamsize xsputn(const char_type* s, std::streamsize n) override;
int sync() override;
std::ostream& os;
logging::log_level level;
std::streambuf* old_buf;

+ 93
- 0
incl/spdlog/async.h View File

@@ -0,0 +1,93 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

//
// Async logging using global thread pool
// All loggers created here share same global thread pool.
// Each log message is pushed to a queue along withe a shared pointer to the
// logger.
// If a logger deleted while having pending messages in the queue, it's actual
// destruction will defer
// until all its messages are processed by the thread pool.
// This is because each message in the queue holds a shared_ptr to the
// originating logger.

#include "spdlog/async_logger.h"
#include "spdlog/details/registry.h"
#include "spdlog/details/thread_pool.h"

#include <memory>
#include <mutex>
#include <functional>

namespace spdlog {

namespace details {
static const size_t default_async_q_size = 8192;
}

// async logger factory - creates async loggers backed with thread pool.
// if a global thread pool doesn't already exist, create it with default queue
// size of 8192 items and single thread.
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
{
auto &registry_inst = details::registry::instance();

// create global thread pool if not already exists..

auto &mutex = registry_inst.tp_mutex();
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
auto tp = registry_inst.get_tp();
if (tp == nullptr)
{
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
registry_inst.set_tp(tp);
}

auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.initialize_logger(new_logger);
return new_logger;
}
};

using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;

template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}

template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}

// set global thread pool.
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
{
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
details::registry::instance().set_tp(std::move(tp));
}

// set global thread pool.
inline void init_thread_pool(size_t q_size, size_t thread_count)
{
init_thread_pool(q_size, thread_count, [] {});
}

// get the global thread pool.
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
{
return details::registry::instance().get_tp();
}
} // namespace spdlog

+ 92
- 0
incl/spdlog/async_logger-inl.h View File

@@ -0,0 +1,92 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/async_logger.h"
#endif

#include "spdlog/sinks/sink.h"
#include "spdlog/details/thread_pool.h"

#include <memory>
#include <string>

SPDLOG_INLINE spdlog::async_logger::async_logger(
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
{}

SPDLOG_INLINE spdlog::async_logger::async_logger(
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
{}

// send the log message to the thread pool
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
{
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
}
else
{
SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore"));
}
}

// send flush request to the thread pool
SPDLOG_INLINE void spdlog::async_logger::flush_()
{
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
}
else
{
SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore"));
}
}

//
// backend functions - called from the thread pool to do the actual job
//
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
{
for (auto &sink : sinks_)
{
if (sink->should_log(msg.level))
{
SPDLOG_TRY
{
sink->log(msg);
}
SPDLOG_LOGGER_CATCH()
}
}

if (should_flush_(msg))
{
backend_flush_();
}
}

SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
{
for (auto &sink : sinks_)
{
SPDLOG_TRY
{
sink->flush();
}
SPDLOG_LOGGER_CATCH()
}
}

SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
{
auto cloned = std::make_shared<spdlog::async_logger>(*this);
cloned->name_ = std::move(new_name);
return cloned;
}

+ 68
- 0
incl/spdlog/async_logger.h View File

@@ -0,0 +1,68 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

// Fast asynchronous logger.
// Uses pre allocated queue.
// Creates a single back thread to pop messages from the queue and log them.
//
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Push a new copy of the message to a queue (or block the caller until
// space is available in the queue)
// Upon destruction, logs all remaining messages in the queue before
// destructing..

#include "spdlog/logger.h"

namespace spdlog {

// Async overflow policy - block by default.
enum class async_overflow_policy
{
block, // Block until message can be enqueued
overrun_oldest // Discard oldest message in the queue if full when trying to
// add new item.
};

namespace details {
class thread_pool;
}

class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{
friend class details::thread_pool;

public:
template<typename It>
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block)
: logger(std::move(logger_name), begin, end)
, thread_pool_(std::move(tp))
, overflow_policy_(overflow_policy)
{}

async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);

async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);

std::shared_ptr<logger> clone(std::string new_name) override;

protected:
void sink_it_(const details::log_msg &msg) override;
void flush_() override;
void backend_sink_it_(const details::log_msg &incoming_log_msg);
void backend_flush_();

private:
std::weak_ptr<details::thread_pool> thread_pool_;
async_overflow_policy overflow_policy_;
};
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "async_logger-inl.h"
#endif

+ 57
- 0
incl/spdlog/common-inl.h View File

@@ -0,0 +1,57 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/common.h"
#endif

namespace spdlog {
namespace level {
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;

static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;

SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return level_string_views[l];
}

SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return short_level_names[l];
}

SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
{
int level = 0;
for (const auto &level_str : level_string_views)
{
if (level_str == name)
{
return static_cast<level::level_enum>(level);
}
level++;
}
return level::off;
}
} // namespace level

SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
: msg_(std::move(msg))
{}

SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
{
memory_buf_t outbuf;
fmt::format_system_error(outbuf, last_errno, msg);
msg_ = fmt::to_string(outbuf);
}

SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
{
return msg_.c_str();
}

} // namespace spdlog

+ 246
- 0
incl/spdlog/common.h View File

@@ -0,0 +1,246 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/tweakme.h"
#include "spdlog/details/null_mutex.h"

#include <atomic>
#include <chrono>
#include <initializer_list>
#include <memory>
#include <exception>
#include <string>
#include <type_traits>
#include <functional>

#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#endif //_WIN32

#ifdef SPDLOG_COMPILED_LIB
#undef SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE
#else
#define SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE inline
#endif

#include "spdlog/fmt/fmt.h"

// visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT _NOEXCEPT
#define SPDLOG_CONSTEXPR
#else
#define SPDLOG_NOEXCEPT noexcept
#define SPDLOG_CONSTEXPR constexpr
#endif

#if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated)
#else
#define SPDLOG_DEPRECATED
#endif

// disable thread local on msvc 2013
#ifndef SPDLOG_NO_TLS
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
#define SPDLOG_NO_TLS 1
#endif
#endif

#ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION __FUNCTION__
#endif

#ifdef SPDLOG_NO_EXCEPTIONS
#define SPDLOG_TRY
#define SPDLOG_THROW(ex) \
do \
{ \
printf("spdlog fatal error: %s\n", ex.what()); \
std::abort(); \
} while (0)
#define SPDLOG_CATCH_ALL()
#else
#define SPDLOG_TRY try
#define SPDLOG_THROW(ex) throw(ex)
#define SPDLOG_CATCH_ALL() catch (...)
#endif

namespace spdlog {

class formatter;

namespace sinks {
class sink;
}

#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring;
#define SPDLOG_FILENAME_T(s) L##s
#else
using filename_t = std::string;
#define SPDLOG_FILENAME_T(s) s
#endif

using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using err_handler = std::function<void(const std::string &err_msg)>;
using string_view_t = fmt::basic_string_view<char>;
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;

#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else
using wstring_view_t = fmt::basic_string_view<wchar_t>;

template<typename T>
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
{};
#endif // _WIN32
#else
template<typename>
struct is_convertible_to_wstring_view : std::false_type
{};
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT

#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic<int>;
#endif

#define SPDLOG_LEVEL_TRACE 0
#define SPDLOG_LEVEL_DEBUG 1
#define SPDLOG_LEVEL_INFO 2
#define SPDLOG_LEVEL_WARN 3
#define SPDLOG_LEVEL_ERROR 4
#define SPDLOG_LEVEL_CRITICAL 5
#define SPDLOG_LEVEL_OFF 6

#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif

// Log level enum
namespace level {
enum level_enum
{
trace = SPDLOG_LEVEL_TRACE,
debug = SPDLOG_LEVEL_DEBUG,
info = SPDLOG_LEVEL_INFO,
warn = SPDLOG_LEVEL_WARN,
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
};

#if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES \
{ \
"trace", "debug", "info", "warning", "error", "critical", "off" \
}
#endif

#if !defined(SPDLOG_SHORT_LEVEL_NAMES)

#define SPDLOG_SHORT_LEVEL_NAMES \
{ \
"T", "D", "I", "W", "E", "C", "O" \
}
#endif

string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;

using level_hasher = std::hash<int>;
} // namespace level

//
// Color mode used by sinks with color support.
//
enum class color_mode
{
always,
automatic,
never
};

//
// Pattern time - specific time getting to use for pattern_formatter.
// local time by default
//
enum class pattern_time_type
{
local, // log localtime
utc // log utc
};

//
// Log exception
//
class spdlog_ex : public std::exception
{
public:
explicit spdlog_ex(std::string msg);
spdlog_ex(const std::string &msg, int last_errno);
const char *what() const SPDLOG_NOEXCEPT override;

private:
std::string msg_;
};

struct source_loc
{
SPDLOG_CONSTEXPR source_loc() = default;
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
: filename{filename_in}
, line{line_in}
, funcname{funcname_in}
{}

SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
{
return line == 0;
}
const char *filename{nullptr};
int line{0};
const char *funcname{nullptr};
};

namespace details {
// make_unique support for pre c++14

#if __cplusplus >= 201402L // C++14 and beyond
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
#endif
} // namespace details

} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "common-inl.h"
#endif

+ 74
- 0
incl/spdlog/details/backtracer-inl.h View File

@@ -0,0 +1,74 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/details/backtracer.h"
#endif
namespace spdlog {
namespace details {
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
{
std::lock_guard<std::mutex> lock(other.mutex_);
enabled_ = other.enabled();
messages_ = other.messages_;
}

SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
{
std::lock_guard<std::mutex> lock(other.mutex_);
enabled_ = other.enabled();
messages_ = std::move(other.messages_);
}

SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
{
std::lock_guard<std::mutex> lock(mutex_);
enabled_ = other.enabled();
messages_ = other.messages_;
return *this;
}

SPDLOG_INLINE void backtracer::enable(size_t size)
{
std::lock_guard<std::mutex> lock{mutex_};
enabled_.store(true, std::memory_order_relaxed);
messages_ = circular_q<log_msg_buffer>{size};
}

SPDLOG_INLINE void backtracer::disable()
{
std::lock_guard<std::mutex> lock{mutex_};
enabled_.store(false, std::memory_order_relaxed);
}

SPDLOG_INLINE bool backtracer::enabled() const
{
return enabled_.load(std::memory_order_relaxed);
}

SPDLOG_INLINE backtracer::operator bool() const
{
return enabled();
}

SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
{
std::lock_guard<std::mutex> lock{mutex_};
messages_.push_back(log_msg_buffer{msg});
}

// pop all items in the q and apply the given fun on each of them.
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
{
std::lock_guard<std::mutex> lock{mutex_};
while (!messages_.empty())
{
auto &front_msg = messages_.front();
fun(front_msg);
messages_.pop_front();
}
}
} // namespace details
} // namespace spdlog

+ 46
- 0
incl/spdlog/details/backtracer.h View File

@@ -0,0 +1,46 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/details/log_msg_buffer.h"
#include "spdlog/details/circular_q.h"

#include <atomic>
#include <mutex>
#include <functional>

// Store log messages in circular buffer.
// Useful for storing debug data in case of error/warning happens.

namespace spdlog {
namespace details {
class backtracer
{
mutable std::mutex mutex_;
std::atomic<bool> enabled_{false};
circular_q<log_msg_buffer> messages_;

public:
backtracer() = default;
backtracer(const backtracer &other);

backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
backtracer &operator=(backtracer other);

void enable(size_t size);
void disable();
bool enabled() const;
explicit operator bool() const;
void push_back(const log_msg &msg);

// pop all items in the q and apply the given fun on each of them.
void foreach_pop(std::function<void(const details::log_msg &)> fun);
};

} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "backtracer-inl.h"
#endif

+ 119
- 0
incl/spdlog/details/circular_q.h View File

@@ -0,0 +1,119 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

// cirucal q view of std::vector.
#pragma once

#include <vector>

namespace spdlog {
namespace details {
template<typename T>
class circular_q
{
size_t max_items_ = 0;
typename std::vector<T>::size_type head_ = 0;
typename std::vector<T>::size_type tail_ = 0;
size_t overrun_counter_ = 0;
std::vector<T> v_;

public:
using value_type = T;

// empty ctor - create a disabled queue with no elements allocated at all
circular_q() = default;

explicit circular_q(size_t max_items)
: max_items_(max_items + 1) // one item is reserved as marker for full q
, v_(max_items_)
{}

circular_q(const circular_q &) = default;
circular_q &operator=(const circular_q &) = default;

// move cannot be default,
// since we need to reset head_, tail_, etc to zero in the moved object
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
{
copy_moveable(std::move(other));
}

circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
{
copy_moveable(std::move(other));
return *this;
}

// push back, overrun (oldest) item if no room left
void push_back(T &&item)
{
if (max_items_ > 0)
{
v_[tail_] = std::move(item);
tail_ = (tail_ + 1) % max_items_;

if (tail_ == head_) // overrun last item if full
{
head_ = (head_ + 1) % max_items_;
++overrun_counter_;
}
}
}

// Return reference to the front item.
// If there are no elements in the container, the behavior is undefined.
const T &front() const
{
return v_[head_];
}

T &front()
{
return v_[head_];
}

// Pop item from front.
// If there are no elements in the container, the behavior is undefined.
void pop_front()
{
head_ = (head_ + 1) % max_items_;
}

bool empty() const
{
return tail_ == head_;
}

bool full() const
{
// head is ahead of the tail by 1
if (max_items_ > 0)
{
return ((tail_ + 1) % max_items_) == head_;
}
return false;
}

size_t overrun_counter() const
{
return overrun_counter_;
}

private:
// copy from other&& and reset it to disabled state
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
{
max_items_ = other.max_items_;
head_ = other.head_;
tail_ = other.tail_;
overrun_counter_ = other.overrun_counter_;
v_ = std::move(other.v_);

// put &&other in disabled, but valid state
other.max_items_ = 0;
other.head_ = other.tail_ = 0;
other.overrun_counter_ = 0;
}
};
} // namespace details
} // namespace spdlog

+ 32
- 0
incl/spdlog/details/console_globals.h View File

@@ -0,0 +1,32 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/details/null_mutex.h"
#include <mutex>

namespace spdlog {
namespace details {

struct console_mutex
{
using mutex_t = std::mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};

struct console_nullmutex
{
using mutex_t = null_mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};
} // namespace details
} // namespace spdlog

+ 133
- 0
incl/spdlog/details/file_helper-inl.h View File

@@ -0,0 +1,133 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/details/file_helper.h"
#endif

#include "spdlog/details/os.h"
#include "spdlog/common.h"

#include <cerrno>
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <tuple>

namespace spdlog {
namespace details {

SPDLOG_INLINE file_helper::~file_helper()
{
close();
}

SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
{
close();
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
if (!os::fopen_s(&fd_, fname, mode))
{
return;
}

details::os::sleep_for_millis(open_interval);
}

SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno));
}

SPDLOG_INLINE void file_helper::reopen(bool truncate)
{
if (_filename.empty())
{
SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
}
open(_filename, truncate);
}

SPDLOG_INLINE void file_helper::flush()
{
std::fflush(fd_);
}

SPDLOG_INLINE void file_helper::close()
{
if (fd_ != nullptr)
{
std::fclose(fd_);
fd_ = nullptr;
}
}

SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
{
size_t msg_size = buf.size();
auto data = buf.data();
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
{
SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno));
}
}

SPDLOG_INLINE size_t file_helper::size() const
{
if (fd_ == nullptr)
{
SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)));
}
return os::filesize(fd_);
}

SPDLOG_INLINE const filename_t &file_helper::filename() const
{
return _filename;
}

SPDLOG_INLINE bool file_helper::file_exists(const filename_t &fname)
{
return os::file_exists(fname);
}

//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
{
auto ext_index = fname.rfind('.');

// no valid extension found - return whole path and empty string as
// extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
{
return std::make_tuple(fname, filename_t());
}

// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.rfind(details::os::folder_sep);
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
{
return std::make_tuple(fname, filename_t());
}

// finally - return a valid base and extension tuple
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
}
} // namespace details
} // namespace spdlog

+ 60
- 0
incl/spdlog/details/file_helper.h View File

@@ -0,0 +1,60 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/common.h"
#include <tuple>

namespace spdlog {
namespace details {

// Helper class for file sinks.
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
// Throw spdlog_ex exception on errors.

class file_helper
{
public:
explicit file_helper() = default;

file_helper(const file_helper &) = delete;
file_helper &operator=(const file_helper &) = delete;
~file_helper();

void open(const filename_t &fname, bool truncate = false);
void reopen(bool truncate);
void flush();
void close();
void write(const memory_buf_t &buf);
size_t size() const;
const filename_t &filename() const;
static bool file_exists(const filename_t &fname);

//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);

private:
const int open_tries = 5;
const int open_interval = 10;
std::FILE *fd_{nullptr};
filename_t _filename;
};
} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "file_helper-inl.h"
#endif

+ 111
- 0
incl/spdlog/details/fmt_helper.h View File

@@ -0,0 +1,111 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once

#include <chrono>
#include <type_traits>
#include "spdlog/fmt/fmt.h"
#include "spdlog/common.h"

// Some fmt helpers to efficiently format and pad ints and strings
namespace spdlog {
namespace details {
namespace fmt_helper {

inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
{
return spdlog::string_view_t{buf.data(), buf.size()};
}

inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
{
auto *buf_ptr = view.data();
if (buf_ptr != nullptr)
{
dest.append(buf_ptr, buf_ptr + view.size());
}
}

template<typename T>
inline void append_int(T n, memory_buf_t &dest)
{
fmt::format_int i(n);
dest.append(i.data(), i.data() + i.size());
}

template<typename T>
inline unsigned count_digits(T n)
{
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
}

inline void pad2(int n, memory_buf_t &dest)
{
if (n > 99)
{
append_int(n, dest);
}
else if (n > 9) // 10-99
{
dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back(static_cast<char>('0' + n % 10));
}
else if (n >= 0) // 0-9
{
dest.push_back('0');
dest.push_back(static_cast<char>('0' + n));
}
else // negatives (unlikely, but just in case, let fmt deal with it)
{
fmt::format_to(dest, "{:02}", n);
}
}

template<typename T>
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
{
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
auto digits = count_digits(n);
if (width > digits)
{
const char *zeroes = "0000000000000000000";
dest.append(zeroes, zeroes + width - digits);
}
append_int(n, dest);
}

template<typename T>
inline void pad3(T n, memory_buf_t &dest)
{
pad_uint(n, 3, dest);
}

template<typename T>
inline void pad6(T n, memory_buf_t &dest)
{
pad_uint(n, 6, dest);
}

template<typename T>
inline void pad9(T n, memory_buf_t &dest)
{
pad_uint(n, 9, dest);
}

// return fraction of a second of the given time_point.
// e.g.
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
template<typename ToDuration>
inline ToDuration time_fraction(log_clock::time_point tp)
{
using std::chrono::duration_cast;
using std::chrono::seconds;
auto duration = tp.time_since_epoch();
auto secs = duration_cast<seconds>(duration);
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
}

} // namespace fmt_helper
} // namespace details
} // namespace spdlog

+ 34
- 0
incl/spdlog/details/log_msg-inl.h View File

@@ -0,0 +1,34 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/details/log_msg.h"
#endif

#include "spdlog/details/os.h"

namespace spdlog {
namespace details {

SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
: logger_name(logger_name)
, level(lvl)
#ifndef SPDLOG_NO_DATETIME
, time(os::now())
#endif

#ifndef SPDLOG_NO_THREAD_ID
, thread_id(os::thread_id())
#endif
, source(loc)
, payload(msg)
{}

SPDLOG_INLINE log_msg::log_msg(string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
: log_msg(source_loc{}, logger_name, lvl, msg)
{}

} // namespace details
} // namespace spdlog

+ 35
- 0
incl/spdlog/details/log_msg.h View File

@@ -0,0 +1,35 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/common.h"
#include <string>

namespace spdlog {
namespace details {
struct log_msg
{
log_msg() = default;
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(const log_msg &other) = default;

string_view_t logger_name;
level::level_enum level{level::off};
log_clock::time_point time;
size_t thread_id{0};

// wrapping the formatted text with color (updated by pattern_formatter).
mutable size_t color_range_start{0};
mutable size_t color_range_end{0};

source_loc source;
string_view_t payload;
};
} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "log_msg-inl.h"
#endif

+ 60
- 0
incl/spdlog/details/log_msg_buffer-inl.h View File

@@ -0,0 +1,60 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/details/log_msg_buffer.h"
#endif

namespace spdlog {
namespace details {

SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
: log_msg{orig_msg}
{
buffer.append(logger_name.begin(), logger_name.end());
buffer.append(payload.begin(), payload.end());
update_string_views();
}

SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
: log_msg{other}
{
buffer.append(logger_name.begin(), logger_name.end());
buffer.append(payload.begin(), payload.end());
update_string_views();
}

SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other)
: log_msg{std::move(other)}
, buffer{std::move(other.buffer)}
{
update_string_views();
}

SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
{
log_msg::operator=(other);
buffer.clear();
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
update_string_views();
return *this;
}

SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other)
{
log_msg::operator=(std::move(other));
buffer = std::move(other.buffer);
update_string_views();
return *this;
}

SPDLOG_INLINE void log_msg_buffer::update_string_views()
{
logger_name = string_view_t{buffer.data(), logger_name.size()};
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
}

} // namespace details
} // namespace spdlog

+ 33
- 0
incl/spdlog/details/log_msg_buffer.h View File

@@ -0,0 +1,33 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/details/log_msg.h"

namespace spdlog {
namespace details {

// Extend log_msg with internal buffer to store its payload.
// THis is needed since log_msg holds string_views that points to stack data.

class log_msg_buffer : public log_msg
{
memory_buf_t buffer;
void update_string_views();

public:
log_msg_buffer() = default;
explicit log_msg_buffer(const log_msg &orig_msg);
log_msg_buffer(const log_msg_buffer &other);
log_msg_buffer(log_msg_buffer &&other);
log_msg_buffer &operator=(const log_msg_buffer &other);
log_msg_buffer &operator=(log_msg_buffer &&other);
};

} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "log_msg_buffer-inl.h"
#endif

+ 120
- 0
incl/spdlog/details/mpmc_blocking_q.h View File

@@ -0,0 +1,120 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

// multi producer-multi consumer blocking queue.
// enqueue(..) - will block until room found to put the new message.
// enqueue_nowait(..) - will return immediately with false if no room left in
// the queue.
// dequeue_for(..) - will block until the queue is not empty or timeout have
// passed.

#include "spdlog/details/circular_q.h"

#include <condition_variable>
#include <mutex>

namespace spdlog {
namespace details {

template<typename T>
class mpmc_blocking_queue
{
public:
using item_type = T;
explicit mpmc_blocking_queue(size_t max_items)
: q_(max_items)
{}

#ifndef __MINGW32__
// try to enqueue and block if no room left
void enqueue(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item));
}
push_cv_.notify_one();
}

// enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
q_.push_back(std::move(item));
}
push_cv_.notify_one();
}

// try to dequeue item. if no item found. wait upto timeout and try again
// Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
{
return false;
}
popped_item = std::move(q_.front());
q_.pop_front();
}
pop_cv_.notify_one();
return true;
}

#else
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
// so release the mutex at the very end each function.

// try to enqueue and block if no room left
void enqueue(T &&item)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item));
push_cv_.notify_one();
}

// enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
q_.push_back(std::move(item));
push_cv_.notify_one();
}

// try to dequeue item. if no item found. wait upto timeout and try again
// Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
{
return false;
}
popped_item = std::move(q_.front());
q_.pop_front();
pop_cv_.notify_one();
return true;
}

#endif

size_t overrun_counter()
{
std::unique_lock<std::mutex> lock(queue_mutex_);
return q_.overrun_counter();
}

private:
std::mutex queue_mutex_;
std::condition_variable push_cv_;
std::condition_variable pop_cv_;
spdlog::details::circular_q<T> q_;
};
} // namespace details
} // namespace spdlog

+ 49
- 0
incl/spdlog/details/null_mutex.h View File

@@ -0,0 +1,49 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include <atomic>
#include <utility>
// null, no cost dummy "mutex" and dummy "atomic" int

namespace spdlog {
namespace details {
struct null_mutex
{
void lock() const {}
void unlock() const {}
bool try_lock() const
{
return true;
}
};

struct null_atomic_int
{
int value;
null_atomic_int() = default;

explicit null_atomic_int(int new_value)
: value(new_value)
{}

int load(std::memory_order = std::memory_order_relaxed) const
{
return value;
}

void store(int new_value, std::memory_order = std::memory_order_relaxed)
{
value = new_value;
}

int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
{
std::swap(new_value, value);
return new_value; // return value before the call
}
};

} // namespace details
} // namespace spdlog

+ 465
- 0
incl/spdlog/details/os-inl.h View File

@@ -0,0 +1,465 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#ifndef SPDLOG_HEADER_ONLY
#include "spdlog/details/os.h"
#endif

#include "spdlog/common.h"

#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <string>
#include <thread>
#include <array>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef _WIN32

#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support
#include <windows.h>

#ifdef __MINGW32__
#include <share.h>
#endif

#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
#include <limits>
#endif

#else // unix

#include <fcntl.h>
#include <unistd.h>

#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id

#elif defined(_AIX)
#include <pthread.h> // for pthread_getthreadid_np

#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <pthread_np.h> // for pthread_getthreadid_np

#elif defined(__NetBSD__)
#include <lwp.h> // for _lwp_self

#elif defined(__sun)
#include <thread.h> // for thr_self
#endif

#endif // unix

#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif

namespace spdlog {
namespace details {
namespace os {

SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
{

#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));

#else
return log_clock::now();
#endif
}
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
{

#ifdef _WIN32
std::tm tm;
localtime_s(&tm, &time_tt);
#else
std::tm tm;
localtime_r(&time_tt, &tm);
#endif
return tm;
}

SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
{
std::time_t now_t = time(nullptr);
return localtime(now_t);
}

SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
{

#ifdef _WIN32
std::tm tm;
gmtime_s(&tm, &time_tt);
#else
std::tm tm;
gmtime_r(&time_tt, &tm);
#endif
return tm;
}

SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
{
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}

SPDLOG_INLINE void prevent_child_fd(FILE *f)
{

#ifdef _WIN32
#if !defined(__cplusplus_winrt)
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(f)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
#endif
#else
auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
}
#endif
}

// fopen_s on non windows for writing
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{
#ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif
#else // unix
*fp = fopen((filename.c_str()), mode.c_str());
#endif

#ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr)
{
prevent_child_fd(*fp);
}
#endif
return *fp == nullptr;
}

SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wremove(filename.c_str());
#else
return std::remove(filename.c_str());
#endif
}

SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
{
return file_exists(filename) ? remove(filename) : 0;
}

SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str());
#else
return std::rename(filename1.c_str(), filename2.c_str());
#endif
}

// Return true if file exists
SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
{
#ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
auto attribs = GetFileAttributesW(filename.c_str());
#else
auto attribs = GetFileAttributesA(filename.c_str());
#endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else // common linux/unix all have the stat system call
struct stat buffer;
return (::stat(filename.c_str(), &buffer) == 0);
#endif
}

// Return file size according to open FILE* object
SPDLOG_INLINE size_t filesize(FILE *f)
{
if (f == nullptr)
{
SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
}
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f);
#if _WIN64 // 64 bits
__int64 ret = _filelengthi64(fd);
if (ret >= 0)
{
return static_cast<size_t>(ret);
}

#else // windows 32 bits
long ret = _filelength(fd);
if (ret >= 0)
{
return static_cast<size_t>(ret);
}
#endif

#else // unix
int fd = fileno(f);
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
struct stat64 st;
if (::fstat64(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size);
}
#else // unix 32 bits or cygwin
struct stat st;

if (::fstat(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size);
}
#endif
#endif
SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
}

// Return utc offset in minutes or throw spdlog_ex on failure
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
{

#ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo;
auto rv = GetTimeZoneInformation(&tzinfo);
#else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
#endif
if (rv == TIME_ZONE_ID_INVALID)
SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));

int offset = -tzinfo.Bias;
if (tm.tm_isdst)
{
offset -= tzinfo.DaylightBias;
}
else
{
offset -= tzinfo.StandardBias;
}
return offset;
#else

#if defined(sun) || defined(__sun) || defined(_AIX)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper
{
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
{
int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1);

long int days = (
// difference in day of year
localtm.tm_yday -
gmtm.tm_yday

// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))

// + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365);

long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);

return secs;
}
};

auto offset_seconds = helper::calculate_gmt_offset(tm);
#else
auto offset_seconds = tm.tm_gmtoff;
#endif

return static_cast<int>(offset_seconds / 60);
#endif
}

// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
#elif defined(__linux__)
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
#define SYS_gettid __NR_gettid
#endif
return static_cast<size_t>(syscall(SYS_gettid));
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
return static_cast<size_t>(pthread_getthreadid_np());
#elif defined(__NetBSD__)
return static_cast<size_t>(_lwp_self());
#elif defined(__OpenBSD__)
return static_cast<size_t>(getthrid());
#elif defined(__sun)
return static_cast<size_t>(thr_self());
#elif __APPLE__
uint64_t tid;
pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(tid);
#else // Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}

// Return current thread id as size_t (from thread local storage)
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
{
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}

// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
{
#if defined(_WIN32)
::Sleep(milliseconds);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
#endif
}

// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
{
memory_buf_t buf;
wstr_to_utf8buf(filename, buf);
return fmt::to_string(buf);
}
#else
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
{
return filename;
}
#endif

SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
{

#ifdef _WIN32
return static_cast<int>(::GetCurrentProcessId());
#else
return static_cast<int>(::getpid());
#endif
}

// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return true;
#else
static constexpr std::array<const char *, 14> Terms = {
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};

const char *env_p = std::getenv("TERM");
if (env_p == nullptr)
{
return false;
}

static const bool result =
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
return result;
#endif
}

// Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
{

#ifdef _WIN32
return _isatty(_fileno(file)) != 0;
#else
return isatty(fileno(file)) != 0;
#endif
}

#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
{
if (wstr.size() > static_cast<size_t>(std::numeric_limits<int>::max()))
{
SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
}

int wstr_size = static_cast<int>(wstr.size());
if (wstr_size == 0)
{
target.resize(0);
return;
}

int result_size = static_cast<int>(target.capacity());
if ((wstr_size + 1) * 2 > result_size)
{
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
}

if (result_size > 0)
{
target.resize(result_size);
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);

if (result_size > 0)
{
target.resize(result_size);
return;
}
}

SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
}
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)

} // namespace os
} // namespace details
} // namespace spdlog

+ 98
- 0
incl/spdlog/details/os.h View File

@@ -0,0 +1,98 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/common.h"
#include <ctime> // std::time_t

namespace spdlog {
namespace details {
namespace os {

spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;

std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;

std::tm localtime() SPDLOG_NOEXCEPT;

std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;

std::tm gmtime() SPDLOG_NOEXCEPT;

// eol definition
#if !defined(SPDLOG_EOL)
#ifdef _WIN32
#define SPDLOG_EOL "\r\n"
#else
#define SPDLOG_EOL "\n"
#endif
#endif

SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;

// folder separator
#ifdef _WIN32
const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif

void prevent_child_fd(FILE *f);

// fopen_s on non windows for writing
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);

// Remove filename. return 0 on success
int remove(const filename_t &filename) SPDLOG_NOEXCEPT;

// Remove file if exists. return 0 on success
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;

int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;

// Return if file exists.
bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT;

// Return file size according to open FILE* object
size_t filesize(FILE *f);

// Return utc offset in minutes or throw spdlog_ex on failure
int utc_minutes_offset(const std::tm &tm = details::os::localtime());

// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
size_t _thread_id() SPDLOG_NOEXCEPT;

// Return current thread id as size_t (from thread local storage)
size_t thread_id() SPDLOG_NOEXCEPT;

// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;

std::string filename_to_str(const filename_t &filename);

int pid() SPDLOG_NOEXCEPT;

// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
bool is_color_terminal() SPDLOG_NOEXCEPT;

// Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;

#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
#endif

} // namespace os
} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#include "os-inl.h"
#endif

+ 1317
- 0
incl/spdlog/details/pattern_formatter-inl.h
File diff suppressed because it is too large
View File


+ 99
- 0