Browse Source

More graphics work + logging

testing
= 3 years ago
parent
commit
c08726ef28
47 changed files with 9999 additions and 37 deletions
  1. 1
    0
      .gitignore
  2. 11
    0
      include/fileutils.h
  3. 6
    4
      include/graphics/drawable.h
  4. 2
    0
      include/graphics/glstate.h
  5. 5
    16
      include/graphics/shader.h
  6. 21
    0
      include/loggersetup.h
  7. 77
    0
      include/spdlog/async_logger.h
  8. 143
    0
      include/spdlog/common.h
  9. 390
    0
      include/spdlog/details/async_log_helper.h
  10. 89
    0
      include/spdlog/details/async_logger_impl.h
  11. 118
    0
      include/spdlog/details/file_helper.h
  12. 46
    0
      include/spdlog/details/log_msg.h
  13. 293
    0
      include/spdlog/details/logger_impl.h
  14. 172
    0
      include/spdlog/details/mpmc_bounded_q.h
  15. 45
    0
      include/spdlog/details/null_mutex.h
  16. 361
    0
      include/spdlog/details/os.h
  17. 636
    0
      include/spdlog/details/pattern_formatter_impl.h
  18. 185
    0
      include/spdlog/details/registry.h
  19. 182
    0
      include/spdlog/details/spdlog_impl.h
  20. 557
    0
      include/spdlog/fmt/bundled/format.cc
  21. 4455
    0
      include/spdlog/fmt/bundled/format.h
  22. 36
    0
      include/spdlog/fmt/bundled/ostream.cc
  23. 118
    0
      include/spdlog/fmt/bundled/ostream.h
  24. 642
    0
      include/spdlog/fmt/bundled/printf.h
  25. 28
    0
      include/spdlog/fmt/fmt.h
  26. 17
    0
      include/spdlog/fmt/ostr.h
  27. 45
    0
      include/spdlog/formatter.h
  28. 94
    0
      include/spdlog/logger.h
  29. 75
    0
      include/spdlog/sinks/android_sink.h
  30. 116
    0
      include/spdlog/sinks/ansicolor_sink.h
  31. 45
    0
      include/spdlog/sinks/base_sink.h
  32. 71
    0
      include/spdlog/sinks/dist_sink.h
  33. 237
    0
      include/spdlog/sinks/file_sinks.h
  34. 50
    0
      include/spdlog/sinks/msvc_sink.h
  35. 34
    0
      include/spdlog/sinks/null_sink.h
  36. 47
    0
      include/spdlog/sinks/ostream_sink.h
  37. 50
    0
      include/spdlog/sinks/sink.h
  38. 75
    0
      include/spdlog/sinks/stdout_sinks.h
  39. 81
    0
      include/spdlog/sinks/syslog_sink.h
  40. 170
    0
      include/spdlog/spdlog.h
  41. 103
    0
      include/spdlog/tweakme.h
  42. 26
    0
      src/fileutils.cpp
  43. 3
    0
      src/graphics/drawable.cpp
  44. 10
    2
      src/graphics/glstate.cpp
  45. 1
    15
      src/graphics/shader.cpp
  46. 22
    0
      src/loggersetup.cpp
  47. 8
    0
      src/main.cpp

+ 1
- 0
.gitignore View File

@@ -30,3 +30,4 @@ Makefile
cmake_install.cmake
compile_commands.json
vtk
logs/

+ 11
- 0
include/fileutils.h View File

@@ -0,0 +1,11 @@
/*
* File loading and handling functions
*/

#include <string>

namespace vtk::file {

std::string loadFileIntoString(const std::string& fName);

}

+ 6
- 4
include/graphics/drawable.h View File

@@ -2,7 +2,6 @@
* Drawable class
*/

#include "../camera.h"

#include <memory>

@@ -15,13 +14,16 @@ class RenderTask;

