pull/283/head
Mats Webjörn 9 years ago
commit 5de728ea5c

@ -11,6 +11,7 @@ Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/inclu
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+) * Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+)
* Mac OSX (clang 3.5+) * Mac OSX (clang 3.5+)
* Solaris (gcc 5.2.0+) * Solaris (gcc 5.2.0+)
* Android
##Features ##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).

@ -1,9 +1,9 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
all: $(binaries) all: $(binaries)
@ -17,6 +17,11 @@ spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-null-async: spdlog-null-async.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
boost-bench: boost-bench.cpp boost-bench: boost-bench.cpp

@ -0,0 +1,112 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// bench.cpp : spdlog benchmarks
//
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char* argv[])
{
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
int iters = 10;
try
{
if(argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size);
size_t total_rate = 0;
for(int i = 0; i < iters; ++i)
{
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
auto as = spdlog::create<null_sink_st>("async(null-sink)");
total_rate+= bench_as(howmany, as, threads);
spdlog::drop("async(null-sink)");
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
//return rate/sec
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
for(;;)
{
int counter = ++msg_counter;
if (counter > howmany) break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
auto per_sec = size_t(howmany / delta_d);
cout << format(per_sec) << "/sec" << endl;
return per_sec;
}

@ -0,0 +1,35 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
namespace utils
{
template<typename T>
inline std::string format(const T& value)
{
static std::locale loc("");
std::stringstream ss;
ss.imbue(loc);
ss << value;
return ss.str();
}
template<>
inline std::string format(const double & value)
{
static std::locale loc("");
std::stringstream ss;
ss.imbue(loc);
ss << std::fixed << std::setprecision(1) << value;
return ss.str();
}
}

@ -31,9 +31,8 @@ public:
const int open_tries = 5; const int open_tries = 5;
const int open_interval = 10; const int open_interval = 10;
explicit file_helper(bool force_flush) : explicit file_helper() :
_fd(nullptr), _fd(nullptr)
_force_flush(force_flush)
{} {}
file_helper(const file_helper&) = delete; file_helper(const file_helper&) = delete;
@ -90,10 +89,7 @@ public:
size_t msg_size = msg.formatted.size(); size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data(); auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size) if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
if (_force_flush)
std::fflush(_fd);
} }
size_t size() size_t size()
@ -116,8 +112,7 @@ public:
private: private:
FILE* _fd; FILE* _fd;
filename_t _filename; filename_t _filename;
bool _force_flush;
}; };
} }
} }

@ -303,3 +303,8 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
const auto flush_level = _flush_level.load(std::memory_order_relaxed); const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off); return (msg.level >= flush_level) && (msg.level != level::off);
} }
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

@ -31,6 +31,7 @@
#endif #endif
#include <sys/types.h> #include <sys/types.h>
#include <io.h>
#elif __linux__ #elif __linux__
@ -204,9 +205,9 @@ inline size_t filesize(FILE *f)
return st.st_size; return st.st_size;
#else //windows 32 bits #else //windows 32 bits
struct _stat st; long ret = _filelength(fd);
if (_fstat(fd, &st) == 0) if (ret >= 0)
return st.st_size; return static_cast<size_t>(ret);
#endif #endif
#else // unix #else // unix

@ -12,9 +12,14 @@
#include <spdlog/details/registry.h> #include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h> #include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h> #include <spdlog/sinks/stdout_sinks.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h> #include <spdlog/sinks/syslog_sink.h>
#endif
#include <spdlog/sinks/ansicolor_sink.h> #include <spdlog/sinks/ansicolor_sink.h>
#ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h> #include <spdlog/sinks/android_sink.h>
#endif
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@ -105,7 +110,7 @@ inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string&
} }
#endif #endif
#if defined(__ANDROID__) #ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag) inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{ {
return create<spdlog::sinks::android_sink>(logger_name, tag); return create<spdlog::sinks::android_sink>(logger_name, tag);

@ -71,6 +71,8 @@ public:
virtual void flush(); virtual void flush();
const std::vector<sink_ptr>& sinks() const;
protected: protected:
virtual void _sink_it(details::log_msg&); virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&); virtual void _set_pattern(const std::string&);
@ -93,5 +95,3 @@ protected:
} }
#include <spdlog/details/logger_impl.h> #include <spdlog/details/logger_impl.h>

