Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	include/spdlog/details/format.cc
#	include/spdlog/sinks/stdout_sinks.h
pull/51/head
Francois Coulombe 10 years ago
commit c8846db7cc

@ -0,0 +1,108 @@
# Adapted from various sources, including:
# - Louis Dionne's Hana: https://github.com/ldionne/hana
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
language: cpp
# Test matrix:
# - Build matrix per compiler: C++11/C++14 + Debug/Release
# - Optionally: AddressSanitizer (ASAN)
# - Valgrind: all release builds are also tested with valgrind
# - clang 3.4, 3.5, 3.6, trunk
# - Note: 3.4 and trunk are tested with/without ASAN,
# the rest is only tested with ASAN=On.
# - gcc 4.9, 5.0
#
matrix:
include:
# Test clang-3.5: C++11/C++14, Buidd=Debug/Release, ASAN=On/Off
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 ASAN=On LIBCXX=On
os: linux
addons: &clang35
apt:
packages:
- clang-3.5
- valgrind
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=On
os: linux
addons: *clang35
# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
os: linux
addons: &gcc48
apt:
packages:
- g++-4.8
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
os: linux
addons: *gcc48
# Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
- env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
os: linux
addons: &gcc49
apt:
packages:
- g++-4.9
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
os: linux
addons: *gcc49
# Install dependencies
before_install:
- export CHECKOUT_PATH=`pwd`;
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
- if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
- which $CXX
- which $CC
- which valgrind
- if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
install:
- cd $CHECKOUT_PATH
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
# It is fixed in valgrind 3.10 so this won't be necessary if someone
# replaces the current valgrind (3.7) with valgrind-3.10
- sed -i 's/march=native/msse4.2/' example/Makefile
- if [ ! -d build ]; then mkdir build; fi
- export CXX_FLAGS=""
- export CXX_LINKER_FLAGS=""
- if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
- if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
- if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
- if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
- if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
- CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
# Build examples
- cd example
- if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
- if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
script:
- ./"${BIN}"
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
notifications:
email: false