class Drawable {
public:
///\brief Destructor
virtual ~Drawable();

//Draw (called from rednertask)
virtual void draw(RenderTask& task);
virtual void draw(RenderTask& task) = 0;

void setShader(std::shared_ptr<Shader>);
std::shared_ptr<Shader> getShader();

protected:
std::shared_ptr<Shader> linkedShader;
std::shared_ptr<Shader> mLinkedShader;
};

}

+ 2
- 0
include/graphics/glstate.h View File

@@ -16,7 +16,9 @@ namespace vtk {

namespace vtk::gls {

// set active shader
int setShader(const GLuint& shaderID);
// enable tracking of state changes
void setTracking(const bool& flag);
void setFlag(const GLenum& flag, const bool& newState);
void restoreState();

+ 5
- 16
include/graphics/shader.h View File

@@ -1,19 +1,5 @@
/*
* =====================================================================================
*
* Filename: shader.h
*
* Description: Shader that shades
*
* Version: 1.0
* Created: 05/12/2014 10:54:44 PM
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
* Loads and manages a shader
*/

#pragma once
@@ -27,7 +13,10 @@ public:
bool activate(); //Must be called to use shader

//Load shader, type 0 = Vert, 1 = Frag, 2 = Geometry
bool loadShaderFromFile(const std::string& fileName, const unsigned& type);
bool loadShaderFiles(const std::string& vertShader,
const std::string& fragShader,
const std::string& geomShader = "");




+ 21
- 0
include/loggersetup.h View File

@@ -0,0 +1,21 @@
#pragma once

#include <string>

#include "spdlog/spdlog.h"

namespace vtk {

class LoggerSetup {
public:
LoggerSetup();

void setLogFolder(const std::string& path);
void setup();

protected:
std::string mLogFolder;

};

}

+ 77
- 0
include/spdlog/async_logger.h View File

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

#pragma once

// Very fast asynchronous logger (millions of logs per second on an average desktop)
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
// 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)
// 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing..

#include <spdlog/common.h>
#include <spdlog/logger.h>

#include <chrono>
#include <functional>
#include <string>
#include <memory>

namespace spdlog
{

namespace details
{
class async_log_helper;
}

class async_logger :public logger
{
public:
template<class It>
async_logger(const std::string& name,
const It& begin,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);

async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);

async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);

//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
void flush() override;
protected:
void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern) override;

private:
std::unique_ptr<details::async_log_helper> _async_log_helper;
};
}


#include <spdlog/details/async_logger_impl.h>

+ 143
- 0
include/spdlog/common.h View File

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

#pragma once

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

#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include <codecvt>
#include <locale>
#endif

#include <spdlog/details/null_mutex.h>

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

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


#include <spdlog/fmt/fmt.h>

namespace spdlog
{

class formatter;

namespace sinks
{
class sink;
}

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 formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic_int;
#endif

using log_err_handler = std::function<void(const std::string &err_msg)>;

//Log level enum
namespace level
{
typedef enum
{
trace = 0,
debug = 1,
info = 2,
warn = 3,
err = 4,
critical = 5,
off = 6
} level_enum;

static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" };

static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };

inline const char* to_str(spdlog::level::level_enum l)
{
return level_names[l];
}

inline const char* to_short_str(spdlog::level::level_enum l)
{
return short_level_names[l];
}
} //level


//
// Async overflow policy - block by default.
//
enum class async_overflow_policy
{
block_retry, // Block / yield / sleep until message can be enqueued
discard_log_msg // Discard the message it enqueue fails
};


//
// Log exception
//
namespace details
{
namespace os
{
std::string errno_str(int err_num);
}
}
class spdlog_ex: public std::exception
{
public:
spdlog_ex(const std::string& msg):_msg(msg)
{}
spdlog_ex(const std::string& msg, int last_errno)
{
_msg = msg + ": " + details::os::errno_str(last_errno);
}
const char* what() const SPDLOG_NOEXCEPT override
{
return _msg.c_str();
}
private:
std::string _msg;

};

//
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
//
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring;
#else
using filename_t = std::string;
#endif


} //spdlog

+ 390
- 0
include/spdlog/details/async_log_helper.h View File

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