@ -23,16 +23,13 @@ namespace spdlog
namespace sinks namespace sinks
{ {
/* /*
* Trivial file sink with single file as target * Trivial file sink with single file as target
*/ */
template<class Mutex> template<class Mutex>
class simple_file_sink : public base_sink < Mutex > class simple_file_sink : public base_sink < Mutex >
{ {
public: public:
explicit simple_file_sink(const filename_t &filename, explicit simple_file_sink(const filename_t &filename, bool truncate = false)
bool force_flush = false,
bool truncate = false) :
_file_helper(force_flush)
{ {
_file_helper.open(filename, truncate); _file_helper.open(filename, truncate);
} }
@ -54,21 +51,20 @@ typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st; typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/* /*
* Rotating file sink based on size * Rotating file sink based on size
*/ */
template<class Mutex> template<class Mutex>
class rotating_file_sink : public base_sink < Mutex > class rotating_file_sink : public base_sink < Mutex >
{ {
public: public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension, rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files, std::size_t max_size, std::size_t max_files ) :
bool force_flush = false) :
_base_filename(base_filename), _base_filename(base_filename),
_extension(extension), _extension(extension),
_max_size(max_size), _max_size(max_size),
_max_files(max_files), _max_files(max_files),
_current_size(0), _current_size(0),
_file_helper(force_flush) _file_helper()
{ {
_file_helper.open(calc_filename(_base_filename, 0, _extension)); _file_helper.open(calc_filename(_base_filename, 0, _extension));
_current_size = _file_helper.size(); //expensive. called only once _current_size = _file_helper.size(); //expensive. called only once
@ -143,11 +139,11 @@ typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st; typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/* /*
* Default generator of daily log file names. * Default generator of daily log file names.
*/ */
struct default_daily_file_name_calculator struct default_daily_file_name_calculator
{ {
//Create filename for the form basename.YYYY-MM-DD_hh-mm.extension // Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension) static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{ {
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
@ -158,11 +154,11 @@ struct default_daily_file_name_calculator
}; };
/* /*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension * Generator of daily log file names in format basename.YYYY-MM-DD.extension
*/ */
struct dateonly_daily_file_name_calculator struct dateonly_daily_file_name_calculator
{ {
//Create filename for the form basename.YYYY-MM-DD.extension // Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension) static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{ {
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
@ -173,8 +169,8 @@ struct dateonly_daily_file_name_calculator
}; };
/* /*
* Rotating file sink based on date. rotates at midnight * Rotating file sink based on date. rotates at midnight
*/ */
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator> template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink :public base_sink < Mutex > class daily_file_sink :public base_sink < Mutex >
{ {
@ -184,12 +180,10 @@ public:
const filename_t& base_filename, const filename_t& base_filename,
const filename_t& extension, const filename_t& extension,
int rotation_hour, int rotation_hour,
int rotation_minute, int rotation_minute) : _base_filename(base_filename),
bool force_flush = false) : _base_filename(base_filename),
_extension(extension), _extension(extension),
_rotation_h(rotation_hour), _rotation_h(rotation_hour),
_rotation_m(rotation_minute), _rotation_m(rotation_minute)
_file_helper(force_flush)
{ {
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");

@ -28,6 +28,7 @@ TEST_CASE("custom_error_handler", "[errors]]")
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log.txt"; std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string& msg) logger->set_error_handler([=](const std::string& msg)
{ {
throw custom_ex(); throw custom_ex();

@ -12,6 +12,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
log_msg msg; log_msg msg;
msg.formatted << std::string(howmany, '1'); msg.formatted << std::string(howmany, '1');
helper.write(msg); helper.write(msg);
helper.flush();
} }
@ -19,7 +20,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
{ {
prepare_logdir(); prepare_logdir();
file_helper helper(false); file_helper helper;
helper.open(target_filename); helper.open(target_filename);
REQUIRE(helper.filename() == target_filename); REQUIRE(helper.filename() == target_filename);
} }
@ -31,7 +32,7 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
prepare_logdir(); prepare_logdir();
size_t expected_size = 123; size_t expected_size = 123;
{ {
file_helper helper(true); file_helper helper;
helper.open(target_filename); helper.open(target_filename);
write_with_helper(helper, expected_size); write_with_helper(helper, expected_size);
REQUIRE(static_cast<size_t>(helper.size()) == expected_size); REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
@ -44,7 +45,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{ {
prepare_logdir(); prepare_logdir();
REQUIRE(!file_helper::file_exists(target_filename)); REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper(false); file_helper helper;
helper.open(target_filename); helper.open(target_filename);
REQUIRE(file_helper::file_exists(target_filename)); REQUIRE(file_helper::file_exists(target_filename));
} }
@ -52,7 +53,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]") TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
{ {
prepare_logdir(); prepare_logdir();
file_helper helper(true); file_helper helper;
helper.open(target_filename); helper.open(target_filename);
write_with_helper(helper, 12); write_with_helper(helper, 12);
REQUIRE(helper.size() == 12); REQUIRE(helper.size() == 12);
@ -64,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{ {
prepare_logdir(); prepare_logdir();
size_t expected_size = 14; size_t expected_size = 14;
file_helper helper(true); file_helper helper;
helper.open(target_filename); helper.open(target_filename);
write_with_helper(helper, expected_size); write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size); REQUIRE(helper.size() == expected_size);

@ -18,7 +18,26 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
logger->flush(); logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n")); REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
} }
TEST_CASE("rotating_file_logger1", "[rotating_logger]]") TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
@ -26,15 +45,13 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
prepare_logdir(); prepare_logdir();
std::string basename = "logs/rotating_log"; std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0); auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt"; auto filename = basename + ".txt";
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
} }
@ -61,7 +78,6 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
TEST_CASE("daily_logger", "[daily_logger]]") TEST_CASE("daily_logger", "[daily_logger]]")
{ {
prepare_logdir(); prepare_logdir();
//calculate filename (time based) //calculate filename (time based)
std::string basename = "logs/daily_log"; std::string basename = "logs/daily_log";
@ -92,10 +108,10 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
fmt::MemoryWriter w; fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true); auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush();
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
@ -124,11 +140,12 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
fmt::MemoryWriter w; fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true); auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
auto filename = w.str(); logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }

Loading…
Cancel
Save