@ -1,25 +1,23 @@
# spdlog
Very fast, header only, C++ logging library.
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)
## Install
Just copy the files to your build tree and use a C++11 compiler
## Tested on:
* gcc 4.8.1 and above
* clang 3.5
* visual studio 2013
* mingw with g++ 4.9.x
## Platforms
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, mingw with g++ 4.9.1+)
* Mac OSX (clang 3.5+)
##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only.
* No dependencies - just copy and use.
* Cross platform - Linux / Windows on 32/64 bits.
* **new!** Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* ostream call style is supported too.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
@ -33,7 +31,7 @@ Just copy the files to your build tree and use a C++11 compiler
## Benchmarks
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
#### Synchronous mode
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
@ -45,7 +43,7 @@ Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of
|100| 15.008 |1.139s |4.512s |0.497s|
#### Asynchronous mode
#### Asynchronous mode
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
@ -72,7 +70,7 @@ int main(int, char* [])
console->info("Welcome to spdlog!") ;
console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1;
//Formatting examples
console->info("Easy padding in numbers like {:08d}", 12);
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@ -82,7 +80,7 @@ int main(int, char* [])
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
//
// Runtime log levels
//
@ -90,7 +88,7 @@ int main(int, char* [])
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("Now it should..");
//
// Create a file rotating logger with 5mb size max and 3 rotated files
//
@ -102,8 +100,8 @@ int main(int, char* [])
// Create a daily logger - a new file is created every day on 2:30am
//
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
//
//
// Customize msg format for all messages
//
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
@ -117,7 +115,7 @@ int main(int, char* [])
//
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
//
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
@ -126,14 +124,14 @@ int main(int, char* [])
spdlog::set_async_mode(q_size);
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
async_file->info() << "This is async log.." << "Should be very fast!";
//
//
// syslog example. linux only..
//
#ifdef __linux__
std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
#endif
}
catch (const spd::spdlog_ex& ex)
@ -153,5 +151,7 @@ void custom_class_example()
spdlog::get("console")->info("custom class with operator<<: {}..", c);
spdlog::get("console")->info() << "custom class with operator<<: " << c << "..";
}
```
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.

@ -1,235 +0,0 @@
----------------------------------------------------------
Single threaded benchmarks.. (1 thread, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench ****************
real 0m4.382s
user 0m4.213s
sys 0m0.048s
real 0m4.159s
user 0m4.120s
sys 0m0.040s
real 0m4.169s
user 0m4.117s
sys 0m0.052s
**************** glog-bench ****************
real 0m1.082s
user 0m0.944s
sys 0m0.136s
real 0m1.079s
user 0m0.977s
sys 0m0.101s
real 0m1.066s
user 0m0.951s
sys 0m0.114s
**************** easylogging-bench ****************
real 0m0.975s
user 0m0.963s
sys 0m0.012s
real 0m0.986s
user 0m0.954s
sys 0m0.033s
real 0m0.963s
user 0m0.919s
sys 0m0.044s
**************** spdlog-bench ****************
real 0m0.302s
user 0m0.285s
sys 0m0.016s
real 0m0.311s
user 0m0.287s
sys 0m0.025s
real 0m0.308s
user 0m0.276s
sys 0m0.032s
----------------------------------------------------------
Multi threaded benchmarks.. (10 threads, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench-mt ****************
real 0m16.293s
user 0m38.723s
sys 0m8.469s
real 0m16.029s
user 0m39.186s
sys 0m8.413s
real 0m16.257s
user 0m38.322s
sys 0m7.880s
**************** glog-bench-mt ****************
real 0m4.455s
user 0m12.871s
sys 0m13.508s
real 0m5.039s
user 0m14.239s
sys 0m15.900s
real 0m3.032s
user 0m8.654s
sys 0m9.473s
**************** easylogging-bench-mt ****************
real 0m4.076s
user 0m4.350s
sys 0m2.861s
real 0m2.857s
user 0m3.270s
sys 0m1.744s
real 0m4.588s
user 0m5.085s
sys 0m3.058s
**************** spdlog-bench-mt ****************
real 0m2.374s
user 0m4.369s
sys 0m10.426s
real 0m0.968s
user 0m1.804s
sys 0m4.186s
real 0m1.527s
user 0m3.132s
sys 0m6.427s
----------------------------------------------------------
Multi threaded benchmarks.. (100 threads, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench-mt ****************
real 0m15.623s
user 0m39.283s
sys 0m8.428s
real 0m15.008s
user 0m36.851s
sys 0m7.956s
real 0m15.478s
user 0m38.873s
sys 0m8.368s
**************** glog-bench-mt ****************
real 0m1.139s
user 0m3.003s
sys 0m5.214s
real 0m1.167s
user 0m3.004s
sys 0m5.431s
real 0m1.159s
user 0m2.909s
sys 0m5.456s
**************** easylogging-bench-mt ****************
real 0m4.510s
user 0m4.565s
sys 0m3.510s
real 0m8.841s
user 0m8.363s
sys 0m7.057s
real 0m5.638s
user 0m5.531s
sys 0m4.168s
**************** spdlog-bench-mt ****************
real 0m0.497s
user 0m0.951s
sys 0m2.743s
real 0m0.502s
user 0m0.940s
sys 0m2.816s
real 0m0.504s
user 0m0.911s
sys 0m2.860s
---------------------------------------------------------------
Async, single threaded benchmark.. (1 thread, 1,000,000 lines)
---------------------------------------------------------------
**************** spdlog-async ****************
Total: 1000000
Threads: 1
Delta = 0.216366 seconds
Rate = 4.62179e+06/sec
Total: 1000000
Threads: 1
Delta = 0.215076 seconds
Rate = 4.64953e+06/sec
Total: 1000000
Threads: 1
Delta = 0.210712 seconds
Rate = 4.74581e+06/sec
**************** g2log-async ****************
Total: 1000000
Threads: 1
Delta = 1.85039 seconds
Rate = 540428/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214929.log
Total: 1000000
Threads: 1
Delta = 1.85434 seconds
Rate = 539274/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214935.log
Total: 1000000
Threads: 1
Delta = 1.86829 seconds
Rate = 535249/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214941.log
---------------------------------------------------------------
Async, multi threaded benchmark.. (10 threads, 1,000,000 lines)
---------------------------------------------------------------
**************** spdlog-async ****************
Total: 1000000
Threads: 10
Delta = 0.175684 seconds
Rate = 5.69204e+06/sec
Total: 1000000
Threads: 10
Delta = 0.173104 seconds
Rate = 5.77688e+06/sec
Total: 1000000
Threads: 10
Delta = 0.173881 seconds
Rate = 5.75106e+06/sec
**************** g2log-async ****************
Total: 1000000
Threads: 10
Delta = 0.945252 seconds
Rate = 1.05792e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214958.log
Total: 1000000
Threads: 10
Delta = 0.950362 seconds
Rate = 1.05223e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-215004.log
Total: 1000000
Threads: 10
Delta = 0.943302 seconds
Rate = 1.06011e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-215011.log

@ -1,32 +1,29 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3 -flto
CXX_DEBUG_FLAGS= -g
CXXFLAGS =
CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O3 -march=native
CXX_DEBUG_FLAGS= -g
all: example bench
debug: example-debug bench-debug
example: example.cpp
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
$(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug
rm -f *.o logs/*.txt example example-debug bench bench-debug
rebuild: clean all
rebuild-debug: clean debug

@ -50,7 +50,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
int main(int argc, char* argv[])
{
int howmany = 1048576;
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
bool auto_flush = false;
int file_size = 30 * 1024 * 1024;
@ -63,6 +64,8 @@ int main(int argc, char* argv[])
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n";
@ -92,7 +95,7 @@ int main(int argc, char* argv[])
cout << "*******************************************************************************\n";
spdlog::set_async_mode(howmany);
spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i)
{

@ -93,7 +93,7 @@ int main(int, char*[])
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
async_file->info() << "This is async log.." << "Should be very fast!";
spdlog::drop_all(); //Close all loggers
//
// syslog example. linux only..
//
@ -112,7 +112,8 @@ int main(int, char*[])
// Example of user defined class with operator<<
class some_class {};
std::ostream& operator<<(std::ostream& os, const some_class&) {
std::ostream& operator<<(std::ostream& os, const some_class&)
{
return os << "some_class";
}

@ -58,19 +58,22 @@ public:
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::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
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::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
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::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
protected:

@ -36,13 +36,6 @@
#define SPDLOG_NOEXCEPT throw()
#endif
// under linux, you can use the much faster CLOCK_REALTIME_COARSE clock.
// this clock is less accurate - can be off by few millis - depending on the kernel HZ
// uncomment to use it instead of the regular (and slower) clock
//#ifdef __linux__
//#define SPDLOG_CLOCK_COARSE
//#endif
namespace spdlog
{
@ -56,25 +49,26 @@ class sink;
// Common types across the lib
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 sink_ptr = std::shared_ptr < sinks::sink >;
using sinks_init_list = std::initializer_list < sink_ptr >;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
//Log level enum
namespace level
{
typedef enum
{
trace = 0,
debug = 1,
info = 2,
notice = 3,
warn = 4,
err = 5,
trace = 0,
debug = 1,
info = 2,
notice = 3,
warn = 4,
err = 5,
critical = 6,
alert = 7,
emerg = 8,
off = 9
alert = 7,
emerg = 8,
off = 9
} level_enum;
static const char* level_names[] { "trace", "debug", "info", "notice", "warning", "error", "critical", "alert", "emerg", "off"};
@ -119,4 +113,4 @@ private:
};
} //spdlog
} //spdlog

@ -43,6 +43,7 @@
#include "./mpmc_bounded_q.h"
#include "./log_msg.h"
#include "./format.h"
#include "os.h"
namespace spdlog
@ -59,16 +60,17 @@ class async_log_helper
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
std::string txt;
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))
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))
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
@ -76,6 +78,7 @@ class async_log_helper
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);
return *this;
}
@ -88,6 +91,7 @@ class async_log_helper
logger_name(m.logger_name),
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size())
{}
@ -99,6 +103,7 @@ class async_log_helper
msg.logger_name = logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
}
};
@ -115,7 +120,8 @@ public:
const std::vector<sink_ptr>& 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::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
void log(const details::log_msg& msg);
@ -141,6 +147,9 @@ private:
// 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
std::thread _worker_thread;
@ -152,10 +161,14 @@ private:
// pop next message from the queue and process it
// return true if a message was available (queue was not empty), will set the last_pop to the pop time
bool process_next_msg(clock::time_point& last_pop);
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 clock::time_point& last_op_time);
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
};
}
@ -164,12 +177,13 @@ private:
///////////////////////////////////////////////////////////////////////////////
// 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, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb):
inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector<sink_ptr>& 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):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms),
_worker_thread(&async_log_helper::worker_loop, this)
{}
@ -195,10 +209,12 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
async_msg new_msg(msg);
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{
auto last_op_time = clock::now();
auto last_op_time = details::os::now();
auto now = last_op_time;
do
{
sleep_or_yield(last_op_time);
now = details::os::now();
sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
}
@ -210,8 +226,9 @@ inline void spdlog::details::async_log_helper::worker_loop()
try
{
if (_worker_warmup_cb) _worker_warmup_cb();
clock::time_point last_pop = clock::now();
while(process_next_msg(last_pop));
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
}
catch (const std::exception& ex)
{
@ -225,7 +242,7 @@ inline void spdlog::details::async_log_helper::worker_loop()
// process next message in the queue
// return true if this thread should still be active (no msg with level::off was received)
inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_point& last_pop)
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;
@ -233,7 +250,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_poin
if (_q.dequeue(incoming_async_msg))
{
last_pop = clock::now();
last_pop = details::os::now();
if(incoming_async_msg.level == level::off)
return false;
@ -245,11 +262,22 @@ inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_poin
}
else //empty queue
{
sleep_or_yield(last_pop);
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
}
return true;
}
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{
if (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms)
{
for (auto &s : _sinks)
s->flush();
now = last_flush = details::os::now();
}
}
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
@ -257,12 +285,12 @@ inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_f
// sleep,yield or return immediatly using the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time)
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 std::chrono::milliseconds;
using namespace std::this_thread;
auto time_since_op = clock::now() - last_op_time;
auto time_since_op = now - last_op_time;
// spin upto 1 ms
if (time_since_op <= milliseconds(1))

@ -39,9 +39,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb) :
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) :
logger(logger_name, begin, end),
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb))
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms))
{
}
@ -49,15 +50,17 @@ 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) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb) {}
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
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) :
async_logger(logger_name, { single_sink }, queue_size, overflow_policy, worker_warmup_cb) {}
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) :
async_logger(logger_name, { single_sink }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)

@ -48,7 +48,7 @@ public:
const int open_tries = 5;
const int open_interval = 10;
explicit file_helper(bool force_flush):
explicit file_helper(bool force_flush) :
_fd(nullptr),
_force_flush(force_flush)
{}
@ -62,7 +62,7 @@ public:
}
void open(const std::string& fname, bool truncate=false)
void open(const std::string& fname, bool truncate = false)
{
close();
@ -70,7 +70,7 @@ public:
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
if(!os::fopen_s(&_fd, fname, mode))
if (!os::fopen_s(&_fd, fname, mode))
return;
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
@ -81,12 +81,16 @@ public:
void reopen(bool truncate)
{
if(_filename.empty())
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)
@ -101,14 +105,37 @@ public:
size_t size = msg.formatted.size();
auto data = msg.formatted.data();
if(std::fwrite(data, 1, size, _fd) != size)
if (std::fwrite(data, 1, size, _fd) != size)
throw spdlog_ex("Failed writing to file " + _filename);
if(_force_flush)
if (_force_flush)
std::fflush(_fd);
}
long size()
{
if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + _filename);
auto pos = ftell(_fd);
if (fseek(_fd, 0, SEEK_END) != 0)
throw spdlog_ex("fseek failed on file " + _filename);
auto size = ftell(_fd);
if(fseek(_fd, pos, SEEK_SET) !=0)
throw spdlog_ex("fseek failed on file " + _filename);
if (size == -1)
throw spdlog_ex("ftell failed on file " + _filename);
return size;
}
const std::string& filename() const
{
return _filename;
@ -128,6 +155,8 @@ public:
}
}
private:
FILE* _fd;
std::string _filename;
@ -137,4 +166,3 @@ private:
};
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -27,7 +27,6 @@
#include "../common.h"
#include "../logger.h"
// Line logger class - aggregates operator<< calls to fast ostream
// and logs upon destruction
@ -63,8 +62,16 @@ public:
{
if (_enabled)
{
#ifndef SPDLOG_NO_NAME
_log_msg.logger_name = _callback_logger->name();
#endif
#ifndef SPDLOG_NO_DATETIME
_log_msg.time = os::now();
#endif
#ifndef SPDLOG_NO_THREAD_ID
_log_msg.thread_id = os::thread_id();
#endif
_callback_logger->_log_msg(_log_msg);
}
}
@ -199,6 +206,11 @@ public:
_enabled = false;
}
bool is_enabled() const
{
return _enabled;
}
private:
logger* _callback_logger;

@ -24,6 +24,7 @@
#pragma once
#include <thread>
#include "../common.h"
#include "./format.h"
@ -37,7 +38,6 @@ struct log_msg
log_msg(level::level_enum l):
logger_name(),
level(l),
time(),
raw(),
formatted() {}
@ -45,7 +45,8 @@ struct log_msg
log_msg(const log_msg& other) :
logger_name(other.logger_name),
level(other.level),
time(other.time)
time(other.time),
thread_id(other.thread_id)
{
if (other.raw.size())
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size());
@ -57,6 +58,7 @@ struct log_msg
logger_name(std::move(other.logger_name)),
level(other.level),
time(std::move(other.time)),
thread_id(other.thread_id),
raw(std::move(other.raw)),
formatted(std::move(other.formatted))
{
@ -71,6 +73,7 @@ struct log_msg
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
raw = std::move(other.raw);
formatted = std::move(other.formatted);
other.clear();
@ -87,6 +90,7 @@ struct log_msg
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
};

@ -50,7 +50,9 @@ inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list si
// ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink) :
logger(logger_name, { single_sink }) {}
logger(logger_name, {
single_sink
}) {}
inline spdlog::logger::~logger() = default;
@ -312,4 +314,7 @@ inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
_formatter = msg_formatter;
}
inline void spdlog::logger::flush() {
for (auto& sink : _sinks)
sink->flush();
}

@ -32,6 +32,16 @@
# define WIN32_LEAN_AND_MEAN
# endif
# include <Windows.h>
#ifdef __MINGW32__
#include <share.h>
#endif
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <unistd.h>
#else
#include <thread>
#endif
#include "../common.h"
@ -46,7 +56,7 @@ namespace os
inline spdlog::log_clock::time_point now()
{
#ifdef SPDLOG_CLOCK_COARSE
#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>(
@ -166,10 +176,22 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
#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__
return static_cast<size_t>(syscall(SYS_gettid));
#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
}
} //os
} //details
} //spdlog

@ -265,6 +265,17 @@ class f_formatter :public flag_formatter
}
};
// 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
{
@ -354,7 +365,7 @@ class t_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << std::hash<std::thread::id>()(std::this_thread::get_id());
msg.formatted << msg.thread_id;
}
};
@ -405,6 +416,7 @@ class full_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
@ -421,6 +433,7 @@ class full_formatter :public flag_formatter
level::to_str(msg.level),
msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
@ -430,7 +443,16 @@ class full_formatter :public flag_formatter
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
msg.formatted << '[' << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
//no datetime needed
#else
(void)tm_time;
#endif
#ifndef SPDLOG_NO_NAME
msg.formatted << '[' << msg.logger_name << "] ";
#endif
msg.formatted << '[' << level::to_str(msg.level) << "] ";
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
@ -478,7 +500,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
@ -564,6 +586,9 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
case('f') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
break;
case('F') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('p') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
@ -610,8 +635,8 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg)
//write eol
msg.formatted << details::os::eol();
}
catch(const details::fmt::FormatError& e)
catch(const fmt::FormatError& e)
{
throw spdlog_ex(details::fmt::format("formatting error while processing format string: {}", e.what()));
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
}
}

@ -33,6 +33,7 @@
#include <unordered_map>
#include <functional>
#include "./null_mutex.h"
#include "../logger.h"
#include "../async_logger.h"
#include "../common.h"
@ -41,13 +42,20 @@ namespace spdlog
{
namespace details
{
class registry
template <class Mutex> class registry_t
{
public:
void register_logger(std::shared_ptr<logger> logger)
{
std::lock_guard<Mutex> lock(_mutex);
register_logger_impl(logger);
}
std::shared_ptr<logger> get(const std::string& logger_name)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
auto found = _loggers.find(logger_name);
return found == _loggers.end() ? nullptr : found->second;
}
@ -55,33 +63,34 @@ public:
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
std::lock_guard<std::mutex> lock(_mutex);
//If already exists, just return it
auto found = _loggers.find(logger_name);
if (found != _loggers.end())
return found->second;
std::shared_ptr<logger> new_logger;
std::lock_guard<Mutex> lock(_mutex);
if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb);
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
if (_formatter)
new_logger->set_formatter(_formatter);
new_logger->set_level(_level);
_loggers[logger_name] = new_logger;
register_logger_impl(new_logger);
return new_logger;
}
void drop(const std::string& logger_name)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_loggers.erase(logger_name);
}
void drop_all()
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_loggers.clear();
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
@ -97,56 +106,62 @@ public:
void formatter(formatter_ptr f)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_formatter = f;
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_pattern(const std::string& pattern)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_level(level::level_enum log_level)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
for (auto& l : _loggers)
l.second->set_level(log_level);
_level = log_level;
}
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb)
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms)
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_async_mode = true;
_async_q_size = q_size;
_overflow_policy = overflow_policy;
_worker_warmup_cb = worker_warmup_cb;
_flush_interval_ms = flush_interval_ms;
}
void set_sync_mode()
{
std::lock_guard<std::mutex> lock(_mutex);
std::lock_guard<Mutex> lock(_mutex);
_async_mode = false;
}
static registry& instance()
static registry_t<Mutex>& instance()
{
static registry s_instance;
static registry_t<Mutex> s_instance;
return s_instance;
}
private:
registry() = default;
registry(const registry&) = delete;
registry& operator=(const registry&) = delete;
std::mutex _mutex;
void register_logger_impl(std::shared_ptr<logger> logger)
{
auto logger_name = logger->name();
if (_loggers.find(logger_name) != std::end(_loggers))
throw spdlog_ex("logger with name " + logger_name + " already exists");
_loggers[logger->name()] = logger;
}
registry_t<Mutex>(){}
registry_t<Mutex>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::info;
@ -154,6 +169,12 @@ private:
size_t _async_q_size = 0;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
std::function<void()> _worker_warmup_cb = nullptr;
std::chrono::milliseconds _flush_interval_ms;
};
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
#else
typedef registry_t<std::mutex> registry;
#endif
}
}

@ -32,6 +32,11 @@
#include "../sinks/stdout_sinks.h"
#include "../sinks/syslog_sink.h"
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{
return details::registry::instance().register_logger(logger);
}
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{
return details::registry::instance().get(name);
@ -67,22 +72,22 @@ inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string
// Create stdout/stderr loggers
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
return create<spdlog::sinks::stdout_sink_mt>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return create<spdlog::sinks::stdout_sink_st>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return create<spdlog::sinks::stderr_sink_mt>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return create<spdlog::sinks::stderr_sink_st>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
#ifdef __linux__
@ -132,9 +137,9 @@ inline void spdlog::set_level(level::level_enum log_level)
}
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb)
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms)
{
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb);
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms);
}
inline void spdlog::set_sync_mode()

@ -107,6 +107,7 @@ public:
void set_pattern(const std::string&);
void set_formatter(formatter_ptr);
void flush();
protected:
virtual void _log_msg(details::log_msg&);

@ -58,7 +58,6 @@ public:
_sink_it(msg);
}
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
Mutex _mutex;

@ -30,25 +30,27 @@
#include "../details/file_helper.h"
#include "../details/format.h"
namespace spdlog
{
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink<Mutex>
class simple_file_sink : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const std::string &filename,
bool force_flush=false):
bool force_flush = false) :
_file_helper(force_flush)
{
_file_helper.open(filename);
}
void flush() override
{
_file_helper.flush();
}
protected:
void _sink_it(const details::log_msg& msg) override
@ -63,15 +65,15 @@ typedef simple_file_sink<std::mutex> simple_file_sink_mt;
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>
class rotating_file_sink : public base_sink<Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const std::string &base_filename, const std::string &extension,
std::size_t max_size, std::size_t max_files,
bool force_flush=false):
bool force_flush = false) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
@ -80,14 +82,19 @@ public:
_file_helper(force_flush)
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_current_size = _file_helper.size(); //expensive. called only once
}
void flush() override
{
_file_helper.flush();
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_current_size += msg.formatted.size();
if (_current_size > _max_size)
if (_current_size > _max_size)
{
_rotate();
_current_size = msg.formatted.size();
@ -95,11 +102,10 @@ protected:
_file_helper.write(msg);
}
private:
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
{
details::fmt::MemoryWriter w;
fmt::MemoryWriter w;
if (index)
w.write("{}.{}.{}", filename, index, extension);
else
@ -107,14 +113,12 @@ private:
return w.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
void _rotate()
{
_file_helper.close();
@ -149,10 +153,10 @@ typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Rotating file sink based on date. rotates at midnight
*/
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex>
class daily_file_sink:public base_sink<Mutex>
class daily_file_sink :public base_sink < Mutex >
{
public:
//create daily file sink which rotates on given time
@ -161,7 +165,7 @@ public:
const std::string& extension,
int rotation_hour,
int rotation_minute,
bool force_flush=false): _base_filename(base_filename),
bool force_flush = false) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute),
@ -169,17 +173,20 @@ public:
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp(),
_rotation_tp = _next_rotation_tp();
_file_helper.open(calc_filename(_base_filename, _extension));
}
void flush() override
{
_file_helper.flush();
}
protected:
void _sink_it(const details::log_msg& msg) override
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.close();
_file_helper.open(calc_filename(_base_filename, _extension));
_rotation_tp = _next_rotation_tp();
}
@ -207,7 +214,7 @@ private:
static std::string calc_filename(const std::string& basename, const std::string& extension)
{
std::tm tm = spdlog::details::os::localtime();
details::fmt::MemoryWriter w;
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
return w.str();
}
@ -218,11 +225,9 @@ private:
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
}
}
}

@ -40,6 +40,9 @@ protected:
void _sink_it(const details::log_msg&) override
{}
void flush() override
{}
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt;

@ -45,12 +45,18 @@ public:
virtual ~ostream_sink() = default;
protected:
virtual void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg& msg) override
{
_ostream.write(msg.formatted.data(), msg.formatted.size());
if (_force_flush)
_ostream.flush();
}
void flush() override
{
_ostream.flush();
}
std::ostream& _ostream;
bool _force_flush;
};

@ -35,6 +35,7 @@ class sink
public:
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0;
};
}
}

@ -45,9 +45,14 @@ template <class Mutex>
class stdout_sink : public outstream_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() : outstream_sink<Mutex>(std::cout, true) {}
stdout_sink() : ostream_sink<Mutex>(std::cout, true) {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
@ -57,8 +62,15 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public outstream_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() : outstream_sink<Mutex>(std::cerr, true) {}
stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stderr_sink<std::mutex> stderr_sink_mt;

@ -78,6 +78,9 @@ public:
::syslog(syslog_prio_from_level(msg), "%s", msg.formatted.str().c_str());
}
void flush() override
{
}
private:

@ -28,16 +28,12 @@
#pragma once
#include "tweakme.h"
#include "common.h"
#include "logger.h"
namespace spdlog
{
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples:
//
@ -72,15 +68,15 @@ void set_level(level::level_enum log_level);
// worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
//
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr);
void set_async_mode(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());
// Turn off async mode
void set_sync_mode();
//
// Create multi/single threaded rotating file logger
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const std::string& filenameB, size_t max_file_size, size_t max_files, bool force_flush = false);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
//
@ -91,7 +87,7 @@ std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const st
//
// Create stdout/stderr loggers
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
@ -100,57 +96,59 @@ std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create a syslog logger
// Create and register a syslog logger
//
#ifdef __linux__
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
// Create a logger with multiple sinks
// Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
// Create a logger with templated sink type
// Create and register a logger with templated sink type
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, const Args&...);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references
void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// Trace & debug macros to be switched on/off at compile time for zero cost debug statements.
// Note: using these mactors overrides the runtime log threshold of the logger.
// Macros to be display source file & line
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
//
// Example:
//
// Enable debug macro, must be defined before including spdlog.h
// #define SPDLOG_DEBUG_ON
// include "spdlog/spdlog.h"
// spdlog::set_level(spdlog::level::debug);
// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2);
//
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_TRACE(logger, ...) logger->force_log(spdlog::level::trace, __VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#define SPDLOG_TRACE(logger, ...) logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#else
#define SPDLOG_TRACE(logger, ...)
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->force_log(spdlog::level::debug, __VA_ARGS__)
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#else
#define SPDLOG_DEBUG(logger, ...)
#endif
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references
void drop_all();
}

@ -0,0 +1,74 @@
/*************************************************************************/
/* spdlog - an extremely fast and easy to use c++11 logging library. */
/* Copyright (c) 2014 Gabi Melman. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////////
// Edit this file to squeeze every last drop of performance out of spdlog.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
// Uncomment to use it instead of the regular (but slower) clock.
// #define SPDLOG_CLOCK_COARSE
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed.
// This will prevent spdlog from quering the clock on each log call.
// #define SPDLOG_NO_DATETIME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
// This will prevent spdlog from quering the thread id on each log call.
// #define SPDLOG_NO_THREAD_ID
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
// #define SPDLOG_NO_NAME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
// #define SPDLOG_DEBUG_ON
// #define SPDLOG_TRACE_ON
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifes concurrently the registry.
// Note that upon creating a logger the registry is modified by spdlog..
// #define SPDLOG_NO_REGISTRY_MUTEX
///////////////////////////////////////////////////////////////////////////////

@ -0,0 +1,22 @@
CXX ?= g++
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2
LDPFALGS = -pthread
CPP_FILES := $(wildcard *.cpp)
OBJ_FILES := $(addprefix ./,$(notdir $(CPP_FILES:.cpp=.o)))
tests: $(OBJ_FILES)
$(CXX) $(CXXFLAGS) $(LDPFALGS) -o $@ $^
mkdir -p logs
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
rm -f tests *.o logs/*.txt
rebuild: clean tests

File diff suppressed because it is too large Load Diff

@ -0,0 +1,132 @@
#include "includes.h"
static std::string file_contents(const std::string& filename)
{
std::ifstream ifs(filename);
if (!ifs)
throw std::runtime_error("Failed open file ");
return std::string((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
}
static std::size_t count_lines(const std::string& filename)
{
std::ifstream ifs(filename);
if (!ifs)
throw std::runtime_error("Failed open file ");
std::string line;
size_t counter = 0;
while(std::getline(ifs, line))
counter++;
return counter;
}
std::ifstream::pos_type filesize(const std::string& filename)
{
std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
if (!ifs)
throw std::runtime_error("Failed open file ");
return ifs.tellg();
}
static void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
auto rv = system("del /F /Q logs\\*");
#else
auto rv = system("rm -f logs/*");
#endif
}
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
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->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0, true);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
auto filename = basename + ".txt";
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
}
TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
{
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1, false);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
logger->flush();
REQUIRE(filesize(filename) <= 1024);
auto filename1 = basename + ".1.txt";
REQUIRE(filesize(filename1) <= 1024);
}
TEST_CASE("daily_logger", "[daily_logger]]")
{
prepare_logdir();
//calculate filename (time based)
std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0, true);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}

@ -0,0 +1,90 @@
#include "includes.h"
template<class T>
std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_level(logger_level);
oss_logger.set_pattern("%v");
oss_logger.info() << what;
//strip last eol and return the logged string
auto eol_size = strlen(spdlog::details::os::eol());
return oss.str().substr(0, oss.str().length() - eol_size);
}
//User defined class with operator<<
struct some_logged_class
{
some_logged_class(const std::string val) :value(val) {};
std::string value;
};
std::ostream& operator<<(std::ostream& os, const some_logged_class& c)
{
return os << c.value;
}
TEST_CASE("basic_logging ", "[basic_logging]")
{
//const char
REQUIRE(log_info("Hello") == "Hello");
REQUIRE(log_info("") == "");
//std::string
REQUIRE(log_info(std::string("Hello")) == "Hello");
REQUIRE(log_info(std::string()) == std::string());
//Numbers
REQUIRE(log_info(5) == "5");
REQUIRE(log_info(5.6) == "5.6");
//User defined class
REQUIRE(log_info(some_logged_class("some_val")) == "some_val");
}
TEST_CASE("log_levels", "[log_levels]")
{
REQUIRE(log_info("Hello", spdlog::level::err) == "");
REQUIRE(log_info("Hello", spdlog::level::critical) == "");
REQUIRE(log_info("Hello", spdlog::level::emerg) == "");
REQUIRE(log_info("Hello", spdlog::level::alert) == "");
REQUIRE(log_info("Hello", spdlog::level::info) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
}
TEST_CASE("invalid_format", "[format]")
{
using namespace spdlog::sinks;
spdlog::logger null_logger("null_logger", std::make_shared<null_sink_st>());
REQUIRE_THROWS_AS(
null_logger.info("{} {}", "first"),
spdlog::spdlog_ex);
REQUIRE_THROWS_AS(
null_logger.info("{0:f}", "aads"),
spdlog::spdlog_ex);
REQUIRE_THROWS_AS(
null_logger.info("{0:kk}", 123),
spdlog::spdlog_ex);
}

@ -0,0 +1,12 @@
#pragma once
#include <cstdio>
#include <fstream>
#include <string>
#include <ostream>
#include <chrono>
#include <exception>
#include "catch.hpp"
#include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h"

@ -0,0 +1,12 @@
#!/bin/bash
#
# Install libc++ under travis
svn --quiet co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx
mkdir libcxx/build
(cd libcxx/build && cmake .. -DLIBCXX_CXX_ABI=libstdc++ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/4.6;/usr/include/c++/4.6/x86_64-linux-gnu")
make -C libcxx/build cxx -j2
sudo cp libcxx/build/lib/libc++.so.1.0 /usr/lib/
sudo cp -r libcxx/build/include/c++/v1 /usr/include/c++/v1/
sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so
sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so.1

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

@ -0,0 +1,53 @@
#include "includes.h"
static const char *logger_name = "null_logger";
TEST_CASE("register_drop", "[registry]")
{
spdlog::drop_all();
spdlog::create<spdlog::sinks::null_sink_mt>(logger_name);
REQUIRE(spdlog::get(logger_name)!=nullptr);
//Throw if registring existing name
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(logger_name), spdlog::spdlog_ex);
}
TEST_CASE("explicit register" "[registry]")
{
spdlog::drop_all();
auto logger = std::make_shared<spdlog::logger>(logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
spdlog::register_logger(logger);
REQUIRE(spdlog::get(logger_name) != nullptr);
//Throw if registring existing name
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(logger_name), spdlog::spdlog_ex);
}
TEST_CASE("drop" "[registry]")
{
spdlog::drop_all();
spdlog::create<spdlog::sinks::null_sink_mt>(logger_name);
spdlog::drop(logger_name);
REQUIRE_FALSE(spdlog::get(logger_name));
}
TEST_CASE("drop_all" "[registry]")
{
spdlog::drop_all();
spdlog::create<spdlog::sinks::null_sink_mt>(logger_name);
spdlog::create<spdlog::sinks::null_sink_mt>("name2");
spdlog::drop_all();
REQUIRE_FALSE(spdlog::get(logger_name));
REQUIRE_FALSE(spdlog::get("name2"));
}
TEST_CASE("drop non existing" "[registry]")
{
spdlog::drop_all();
spdlog::create<spdlog::sinks::null_sink_mt>(logger_name);
spdlog::drop("some_name");
REQUIRE_FALSE(spdlog::get("some_name"));
REQUIRE(spdlog::get(logger_name));
spdlog::drop_all();
}

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.ActiveCfg = Debug|Win32
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.Build.0 = Debug|Win32
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.ActiveCfg = Debug|x64
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.Build.0 = Debug|x64
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.ActiveCfg = Release|Win32
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.Build.0 = Release|Win32
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.ActiveCfg = Release|x64
{59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{59A07559-5F38-4DD6-A7FA-DB4153690B42}</ProjectGuid>
<RootNamespace>tests</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="file_log.cpp" />
<ClCompile Include="format.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="registry.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="catch.hpp" />
<ClInclude Include="includes.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="file_log.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="format.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="registry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="includes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="catch.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
Loading…
Cancel
Save