// async log helper :
// Process logs asynchronously using a back thread.
//
// If the internal queue of log messages reaches its max size,
// then the client call will block until there is more room.
//
// If the back thread throws during logging, a spdlog::spdlog_ex exception
// will be thrown in client's thread when tries to log the next message

#pragma once

#include <spdlog/common.h>
#include <spdlog/sinks/sink.h>
#include <spdlog/details/mpmc_bounded_q.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/formatter.h>

#include <chrono>
#include <exception>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>

namespace spdlog
{
namespace details
{

class async_log_helper
{
// Async msg to move to/from the queue
// Movable only. should never be copied
enum class async_msg_type
{
log,
flush,
terminate
};
struct async_msg
{
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
std::string txt;
async_msg_type msg_type;

async_msg() = default;
~async_msg() = default;


async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type))
{}

async_msg(async_msg_type m_type) :msg_type(m_type)
{};

async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt);
msg_type = other.msg_type;
return *this;
}

// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;

// construct from log_msg
async_msg(const details::log_msg& m) :
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()),
msg_type(async_msg_type::log)
{
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
#endif
}


// copy into log_msg
void fill_log_msg(log_msg &msg)
{
msg.logger_name = &logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
}
};

public:

using item_type = async_msg;
using q_type = details::mpmc_bounded_queue<item_type>;

using clock = std::chrono::steady_clock;


async_log_helper(formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);

void log(const details::log_msg& msg);

// stop logging and join the back thread
~async_log_helper();

void set_formatter(formatter_ptr);

void flush(bool wait_for_q);


private:
formatter_ptr _formatter;
std::vector<std::shared_ptr<sinks::sink>> _sinks;

// queue of messages to log
q_type _q;

log_err_handler _err_handler;

bool _flush_requested;

bool _terminate_requested;


// overflow policy
const async_overflow_policy _overflow_policy;

// worker thread warmup callback - one can set thread priority, affinity, etc
const std::function<void()> _worker_warmup_cb;

// auto periodic sink flush parameter
const std::chrono::milliseconds _flush_interval_ms;

// worker thread teardown callback
const std::function<void()> _worker_teardown_cb;

// worker thread
std::thread _worker_thread;

void push_msg(async_msg&& new_msg);

// worker thread main loop
void worker_loop();

// pop next message from the queue and process it. will set the last_pop to the pop time
// return false if termination of the queue is required
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);

void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);

// sleep,yield or return immediatly using the time passed since last message as a hint
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);

// wait until the queue is empty
void wait_empty_q();

};
}
}

///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_err_handler(err_handler),
_flush_requested(false),
_terminate_requested(false),
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms),
_worker_teardown_cb(worker_teardown_cb),
_worker_thread(&async_log_helper::worker_loop, this)
{}

// Send to the worker thread termination message(level=off)
// and wait for it to finish gracefully
inline spdlog::details::async_log_helper::~async_log_helper()
{
try
{
push_msg(async_msg(async_msg_type::terminate));
_worker_thread.join();
}
catch (...) // don't crash in destructor
{}
}


//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));


}

inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
{
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{
auto last_op_time = details::os::now();
auto now = last_op_time;
do
{
now = details::os::now();
sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
}

}

// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
}

inline void spdlog::details::async_log_helper::worker_loop()
{
try
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}

// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{

async_msg incoming_async_msg;


if (_q.dequeue(incoming_async_msg))
{
last_pop = details::os::now();
switch (incoming_async_msg.msg_type)
{
case async_msg_type::flush:
_flush_requested = true;
break;

case async_msg_type::terminate:
_flush_requested = true;
_terminate_requested = true;
break;

default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if(s->should_log( incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
}
return true;
}

// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
{
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
}

// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
if (should_flush)
{
for (auto &s : _sinks)
s->flush();
now = last_flush = details::os::now();
_flush_requested = false;
}
}

inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}


// spin, yield or sleep. use the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{
using namespace std::this_thread;
using std::chrono::milliseconds;
using std::chrono::microseconds;

auto time_since_op = now - last_op_time;

// spin upto 50 micros
if (time_since_op <= microseconds(50))
return;

// yield upto 150 micros
if (time_since_op <= microseconds(100))
return yield();


// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20));

// sleep for 200 ms
return sleep_for(milliseconds(200));
}

// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (_q.approx_size() > 0)
{
sleep_or_yield(details::os::now(), last_op);
}

}









+ 89
- 0
include/spdlog/details/async_logger_impl.h View File

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

#pragma once

// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread

#include <spdlog/details/async_log_helper.h>
#include <spdlog/async_logger.h>

#include <string>
#include <functional>
#include <chrono>
#include <memory>

template<class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& begin,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
logger(logger_name, begin, end),
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{
}

inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}

inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name,
{
single_sink
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}


inline void spdlog::async_logger::flush()
{
_async_log_helper->flush(true);
}

inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter);
}

inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
_async_log_helper->set_formatter(_formatter);
}


inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
try
{
_async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}

+ 118
- 0
include/spdlog/details/file_helper.h View File

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

#pragma once

// Helper class for file sink
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Can be set to auto flush on every line
// Throw spdlog_ex exception on errors

#include <spdlog/details/os.h>
#include <spdlog/details/log_msg.h>

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

namespace spdlog
{
namespace details
{

class file_helper
{

public:
const int open_tries = 5;
const int open_interval = 10;

explicit file_helper() :
_fd(nullptr)
{}

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

~file_helper()
{
close();
}


void open(const filename_t& fname, bool truncate = false)
{

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;

std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
}

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

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

}

void flush()
{
std::fflush(_fd);
}

void close()
{
if (_fd)
{
std::fclose(_fd);
_fd = nullptr;
}
}

void write(const log_msg& msg)
{

size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
}

size_t size()
{
if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
return os::filesize(_fd);
}

const filename_t& filename() const
{
return _filename;
}

static bool file_exists(const filename_t& name)
{

return os::file_exists(name);
}

private:
FILE* _fd;
filename_t _filename;
};
}
}

+ 46
- 0
include/spdlog/details/log_msg.h View File

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

#pragma once

#include <spdlog/common.h>
#include <spdlog/details/os.h>


#include <string>
#include <utility>

namespace spdlog
{
namespace details
{
struct log_msg
{
log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl)
{
#ifndef SPDLOG_NO_DATETIME
time = os::now();
#endif

#ifndef SPDLOG_NO_THREAD_ID
thread_id = os::thread_id();
#endif
}

log_msg(const log_msg& other) = delete;
log_msg& operator=(log_msg&& other) = delete;
log_msg(log_msg&& other) = delete;


const std::string *logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
};
}
}

+ 293
- 0
include/spdlog/details/logger_impl.h View File

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

#pragma once

#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_sinks.h>

#include <memory>
#include <string>


// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name),
_sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+"))
{
_level = level::info;
_flush_level = level::off;
_last_err_time = 0;
_err_handler = [this](const std::string &msg)
{
this->_default_err_handler(msg);
};
}

// ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
logger(logger_name, sinks_list.begin(), sinks_list.end())
{}


// ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
logger(logger_name,
{
single_sink
})
{}


inline spdlog::logger::~logger() = default;


inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{
_set_formatter(msg_formatter);
}

inline void spdlog::logger::set_pattern(const std::string& pattern)
{
_set_pattern(pattern);
}


template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{
if (!should_log(lvl)) return;

try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw.write(fmt, args...);
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}

template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{
if (!should_log(lvl)) return;
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}

}

template<typename T>
inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{
if (!should_log(lvl)) return;
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}


template <typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... args)
{
log(level::trace, fmt, args...);
}

template <typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... args)
{
log(level::debug, fmt, args...);
}

template <typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... args)
{
log(level::info, fmt, args...);
}


template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
{
log(level::warn, fmt, args...);
}

template <typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... args)
{
log(level::err, fmt, args...);
}

template <typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... args)
{
log(level::critical, fmt, args...);
}


template<typename T>
inline void spdlog::logger::trace(const T& msg)
{
log(level::trace, msg);
}

template<typename T>
inline void spdlog::logger::debug(const T& msg)
{
log(level::debug, msg);
}


template<typename T>
inline void spdlog::logger::info(const T& msg)
{
log(level::info, msg);
}


template<typename T>
inline void spdlog::logger::warn(const T& msg)
{
log(level::warn, msg);
}

template<typename T>
inline void spdlog::logger::error(const T& msg)
{
log(level::err, msg);
}

template<typename T>
inline void spdlog::logger::critical(const T& msg)
{
log(level::critical, msg);
}




//
// name and level
//
inline const std::string& spdlog::logger::name() const
{
return _name;
}

inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
{
_level.store(log_level);
}

inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}

inline spdlog::log_err_handler spdlog::logger::error_handler()
{
return _err_handler;
}


inline void spdlog::logger::flush_on(level::level_enum log_level)
{
_flush_level.store(log_level);
}

inline spdlog::level::level_enum spdlog::logger::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}

inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
{
return msg_level >= _level.load(std::memory_order_relaxed);
}

//
// protected virtual called at end of each user log call (if enabled) by the line_logger
//
inline void spdlog::logger::_sink_it(details::log_msg& msg)
{
_formatter->format(msg);
for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
{
sink->log(msg);
}
}

if(_should_flush_on(msg))
flush();
}

inline void spdlog::logger::_set_pattern(const std::string& pattern)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
}
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}

inline void spdlog::logger::flush()
{
for (auto& sink : _sinks)
sink->flush();
}

inline void spdlog::logger::_default_err_handler(const std::string &msg)
{
auto now = time(nullptr);
if (now - _last_err_time < 60)
return;
auto tm_time = details::os::localtime(now);
char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg;
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
sinks::stderr_sink_mt::instance()->log(err_msg);
_last_err_time = now;
}

inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
{
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
}

+ 172
- 0
include/spdlog/details/mpmc_bounded_q.h View File

@@ -0,0 +1,172 @@
/*
A modified version of Bounded MPMC queue by Dmitry Vyukov.

Original code from:
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue

licensed by Dmitry Vyukov under the terms below:

Simplified BSD license

Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the authors and
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
*/

/*
The code in its current form adds the license below:

Copyright(c) 2015 Gabi Melman.
Distributed under the MIT License (http://opensource.org/licenses/MIT)

*/

#pragma once

#include <spdlog/common.h>

#include <atomic>
#include <utility>

namespace spdlog
{
namespace details
{

template<typename T>
class mpmc_bounded_queue
{
public:

using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");

for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed);
}

~mpmc_bounded_queue()
{
delete [] buffer_;
}


bool enqueue(T&& data)
{
cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq = cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
{
return false;
}
else
{
pos = enqueue_pos_.load(std::memory_order_relaxed);
}
}
cell->data_ = std::move(data);
cell->sequence_.store(pos + 1, std::memory_order_release);
return true;
}

bool dequeue(T& data)
{
cell_t* cell;
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
return false;
else
pos = dequeue_pos_.load(std::memory_order_relaxed);
}
data = std::move(cell->data_);
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
return true;
}

size_t approx_size()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
}

private:
struct cell_t
{
std::atomic<size_t> sequence_;
T data_;
};

size_t const max_size_;

static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];

cacheline_pad_t pad0_;
cell_t* const buffer_;
size_t const buffer_mask_;
cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_;
std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;

mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator= (mpmc_bounded_queue const&) = delete;
};

} // ns details
} // ns spdlog

+ 45
- 0
include/spdlog/details/null_mutex.h View File

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

#pragma once

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

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

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

null_atomic_int(int val):value(val)
{}

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

void store(int val)
{
value = val;
}
};

}
}

+ 361
- 0
include/spdlog/details/os.h View File

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

#include <spdlog/common.h>

#include <cstdio>
#include <ctime>
#include <functional>
#include <string>
#include <stdio.h>
#include <string.h>
#include <sys/stat.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 <windows.h>

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

#include <sys/types.h>
#include <io.h>

#elif __linux__

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

#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id

#else
#include <thread>

#endif

namespace spdlog
{
namespace details
{
namespace os
{

inline spdlog::log_clock::time_point now()
{

#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

}
inline std::tm localtime(const std::time_t &time_tt)
{

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

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


inline std::tm gmtime(const std::time_t &time_tt)
{

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

inline std::tm gmtime()
{
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
inline bool operator==(const std::tm& tm1, const std::tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec &&
tm1.tm_min == tm2.tm_min &&
tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst);
}

inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
{
return !(tm1 == tm2);
}

// 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* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;



//fopen_s on non windows for writing
inline int 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_DENYWR);
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
return *fp == nullptr;
#else
*fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif
}

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

inline int rename(const filename_t& filename1, const filename_t& filename2)
{
#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 if file exists
inline bool file_exists(const filename_t& filename)
{
#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
inline size_t filesize(FILE *f)
{
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
return st.st_size;

#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, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st;
if (fstat64(fd, &st) == 0)
return st.st_size;
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return st.st_size;
#endif
#endif
throw spdlog_ex("Failed getting file size from fd", errno);
}




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

#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)
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)
// '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;
}
};

long int offset_seconds = helper::calculate_gmt_offset(tm);
#else
long int 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(espcially under VS 2013)
inline size_t thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
#elif __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 __FreeBSD__
long tid;
thr_self(&tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (OSX and other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif


}


// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s
inline std::string filename_to_str(const filename_t& filename)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename);
}
#else
#define SPDLOG_FILENAME_T(s) s
inline std::string filename_to_str(const filename_t& filename)
{
return filename;
}
#endif


// Return errno string (thread safe)
inline std::string errno_str(int err_num)
{
char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);

#ifdef _WIN32
if(strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf);
else
return "Unkown error";

#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version

if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
else
return "Unkown error";

#else // gnu version (might not use the given buf, so its retval pointer must be used)
return std::string(strerror_r(err_num, buf, buf_size));
#endif
}

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

+ 636
- 0
include/spdlog/details/pattern_formatter_impl.h View File

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

#pragma once

#include <spdlog/formatter.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/fmt/fmt.h>

#include <chrono>
#include <ctime>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <vector>

namespace spdlog
{
namespace details
{
class flag_formatter
{
public:
virtual ~flag_formatter()
{}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
};

///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
namespace
{
class name_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << *msg.logger_name;
}
};
}

// log level appender
class level_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_str(msg.level);
}
};

// short log level appender
class short_level_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_short_str(msg.level);
}
};

///////////////////////////////////////////////////////////////////////
// Date time pattern appenders
///////////////////////////////////////////////////////////////////////

static const char* ampm(const tm& t)
{
return t.tm_hour >= 12 ? "PM" : "AM";
}

static int to12h(const tm& t)
{
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
}

//Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
class a_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday];
}
};

//Full weekday name
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
class A_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_days[tm_time.tm_wday];
}
};

//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << months[tm_time.tm_mon];
}
};

//Full month name
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
class B_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_months[tm_time.tm_mon];
}
};


//write 2 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}

//write 3 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
return w;
}


//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
}
};


// year - 2 digit
class C_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
}
};



// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
}
};


// year - 4 digit
class Y_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << tm_time.tm_year + 1900;
}
};

// month 1-12
class m_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
}
};

// day of month 1-31
class d_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
}
};

// hours in 24 format 0-23
class H_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
}
};

// hours in 12 format 1-12
class I_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
}
};

// minutes 0-59
class M_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
}
};

// seconds 0-59
class S_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
}
};

// milliseconds
class e_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
}
};

// microseconds
class f_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
}
};

// nanoseconds
class F_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
}
};

// AM/PM
class p_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << ampm(tm_time);
}
};


// 12 hour clock 02:55:02 pm
class r_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
}
};

// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
}
};

// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter:public flag_formatter