add spdlog into project, refactor

This commit is contained in:
Wieczorek Bartosz 2017-03-18 07:56:25 +01:00
parent bfa87eda2f
commit 1a1a1cf2a7
110 changed files with 22838 additions and 130 deletions

View File

@ -14,8 +14,8 @@ AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
@ -50,7 +50,7 @@ NamespaceIndentation: Inner
# PenaltyReturnTypeOnItsOwnLine:
PointerAlignment: Middle
# ReflowComments: false
# SortIncludes: false
SortIncludes: true
SpaceAfterCStyleCast: true
# SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true

View File

@ -4,9 +4,10 @@ project(eedb)
add_definitions( -std=c++17 )
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "/usr/share/cmake/Modules/")
find_package(Wt 4.0.0 REQUIRED)
find_package(Wt REQUIRED)
find_package(Sqlpp11 REQUIRED)
find_package(PostgreSQL REQUIRED)
find_package(Boost 1.54.0 REQUIRED system )
include_directories(share/include)
add_subdirectory(share)

64
share/spdlog/.gitignore vendored Normal file
View File

@ -0,0 +1,64 @@
# Auto generated files
*.slo
*.lo
*.o
*.obj
*.suo
*.tlog
*.ilk
*.log
*.pdb
*.idb
*.iobj
*.ipdb
*.opensdf
*.sdf
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Codelite
.codelite
# .orig files
*.orig
# example files
example/*
!example/example.cpp
!example/bench.cpp
!example/utils.h
!example/Makefile*
!example/example.sln
!example/example.vcxproj
!example/CMakeLists.txt
!example/multisink.cpp
!example/jni
# generated files
generated
# Cmake
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
/tests/tests.VC.VC.opendb
/tests/tests.VC.db
/tests/tests
/tests/logs/file_helper_test.txt

90
share/spdlog/.travis.yml Normal file
View File

@ -0,0 +1,90 @@
# 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 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="-I${CHECKOUT_PATH}/include"
- 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

View File

@ -0,0 +1,87 @@
#
# Copyright(c) 2015 Ruslan Baratov.
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
#
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0)
include(CTest)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
endif()
add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON)
target_include_directories(
spdlog
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:include>"
)
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests)
endif()
### Install ###
# * https://github.com/forexample/package-example
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(config_install_dir "lib/cmake/${PROJECT_NAME}")
set(include_install_dir "include")
set(pkgconfig_install_dir "lib/pkgconfig")
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${version_config}" COMPATIBILITY SameMajorVersion
)
# Note: use 'targets_export_name'
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
install(
TARGETS spdlog
EXPORT "${targets_export_name}"
INCLUDES DESTINATION "${include_install_dir}"
)
install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
install(
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}"
)
install(
FILES "${pkg_config}"
DESTINATION "${pkgconfig_install_dir}"
)
install(
EXPORT "${targets_export_name}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
)
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})

13
share/spdlog/INSTALL Normal file
View File

@ -0,0 +1,13 @@
spdlog is header only library.
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
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
see the makefile in the example folder

22
share/spdlog/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016 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.

218
share/spdlog/README.md Normal file
View File

@ -0,0 +1,218 @@
# spdlog
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
## Install
#### Just copy the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
#### Or use your favourite package manager:
* Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Arch Linux: `pacman -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms
* Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw)
* Mac OSX (clang 3.5+)
* Android
##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
* Daily log files.
* Console logging (colors supported).
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
## Benchmarks
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):
|threads|boost log 1.54|glog |easylogging |spdlog|
|-------|:-------:|:-----:|----------:|------:|
|1| 4.169s |1.066s |0.975s |0.302s|
|10| 6.180s |3.032s |2.857s |0.968s|
|100| 5.981s |1.139s |4.512s |0.497s|
#### 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>|
|:-------|:-----:|-------------------------:|
|1| 1.850s |0.216s |
|10| 0.943s |0.173s|
|100| 0.959s |0.202s|
## Usage Example
```c++
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
void async_example();
void syslog_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char*[])
{
try
{
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// 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);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
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..
async_example();
// syslog example. linux/osx only
syslog_example();
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
}
//syslog example
void syslog_example()
{
#ifdef SPDLOG_ENABLE_SYSLOG
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..");
#endif
}
// user defined types logging by implementing operator<<
struct my_type
{
int i;
template<typename OStream>
friend OStream& operator<<(OStream& os, const my_type &c)
{
return os << "[my_type i="<<c.i << "]";
}
};
#include <spdlog/fmt/ostr.h> // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type { 14 });
}
//
//custom error handler
//
void err_handler_example()
{
spdlog::set_error_handler([](const std::string& msg) {
std::cerr << "my err handler: " << msg << std::endl;
});
// (or logger->set_error_handler(..) to set for specific logger)
}
```
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.

5
share/spdlog/astyle.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix
find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1

View File

@ -0,0 +1,84 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
}
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@ -0,0 +1,47 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
}
int main(int argc, char* [])
{
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
for(int i = 0 ; i < howmany; ++i)
BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
return 0;
}

View File

@ -0,0 +1,10 @@
* GLOBAL:
FORMAT = "[%datetime]: %msg"
FILENAME = ./logs/easylogging.log
ENABLED = true
TO_FILE = true
TO_STANDARD_OUTPUT = false
MILLISECONDS_WIDTH = 3
PERFORMANCE_TRACKING = false
MAX_LOG_FILE_SIZE = 10485760
Log_Flush_Threshold = 10485760

View File

@ -0,0 +1,52 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#define _ELPP_THREAD_SAFE
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", conf);
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@ -0,0 +1,22 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP
int main(int, char* [])
{
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", conf);
for(int i = 0 ; i < howmany; ++i)
LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
return 0;
}

View File

@ -0,0 +1,62 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include "g2logworker.h"
#include "g2log.h"
using namespace std;
template<typename T> std::string format(const T& value);
int main(int argc, char* argv[])
{
using namespace std::chrono;
using clock=steady_clock;
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
g2LogWorker g2log(argv[0], "logs");
g2::initializeLogging(&g2log);
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany/deltaf;
cout << "Total: " << howmany << std::endl;
cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
}

View File

@ -0,0 +1,50 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include "glog/logging.h"
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
FLAGS_logtostderr = 0;
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@ -0,0 +1,21 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "glog/logging.h"
int main(int, char* argv[])
{
int howmany = 1000000;
FLAGS_logtostderr = 0;
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
for(int i = 0 ; i < howmany; ++i)
LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure";
return 0;
}

View File

@ -0,0 +1,13 @@
#!/bin/bash
echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
rm -f *.log
for i in {1..10}
do
echo
sleep 0.5
./spdlog-latency ${1:-10} 2>/dev/null || exit
sleep 0.5
./g3log-latency ${1:-10} 2>/dev/null || exit
done

View File

@ -0,0 +1,37 @@
#include <iostream>
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
void CrusherLoop()
{
size_t counter = 0;
while (true)
{
LOGF(INFO, "Some text to crush you machine. thread:");
if(++counter % 1000000 == 0)
{
std::cout << "Wrote " << counter << " entries" << std::endl;
}
}
}
int main(int argc, char** argv)
{
std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
std::cout << "Are you sure you want to continue ? " << std::endl;
char c;
std::cin >> c;
if (toupper( c ) != 'Y')
return 0;
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
g3::initializeLogging(worker.get());
CrusherLoop();
return 0;
}

View File

@ -0,0 +1,129 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <map>
#include <numeric>
#include <functional>
#include <thread>
#include "utils.h"
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
namespace
{
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
{
while (true)
{
const size_t value_now = ++g_counter;
if (value_now > g_iterations)
{
return;
}
auto start_time = std::chrono::high_resolution_clock::now();
LOGF(INFO, "Some text to log for thread: %ld", id);
auto stop_time = std::chrono::high_resolution_clock::now();
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
result.push_back(time_us);
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
// calc worst latenct
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
}
}// anonymous
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
// the worst latency is for writing a log entry
//
// In the test 1 million log entries will be written
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
{
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
// reserve to 1 million for all the result
// it's a test so let's not care about the wasted space
threads_result[idx].reserve(g_iterations);
}
const std::string g_path = "./" ;
const std::string g_prefix_log_name = "g3log-performance-";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
g3::initializeLogging(worker.get());
auto start_time_application_total = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
}
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@ -0,0 +1,128 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <map>
#include <numeric>
#include <functional>
#include "utils.h"
#include <thread>
#include "spdlog/spdlog.h"
namespace spd = spdlog;
namespace
{
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
{
auto logger = spd::get("file_logger");
while (true)
{
const size_t value_now = ++g_counter;
if (value_now > g_iterations)
{
return;
}
auto start_time = std::chrono::high_resolution_clock::now();
logger->info("Some text to log for thread: [somemore text...............................] {}", id);
auto stop_time = std::chrono::high_resolution_clock::now();
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
result.push_back(time_us);
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
// calc worst latenct
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
}
}// anonymous
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
// the worst latency is for writing a log entry
//
// In the test 1 million log entries will be written
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
{
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
// reserve to 1 million for all the result
// it's a test so let's not care about the wasted space
threads_result[idx].reserve(g_iterations);
}
int queue_size = 1048576; // 2 ^ 20
spdlog::set_async_mode(queue_size);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
//force flush on every call to compare with g3log
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
s->set_force_flush(true);
auto start_time_application_total = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
}
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@ -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();
}
}

4
share/spdlog/bench/logs/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,62 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <cstdlib>
#include "spdlog/spdlog.h"
using namespace std;
int main(int argc, char* argv[])
{
using namespace std::chrono;
using clock=steady_clock;
namespace spd = spdlog;
int thread_count = 10;
if(argc > 1)
thread_count = ::atoi(argv[1]);
int howmany = 1000000;
spd::set_async_mode(1048576);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-async.txt", false);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany/deltaf;
cout << "Total: " << howmany << std::endl;
cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
}

View File

@ -0,0 +1,55 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <cstdlib>
#include "spdlog/spdlog.h"
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
namespace spd = spdlog;
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-mt.txt", false);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::atomic<int > msg_counter {0};
std::vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@ -0,0 +1,20 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "spdlog/spdlog.h"
int main(int, char* [])
{
int howmany = 1000000;
namespace spd = spdlog;
///Create a file rotating logger with 5mb size max and 3 rotated files
auto logger = spdlog::create<spd::sinks::simple_file_sink_st>("file_logger", "logs/spd-bench-st.txt", false);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
for(int i = 0 ; i < howmany; ++i)
logger->info("spdlog message #{} : This is some text for your pleasure", i);
return 0;
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -0,0 +1,24 @@
# *************************************************************************/
# * Copyright (c) 2015 Ruslan Baratov. */
# * */
# * 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. */
# *************************************************************************/
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")

View File

@ -0,0 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/include
Name: @PROJECT_NAME@
Description: Super fast C++ logging library.
Version: @PROJECT_VERSION@

View File

@ -0,0 +1,49 @@
# *************************************************************************/
# * Copyright (c) 2015 Ruslan Baratov. */
# * */
# * 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. */
# *************************************************************************/
cmake_minimum_required(VERSION 3.0)
project(SpdlogExamples)
if(TARGET spdlog)
# Part of the main project
add_library(spdlog::spdlog ALIAS spdlog)
else()
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
endif()
find_package(Threads)
add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(benchmark bench.cpp)
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
enable_testing()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
add_test(NAME RunExample COMMAND example)
add_test(NAME RunBenchmark COMMAND benchmark)

View File

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

View File

@ -0,0 +1,32 @@
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g
all: example bench
debug: example-debug bench-debug
example: example.cpp
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
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)
clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug
rebuild: clean all
rebuild-debug: clean debug

View File

@ -0,0 +1,144 @@
//
// 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/file_sinks.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;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(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 file_size = 30 * 1024 * 1024;
int rotating_files = 5;
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";
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i)
{
auto as = spdlog::daily_logger_st("as", "logs/daily_async");
bench_mt(howmany, as, threads);
spdlog::drop("as");
}
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
cout << log->name() << "...\t\t" << flush;
auto start = system_clock::now();
for (auto i = 0; i < howmany; ++i)
{
log->info("Hello logger: msg number {}", i);
}
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl;
}
void bench_mt(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();
cout << format(int(howmany / delta_d)) << "/sec" << endl;
}

View File

@ -0,0 +1,163 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// spdlog usage example
//
//
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
void async_example();
void syslog_example();
void android_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char*[])
{
try
{
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// 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);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
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..
async_example();
// syslog example. linux/osx only
syslog_example();
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
}
//syslog example (linux/osx/freebsd)
void syslog_example()
{
#ifdef SPDLOG_ENABLE_SYSLOG
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.");
#endif
}
// Android example
void android_example()
{
#if defined(__ANDROID__)
std::string tag = "spdlog-android";
auto android_logger = spd::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
#endif
}
// user defined types logging by implementing operator<<
struct my_type
{
int i;
template<typename OStream>
friend OStream& operator<<(OStream& os, const my_type &c)
{
return os << "[my_type i="<<c.i << "]";
}
};
#include <spdlog/fmt/ostr.h> // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type { 14 });
}
//
//custom error handler
//
void err_handler_example()
{
//can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string& msg)
{
std::cerr << "my err handler: " << msg << std::endl;
});
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}

View File

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
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
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.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="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="example.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\spdlog\async_logger.h" />
<ClInclude Include="..\include\spdlog\common.h" />
<ClInclude Include="..\include\spdlog\details\async_logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\async_log_helper.h" />
<ClInclude Include="..\include\spdlog\details\file_helper.h" />
<ClInclude Include="..\include\spdlog\details\logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\log_msg.h" />
<ClInclude Include="..\include\spdlog\details\mpmc_bounded_q.h" />
<ClInclude Include="..\include\spdlog\details\null_mutex.h" />
<ClInclude Include="..\include\spdlog\details\os.h" />
<ClInclude Include="..\include\spdlog\details\pattern_formatter_impl.h" />
<ClInclude Include="..\include\spdlog\details\registry.h" />
<ClInclude Include="..\include\spdlog\details\spdlog_impl.h" />
<ClInclude Include="..\include\spdlog\fmt\fmt.h" />
<ClInclude Include="..\include\spdlog\fmt\ostr.h" />
<ClInclude Include="..\include\spdlog\formatter.h" />
<ClInclude Include="..\include\spdlog\logger.h" />
<ClInclude Include="..\include\spdlog\sinks\android_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ansicolor_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\base_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\dist_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\file_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\msvc_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\null_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ostream_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
<ClInclude Include="..\include\spdlog\spdlog.h" />
<ClInclude Include="..\include\spdlog\tweakme.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</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>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</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 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>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile />
<PrecompiledHeaderOutputFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile />
<PrecompiledHeaderOutputFile />
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,15 @@
# Setup a project
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := example
LOCAL_SRC_FILES := example.cpp
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
LOCAL_LDFLAGS += -fPIE -pie
# Add exception support and set path for spdlog's headers
LOCAL_CPPFLAGS += -fexceptions -I../include
# Use android's log library
LOCAL_LDFLAGS += -llog
include $(BUILD_EXECUTABLE)

View File

@ -0,0 +1,2 @@
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
APP_STL = gnustl_static

View File

@ -0,0 +1 @@
../example.cpp

View File

@ -0,0 +1,47 @@
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
namespace spd = spdlog;
int main(int, char*[])
{
bool enable_debug = true;
try
{
// This other example use a single logger with multiple sinks.
// This means that the same log_msg is forwarded to multiple sinks;
// Each sink can have it's own log level and a message will be logged.
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
console_multisink.set_level( spdlog::level::warn);
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
console_multisink.warn("warn: will print only on console and regular file");
if( enable_debug )
{
console_multisink.set_level( spdlog::level::debug); // level of the logger
sinks[1]->set_level( spdlog::level::debug); // regular file
sinks[2]->set_level( spdlog::level::debug); // debug file
}
console_multisink.debug("Debug: you should see this on console and both files");
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}

View File

@ -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();
}
}

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>

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 SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated)
#else
#define SPDLOG_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

View File

@ -0,0 +1,378 @@
//
// 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=(const 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 std::this_thread::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);
}
}

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_list,
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_list.begin(), sinks_list.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");
}
}

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;
};
}
}

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;
};
}
}

View File

@ -0,0 +1,298 @@
//
// 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);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

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

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;
}
};
}
}

View File

@ -0,0 +1,406 @@
//
// 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 <chrono>
#include <thread>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle support
#ifdef __MINGW32__
#include <share.h>
#endif
#else // unix
#include <unistd.h>
#include <fcntl.h>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif
#endif //unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog
{
namespace details
{
namespace os
{
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;
inline void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno);
#else
auto fd = fileno(f);
if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif
}
//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
#else //unix
*fp = fopen((filename.c_str()), mode.c_str());
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if(*fp != nullptr)
prevent_child_fd(*fp);
#endif
return *fp == nullptr;
}
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 static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return static_cast<size_t>(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
}
//Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
{
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
return _thread_id();
#else
static thread_local const size_t tid = _thread_id();
return tid;
#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) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_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
}
inline int pid()
{
#ifdef _WIN32
return ::_getpid();
#else
return static_cast<int>(::getpid());
#endif
}
} //os
} //details
} //spdlog

View File

@ -0,0 +1,670 @@
//
// 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>
#include <array>
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
using days_array = std::array<std::string, 7>;
static const days_array& days()
{
static const days_array arr{ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } };
return arr;
}
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 days_array& full_days()
{
static const days_array arr{ { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } };
return arr;
}
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
using months_array = std::array<std::string, 12>;
static const months_array& months()
{
static const months_array arr{ { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" } };
return arr;
}
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 months_array& full_months()
{
static const months_array arr{ { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } };
return arr;
}
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
{
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, tm_time.tm_sec, ':');
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter:public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter():_last_update(std::chrono::seconds(0))
{}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time);
#else
// No need to chache under gcc,
// it is very fast (already stored in tm.tm_gmtoff)
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
bool is_negative = total_minutes < 0;
char sign;
if (is_negative)
{
total_minutes = -total_minutes;
sign = '-';
}
else
{
sign = '+';
}
int h = total_minutes / 60;
int m = total_minutes % 60;
msg.formatted << sign;
pad_n_join(msg.formatted, h, m, ':');
}
private:
log_clock::time_point _last_update;
int _offset_minutes;
std::mutex _mutex;
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
{
using namespace std::chrono;
std::lock_guard<std::mutex> l(_mutex);
if (msg.time - _last_update >= cache_refresh)
{
_offset_minutes = os::utc_minutes_offset(tm_time);
_last_update = msg.time;
}
return _offset_minutes;
}
};
// Thread id
class t_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << msg.thread_id;
}
};
// Current pid
class pid_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << details::os::pid();
}
};
class v_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
class ch_formatter:public flag_formatter
{
public:
explicit ch_formatter(char ch): _ch(ch)
{}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _ch;
}
private:
char _ch;
};
//aggregate user chars to display as is
class aggregate_formatter:public flag_formatter
{
public:
aggregate_formatter()
{}
void add_ch(char ch)
{
_str += ch;
}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _str;
}
private:
std::string _str;
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
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;
/* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
tm_time.tm_year + 1900,
tm_time.tm_mon + 1,
tm_time.tm_mday,
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
static_cast<int>(millis),
msg.logger_name,
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') << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
//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());
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
{
compile_pattern(pattern);
}
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
for (auto it = pattern.begin(); it != end; ++it)
{
if (*it == '%')
{
if (user_chars) //append user chars found so far
_formatters.push_back(std::move(user_chars));
if (++it != end)
handle_flag(*it);
else
break;
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
user_chars->add_ch(*it);
}
}
if (user_chars) //append raw chars found so far
{
_formatters.push_back(std::move(user_chars));
}
}
inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
case 'l':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
break;
case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break;
case('t'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
case('v'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
break;
case('a'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
break;
case('A'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
break;
case('b'):
case('h'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
break;
case('B'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
break;
case('c'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
break;
case('C'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
break;
case('Y'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
break;
case('D'):
case('x'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
break;
case('m'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
break;
case('d'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
break;
case('H'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
break;
case('I'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
break;
case('M'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
break;
case('S'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
break;
case('e'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
break;
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()));
break;
case('r'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
break;
case('R'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
break;
case('T'):
case('X'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
break;
case('z'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
break;
case ('+'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
default: //Unkown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break;
}
}
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
#ifndef SPDLOG_NO_DATETIME
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
#else
std::tm tm_time;
#endif
for (auto &f : _formatters)
{
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol, details::os::eol_size);
}

View File

@ -0,0 +1,185 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Loggers registy of unique name->logger pointer
// An attempt to create a logger with an already existing name will be ignored
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include <spdlog/details/null_mutex.h>
#include <spdlog/logger.h>
#include <spdlog/async_logger.h>
#include <spdlog/common.h>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace spdlog
{
namespace details
{
template <class Mutex> class registry_t
{
public:
void register_logger(std::shared_ptr<logger> logger)
{
std::lock_guard<Mutex> lock(_mutex);
auto logger_name = logger->name();
throw_if_exists(logger_name);
_loggers[logger_name] = logger;
}
std::shared_ptr<logger> get(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
auto found = _loggers.find(logger_name);
return found == _loggers.end() ? nullptr : found->second;
}
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
std::lock_guard<Mutex> lock(_mutex);
throw_if_exists(logger_name);
std::shared_ptr<logger> new_logger;
if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
if (_formatter)
new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
}
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto &l : _loggers)
fun(l.second);
}
void drop(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.erase(logger_name);
}
void drop_all()
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.clear();
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
{
return create(logger_name, sinks.begin(), sinks.end());
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
{
return create(logger_name, { sink });
}
void formatter(formatter_ptr f)
{
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<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<Mutex> lock(_mutex);
for (auto& l : _loggers)
l.second->set_level(log_level);
_level = log_level;
}
void set_error_handler(log_err_handler handler)
{
for (auto& l : _loggers)
l.second->set_error_handler(handler);
_err_handler = handler;
}
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, const std::function<void()>& worker_teardown_cb)
{
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;
_worker_teardown_cb = worker_teardown_cb;
}
void set_sync_mode()
{
std::lock_guard<Mutex> lock(_mutex);
_async_mode = false;
}
static registry_t<Mutex>& instance()
{
static registry_t<Mutex> s_instance;
return s_instance;
}
private:
registry_t<Mutex>() {}
registry_t<Mutex>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
void throw_if_exists(const std::string &logger_name)
{
if (_loggers.find(logger_name) != _loggers.end())
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
}
Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::info;
log_err_handler _err_handler;
bool _async_mode = false;
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;
std::function<void()> _worker_teardown_cb = nullptr;
};
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
#else
typedef registry_t<std::mutex> registry;
#endif
}
}

View File

@ -0,0 +1,245 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Global registry functions
//
#include <spdlog/spdlog.h>
#include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h>
#endif
#ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h>
#else
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h>
#endif
#include <chrono>
#include <functional>
#include <memory>
#include <string>
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);
}
inline void spdlog::drop(const std::string &name)
{
details::registry::instance().drop(name);
}
// Create multi/single threaded simple file logger
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
}
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
}
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
}
//
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
return spdlog::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 spdlog::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 spdlog::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 spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#else //ansi terminal colors
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_mt::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_st::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_mt::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_st::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
#endif
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
}
#endif
#ifdef __ANDROID__
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);
}
#endif
// Create and register a logger a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
{
return details::registry::instance().create(logger_name, sink);
}
//Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
{
sink_ptr sink = std::make_shared<Sink>(args...);
return details::registry::instance().create(logger_name, { sink });
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
}
inline void spdlog::set_pattern(const std::string& format_string)
{
return details::registry::instance().set_pattern(format_string);
}
inline void spdlog::set_level(level::level_enum log_level)
{
return details::registry::instance().set_level(log_level);
}
inline void spdlog::set_error_handler(log_err_handler handler)
{
return details::registry::instance().set_error_handler(handler);
}
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, const std::function<void()>& worker_teardown_cb)
{
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
}
inline void spdlog::set_sync_mode()
{
details::registry::instance().set_sync_mode();
}
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(fun);
}
inline void spdlog::drop_all()
{
details::registry::instance().drop_all();
}

View File

@ -0,0 +1,583 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
using fmt::internal::Arg;
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...)
{
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...)
{
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT
{}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT
{}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT
{}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...)
{
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void(*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT
{
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError
{
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &)
{}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result)
{
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message)
{
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>)
{
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result)
{
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>)
{
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size)
{}
int run()
{
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT
{
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT
{
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0.
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
{
fmt::format_system_error(out, error_code, message);
}
} // namespace internal
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args)
{
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value)
{
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value)
{
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type)
{
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s)
{
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s)
{
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s)
{
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args)
{
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
{
FMT_TRY{
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...)
{}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
{
FMT_TRY{
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...)
{}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args)
{
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t)
{
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error)
{
Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT
{
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT
{
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args)
{
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args)
{
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args)
{
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w)
{
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

View File

@ -0,0 +1,118 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// commented out by spdlog
// #include "format.h"
#include <ostream>
namespace fmt
{
namespace internal
{
template <class Char>
class FormatBuf: public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer): buffer_(buffer), start_(&buffer[0])
{
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof())
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
{
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_ + buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
size_t size() const
{
return to_unsigned(this->pptr() - start_);
}
};
Yes &convert(std::ostream &);
struct DummyStream: std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

View File

@ -0,0 +1,658 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt
{
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker
{
template <typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template <>
struct IntChecker<true>
{
template <typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class PrecisionHandler: public ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg()
{
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value)
{
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt: public ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
template <typename T, typename U>
struct is_same
{
enum
{
value = 0
};
};
template <typename T>
struct is_same<T, T>
{
enum
{
value = 1
};
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter: public ArgVisitor<ArgConverter<T>, void>
{
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type)
{}
void visit_bool(bool value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
}
else
{
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
}
else
{
if (is_signed)
{
arg_.type = Arg::LONG_LONG;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter: public ArgVisitor<CharConverter, void>
{
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg): arg_(arg)
{}
template <typename T>
void visit_any_int(T value)
{
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler: public ArgVisitor<WidthHandler, unsigned>
{
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec): spec_(spec)
{}
void report_unhandled_arg()
{
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value)
{
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicPrintfArgFormatter: public internal::ArgFormatterBase<Impl, Char>
{
private:
void write_null_pointer()
{
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: internal::ArgFormatterBase<Impl, Char>(w, s)
{}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
{
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value)
{
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1)
{
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT)
{
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else
{
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else
{
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value)
{
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value)
{
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = { '}', 0 };
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
/** The default printf argument formatter. */
template <typename Char>
class PrintfArgFormatter
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s)
{}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter: private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w)
{}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{
for (;;)
{
switch (*s++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index
{
++s;
arg_index = value;
}
else
{
if (c == '0')
spec.fill_ = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9')
{
spec.width_ = internal::parse_nonnegative_int(s);
}
else if (*s == '*')
{
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{
const Char *start = format_str.c_str();
const Char *s = start;
while (*s)
{
Char c = *s++;
if (c != '%') continue;
if (*s == c)
{
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++)
{
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
{
PrintfFormatter<Char>(args, w).format(format);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@ -0,0 +1,28 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Include a bundled header-only copy of fmtlib or an external one.
// By default spdlog include its own copy.
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include <spdlog/fmt/bundled/format.h>
#else //external fmtlib
#include <fmt/format.h>
#endif

View File

@ -0,0 +1,17 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// include external or bundled copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/fmt.h>
#include <spdlog/fmt/bundled/ostream.h>
#else
#include <fmt/ostream.h>
#endif

View File

@ -0,0 +1,45 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/details/log_msg.h>
#include <vector>
#include <string>
#include <memory>
namespace spdlog
{
namespace details
{
class flag_formatter;
}
class formatter
{
public:
virtual ~formatter() {}
virtual void format(details::log_msg& msg) = 0;
};
class pattern_formatter : public formatter
{
public:
explicit pattern_formatter(const std::string& pattern);
pattern_formatter(const pattern_formatter&) = delete;
pattern_formatter& operator=(const pattern_formatter&) = delete;
void format(details::log_msg& msg) override;
private:
const std::string _pattern;
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
void handle_flag(char flag);
void compile_pattern(const std::string& pattern);
};
}
#include <spdlog/details/pattern_formatter_impl.h>

View File

@ -0,0 +1,94 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
// Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include <vector>
#include <memory>
#include <string>
namespace spdlog
{
class logger
{
public:
logger(const std::string& logger_name, sink_ptr single_sink);
logger(const std::string& name, sinks_init_list);
template<class It>
logger(const std::string& name, const It& begin, const It& end);
virtual ~logger();
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
template <typename T> void debug(const T&);
template <typename T> void info(const T&);
template <typename T> void warn(const T&);
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
bool should_log(level::level_enum) const;
void set_level(level::level_enum);
level::level_enum level() const;
const std::string& name() const;
void set_pattern(const std::string&);
void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr>& sinks() const;
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&);
virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg);
// return true if the given message level should trigger a flush
bool _should_flush_on(const details::log_msg&);
const std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
spdlog::level_t _level;
spdlog::level_t _flush_level;
log_err_handler _err_handler;
std::atomic<time_t> _last_err_time;
};
}
#include <spdlog/details/logger_impl.h>

View File

@ -0,0 +1,75 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(__ANDROID__)
#include <spdlog/sinks/sink.h>
#include <mutex>
#include <string>
#include <android/log.h>
namespace spdlog
{
namespace sinks
{
/*
* Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/
class android_sink : public sink
{
public:
explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {}
void log(const details::log_msg& msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
// See system/core/liblog/logger_write.c for explanation of return value
const int ret = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
if (ret < 0)
{
throw spdlog_ex("__android_log_write() failed", ret);
}
}
void flush() override
{
}
private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level)
{
switch(level)
{
case spdlog::level::trace:
return ANDROID_LOG_VERBOSE;
case spdlog::level::debug:
return ANDROID_LOG_DEBUG;
case spdlog::level::info:
return ANDROID_LOG_INFO;
case spdlog::level::warn:
return ANDROID_LOG_WARN;
case spdlog::level::err:
return ANDROID_LOG_ERROR;
case spdlog::level::critical:
return ANDROID_LOG_FATAL;
default:
return ANDROID_LOG_DEFAULT;
}
}
std::string _tag;
};
}
}
#endif

View File

@ -0,0 +1,116 @@
//
// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog).
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include <string>
#include <map>
namespace spdlog
{
namespace sinks
{
/**
* @brief The ansi_color_sink is a decorator around another sink and prefixes
* the output with an ANSI escape sequence color code depending on the severity
* of the message.
*/
class ansicolor_sink : public sink
{
public:
ansicolor_sink(sink_ptr wrapped_sink);
virtual ~ansicolor_sink();
ansicolor_sink(const ansicolor_sink& other) = delete;
ansicolor_sink& operator=(const ansicolor_sink& other) = delete;
virtual void log(const details::log_msg& msg) override;
virtual void flush() override;
void set_color(level::level_enum color_level, const std::string& color);
/// Formatting codes
const std::string reset = "\033[00m";
const std::string bold = "\033[1m";
const std::string dark = "\033[2m";
const std::string underline = "\033[4m";
const std::string blink = "\033[5m";
const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m";
// Foreground colors
const std::string grey = "\033[30m";
const std::string red = "\033[31m";
const std::string green = "\033[32m";
const std::string yellow = "\033[33m";
const std::string blue = "\033[34m";
const std::string magenta = "\033[35m";
const std::string cyan = "\033[36m";
const std::string white = "\033[37m";
/// Background colors
const std::string on_grey = "\033[40m";
const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m";
const std::string on_blue = "\033[44m";
const std::string on_magenta = "\033[45m";
const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m";
protected:
sink_ptr sink_;
std::map<level::level_enum, std::string> colors_;
};
inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink)
{
colors_[level::trace] = cyan;
colors_[level::debug] = cyan;
colors_[level::info] = bold;
colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red;
colors_[level::off] = reset;
}
inline void ansicolor_sink::log(const details::log_msg& msg)
{
// Wrap the originally formatted message in color codes
const std::string& prefix = colors_[msg.level];
const std::string& s = msg.formatted.str();
const std::string& suffix = reset;
details::log_msg m;
m.level = msg.level;
m.logger_name = msg.logger_name;
m.time = msg.time;
m.thread_id = msg.thread_id;
m.formatted << prefix << s << suffix;
sink_->log(m);
}
inline void ansicolor_sink::flush()
{
sink_->flush();
}
inline void ansicolor_sink::set_color(level::level_enum color_level, const std::string& color)
{
colors_[color_level] = color;
}
inline ansicolor_sink::~ansicolor_sink()
{
flush();
}
} // namespace sinks
} // namespace spdlog

View File

@ -0,0 +1,45 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method.
// all locking is taken care of here so no locking needed by the implementers..
//
#include <spdlog/sinks/sink.h>
#include <spdlog/formatter.h>
#include <spdlog/common.h>
#include <spdlog/details/log_msg.h>
#include <mutex>
namespace spdlog
{
namespace sinks
{
template<class Mutex>
class base_sink:public sink
{
public:
base_sink():_mutex() {}
virtual ~base_sink() = default;
base_sink(const base_sink&) = delete;
base_sink& operator=(const base_sink&) = delete;
void log(const details::log_msg& msg) override
{
std::lock_guard<Mutex> lock(_mutex);
_sink_it(msg);
}
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
Mutex _mutex;
};
}
}

View File

@ -0,0 +1,71 @@
//
// Copyright (c) 2015 David Schury, Gabi Melman
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/details/log_msg.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/sinks/sink.h>
#include <algorithm>
#include <mutex>
#include <memory>
#include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called
namespace spdlog
{
namespace sinks
{
template<class Mutex>
class dist_sink: public base_sink<Mutex>
{
public:
explicit dist_sink() :_sinks() {}
dist_sink(const dist_sink&) = delete;
dist_sink& operator=(const dist_sink&) = delete;
virtual ~dist_sink() = default;
protected:
std::vector<std::shared_ptr<sink>> _sinks;
void _sink_it(const details::log_msg& msg) override
{
for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
{
sink->log(msg);
}
}
}
public:
void flush() override
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto &sink : _sinks)
sink->flush();
}
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.push_back(sink);
}
void remove_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
}
};
typedef dist_sink<std::mutex> dist_sink_mt;
typedef dist_sink<details::null_mutex> dist_sink_st;
}
}

View File

@ -0,0 +1,239 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/file_helper.h>
#include <spdlog/fmt/fmt.h>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
#include <cerrno>
namespace spdlog
{
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{
_file_helper.open(filename, truncate);
}
void flush() override
{
_file_helper.flush();
}
void set_force_flush(bool force_flush)
{
_force_flush = force_flush;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
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
*/
template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0));
_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)
{
_rotate();
_current_size = msg.formatted.size();
}
_file_helper.write(msg);
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
void _rotate()
{
using details::os::filename_to_str;
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target))
{
if (details::os::remove(target) != 0)
{
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
}
}
if (details::file_helper::file_exists(src) && details::os::rename(src, target))
{
throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
_file_helper.reopen(true);
}
filename_t _base_filename;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
details::file_helper _file_helper;
};
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Default generator of daily log file names.
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
/*
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink :public base_sink < Mutex >
{
public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
{
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();
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
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.open(FileNameCalc::calc_filename(_base_filename));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
}
private:
std::chrono::system_clock::time_point _next_rotation_tp()
{
auto now = std::chrono::system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = _rotation_h;
date.tm_min = _rotation_m;
date.tm_sec = 0;
auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
return rotation_time;
else
return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
}
filename_t _base_filename;
int _rotation_h;
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;
}
}

View File

@ -0,0 +1,50 @@
//
// Copyright(c) 2016 Alexander Dalshov.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(_MSC_VER)
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <WinBase.h>
#include <mutex>
#include <string>
namespace spdlog
{
namespace sinks
{
/*
* MSVC sink (logging using OutputDebugStringA)
*/
template<class Mutex>
class msvc_sink : public base_sink < Mutex >
{
public:
explicit msvc_sink()
{
}
void flush() override
{
}
protected:
void _sink_it(const details::log_msg& msg) override
{
OutputDebugStringA(msg.formatted.c_str());
}
};
typedef msvc_sink<std::mutex> msvc_sink_mt;
typedef msvc_sink<details::null_mutex> msvc_sink_st;
}
}
#endif

View File

@ -0,0 +1,34 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <mutex>
namespace spdlog
{
namespace sinks
{
template <class Mutex>
class null_sink : public base_sink < Mutex >
{
protected:
void _sink_it(const details::log_msg&) override
{}
void flush() override
{}
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<details::null_mutex> null_sink_mt;
}
}

View File

@ -0,0 +1,47 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <ostream>
#include <mutex>
namespace spdlog
{
namespace sinks
{
template<class Mutex>
class ostream_sink: public base_sink<Mutex>
{
public:
explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {}
ostream_sink(const ostream_sink&) = delete;
ostream_sink& operator=(const ostream_sink&) = delete;
virtual ~ostream_sink() = default;
protected:
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;
};
typedef ostream_sink<std::mutex> ostream_sink_mt;
typedef ostream_sink<details::null_mutex> ostream_sink_st;
}
}

View File

@ -0,0 +1,53 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/details/log_msg.h>
namespace spdlog
{
namespace sinks
{
class sink
{
public:
sink()
{
_level = level::trace;
}
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0;
bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level);
level::level_enum level() const;
private:
level_t _level;
};
inline bool sink::should_log(level::level_enum msg_level) const
{
return msg_level >= _level.load(std::memory_order_relaxed);
}
inline void sink::set_level(level::level_enum log_level)
{
_level.store(log_level);
}
inline level::level_enum sink::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
}
}

View File

@ -0,0 +1,77 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <cstdio>
#include <memory>
#include <mutex>
namespace spdlog
{
namespace sinks
{
template <class Mutex>
class stdout_sink: public base_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
flush();
}
void flush() override
{
fflush(stdout);
}
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink: public base_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
flush();
}
void flush() override
{
fflush(stderr);
}
};
typedef stderr_sink<std::mutex> stderr_sink_mt;
typedef stderr_sink<details::null_mutex> stderr_sink_st;
}
}

View File

@ -0,0 +1,81 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/common.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/sink.h>
#include <spdlog/details/log_msg.h>
#include <array>
#include <string>
#include <syslog.h>
namespace spdlog
{
namespace sinks
{
/**
* Sink that write to syslog using the `syscall()` library call.
*
* Locking is not needed, as `syslog()` itself is thread-safe.
*/
class syslog_sink : public sink
{
public:
//
syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER):
_ident(ident)
{
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<int>(level::info)] = LOG_INFO;
_priorities[static_cast<int>(level::warn)] = LOG_WARNING;
_priorities[static_cast<int>(level::err)] = LOG_ERR;
_priorities[static_cast<int>(level::critical)] = LOG_CRIT;
_priorities[static_cast<int>(level::off)] = LOG_INFO;
//set ident to be program name if empty
::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility);
}
~syslog_sink()
{
::closelog();
}
syslog_sink(const syslog_sink&) = delete;
syslog_sink& operator=(const syslog_sink&) = delete;
void log(const details::log_msg &msg) override
{
::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
}
void flush() override
{
}
private:
std::array<int, 7> _priorities;
//must store the ident because the man says openlog might use the pointer as is and not a string copy
const std::string _ident;
//
// Simply maps spdlog's log level to syslog priority level.
//
int syslog_prio_from_level(const details::log_msg &msg) const
{
return _priorities[static_cast<int>(msg.level)];
}
};
}
}
#endif

View File

@ -0,0 +1,116 @@
//
// Copyright(c) 2016 spdlog
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/common.h>
#include <mutex>
#include <string>
#include <map>
#include <wincon.h>
namespace spdlog
{
namespace sinks
{
/*
* Windows color console sink. Uses WriteConsoleA to write to the console with colors
*/
template<class Mutex>
class wincolor_sink: public base_sink<Mutex>
{
public:
const WORD BOLD = FOREGROUND_INTENSITY;
const WORD RED = FOREGROUND_RED;
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
wincolor_sink(HANDLE std_handle): out_handle_(std_handle)
{
colors_[level::trace] = CYAN;
colors_[level::debug] = CYAN;
colors_[level::info] = WHITE | BOLD;
colors_[level::warn] = YELLOW | BOLD;
colors_[level::err] = RED | BOLD; // red bold
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
colors_[level::off] = 0;
}
virtual ~wincolor_sink()
{
flush();
}
wincolor_sink(const wincolor_sink& other) = delete;
wincolor_sink& operator=(const wincolor_sink& other) = delete;
virtual void _sink_it(const details::log_msg& msg) override
{
auto color = colors_[msg.level];
auto orig_attribs = set_console_attribs(color);
WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
}
virtual void flush() override
{
// windows console always flushed?
}
// change the color for the given level
void set_color(level::level_enum level, WORD color)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
colors_[level] = color;
}
private:
HANDLE out_handle_;
std::map<level::level_enum, WORD> colors_;
// set color and return the orig console attributes (for resetting later)
WORD set_console_attribs(WORD attribs)
{
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
SetConsoleTextAttribute(out_handle_, attribs);
return orig_buffer_info.wAttributes; //return orig attribs
}
};
//
// windows color console to stdout
//
template<class Mutex>
class wincolor_stdout_sink: public wincolor_sink<Mutex>
{
public:
wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
{}
};
typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
//
// windows color console to stderr
//
template<class Mutex>
class wincolor_stderr_sink: public wincolor_sink<Mutex>
{
public:
wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
{}
};
typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
}
}

View File

@ -0,0 +1,178 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// spdlog main header file.
// see example.cpp for usage example
#pragma once
#define SPDLOG_VERSION "0.12.0"
#include <spdlog/tweakme.h>
#include <spdlog/common.h>
#include <spdlog/logger.h>
#include <memory>
#include <functional>
#include <chrono>
#include <string>
namespace spdlog
{
//
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
//
std::shared_ptr<logger> get(const std::string& name);
//
// Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
//
void set_pattern(const std::string& format_string);
void set_formatter(formatter_ptr f);
//
// Set global logging level for
//
void set_level(level::level_enum log_level);
//
// Set global error handler
//
void set_error_handler(log_err_handler);
//
// Turn on async mode (off by default) and set the queue size for each async_logger.
// effective only for loggers created after this call.
// queue_size: size of queue (must be power of 2):
// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction.
//
// async_overflow_policy (optional, block_retry by default):
// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
//
// worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
//
// worker_teardown_cb (optional):
// callback function that will be called in worker thread upon exit
//
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(), const std::function<void()>& worker_teardown_cb = nullptr);
// Turn off async mode
void set_sync_mode();
//
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitatons or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
//
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
//
// Create file logger which creates new file on the given time (default in midnight):
//
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
//
// 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);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
//
// Create and register a syslog logger
//
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif
// Create and register a logger a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
// 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 and register a logger with templated sink type
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
// Apply a user defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references from the registry
void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// 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.
// SPDLOG_TRACE(..) will also print current file and line.
//
// Example:
// spdlog::set_level(spdlog::level::trace);
// SPDLOG_TRACE(my_logger, "some trace message");
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#else
#define SPDLOG_TRACE(logger, ...)
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#else
#define SPDLOG_DEBUG(logger, ...)
#endif
}
#include <spdlog/details/spdlog_impl.h>

View File

@ -0,0 +1,108 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
///////////////////////////////////////////////////////////////////////////////
//
// Edit this file to squeeze more performance, and to customize supported features
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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 clock.
//
// #define SPDLOG_CLOCK_COARSE
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from quering the clock on each log call.
//
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
//
// #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.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
//
// #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
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels
// Use only if your code never modifies a logger's log levels concurrently by different threads.
//
// #define SPDLOG_NO_ATOMIC_LEVELS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows.
//
// #define SPDLOG_WCHAR_FILENAMES
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
//
// #define SPDLOG_EOL ";-)\n"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
// In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
//
// #define SPDLOG_FMT_EXTERNAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default)
//
// #define SPDLOG_ENABLE_SYSLOG
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors
//
// #define SPDLOG_PREVENT_CHILD_FD
///////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,19 @@
#
# Tests
#
enable_testing()
find_package(Threads)
# Build Catch unit tests
add_library(catch INTERFACE)
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
add_executable(catch_tests ${catch_tests})
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME catch_tests COMMAND catch_tests)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")

9427
share/spdlog/tests/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
#include<iostream>
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_pattern("%v");
logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
REQUIRE(count_lines(filename) == 1);
}
struct custom_ex {};
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
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)
{
throw custom_ex();
});
logger->info("Good message #1");
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
logger->info("Good message #2");
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("async_error_handler", "[errors]]")
{
prepare_logdir();
std::string err_msg("log failed with some msg");
spdlog::set_async_mode(128);
std::string filename = "logs/simple_async_log.txt";
{
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string& msg)
{
std::ofstream ofs("logs/custom_err.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
ofs << err_msg;
});
logger->info("Good message #1");
logger->info("Bad format msg {} {}", "xxx");
logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
}
REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
}

View File

@ -0,0 +1,78 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
using namespace spdlog::details;
static const std::string target_filename = "logs/file_helper_test.txt";
static void write_with_helper(file_helper &helper, size_t howmany)
{
log_msg msg;
msg.formatted << std::string(howmany, '1');
helper.write(msg);
helper.flush();
}
TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
{
prepare_logdir();
file_helper helper;
helper.open(target_filename);
REQUIRE(helper.filename() == target_filename);
}
TEST_CASE("file_helper_size", "[file_helper::size()]]")
{
prepare_logdir();
size_t expected_size = 123;
{
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
}
REQUIRE(get_filesize(target_filename) == expected_size);
}
TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{
prepare_logdir();
REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper;
helper.open(target_filename);
REQUIRE(file_helper::file_exists(target_filename));
}
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
{
prepare_logdir();
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, 12);
REQUIRE(helper.size() == 12);
helper.reopen(true);
REQUIRE(helper.size() == 0);
}
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{
prepare_logdir();
size_t expected_size = 14;
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size);
helper.reopen(false);
REQUIRE(helper.size() == expected_size);
}

View File

@ -0,0 +1,151 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
prepare_logdir();
std::string filename = "logs/simple_log";
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("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log";
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]]")
{
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
}
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);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
logger->flush();
REQUIRE(get_filesize(filename) <= 1024);
auto filename1 = basename + ".1";
REQUIRE(get_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}", 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);
logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
{
using sink_type = spdlog::sinks::daily_file_sink<
std::mutex,
spdlog::sinks::dateonly_daily_file_name_calculator>;
prepare_logdir();
//calculate filename (time based)
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
struct custom_daily_file_name_calculator
{
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
{
using sink_type = spdlog::sinks::daily_file_sink<
std::mutex,
custom_daily_file_name_calculator>;
prepare_logdir();
//calculate filename (time based)
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}

View File

@ -0,0 +1,56 @@
#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);
return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
}
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::info) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
}

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -0,0 +1,45 @@
#include "includes.h"
void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
auto rv = system("del /F /Q logs\\*");
#else
auto rv = system("rm -f logs/*");
#endif
(void)rv;
}
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>()));
}
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::size_t get_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 static_cast<std::size_t>(ifs.tellg());
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <string>
#include<cstddef>
std::size_t count_lines(const std::string& filename);
void prepare_logdir();
std::string file_contents(const std::string& filename);
std::size_t count_lines(const std::string& filename);
std::size_t get_filesize(const std::string& filename);

View File

@ -2,7 +2,7 @@ set(SOURCES
DatabaseConnection.cpp
main.cpp)
find_package( Boost 1.54.0 REQUIRED system )
INCLUDE_DIRECTORIES(${PostgreSQL_INCLUDE_DIRS})
add_executable(eedb ${SOURCES} )

View File

@ -1,15 +1,42 @@
#include "DatabaseConnection.hpp"
#include <eedb/EEDB.hpp>
#include <eedb/Session.hpp>
#include <eedb/auth/PgUserAuth.hpp>
#include <eedb/auth/Services.hpp>
#include <eedb/db/connection.hpp>
#include <eedb/widgets/AuthWidget.hpp>
#include <eedb/widgets/HomePage.hpp>
#include <eedb/widgets/Theme.hpp>
#include <Wt/WApplication>
#include <Wt/WServer>
namespace Wt {
class WContainerWidget;
}
Wt::WApplication * createApplication(const Wt::WEnvironment & env) {
using std::move;
auto dbConnection = eedb::DbConnection{}.create(env);
return new eedb::EEDB{env, std::move(dbConnection)};
auto userDatabase = std::make_unique< eedb::auth::PgUserAuth >(*dbConnection, env);
auto services = eedb::auth::Services();
auto session = std::make_unique< eedb::Session >(move(dbConnection), env, move(userDatabase));
auto theme = std::make_unique< eedb::BootstrapTheme >();
auto authPageFactory = [ services, _session = session.get() ](Wt::WContainerWidget * parent)->eedb::AuthPage * {
return new eedb::AuthPage(services, *_session, parent);
};
auto homePageFactory = [_session = session.get()](Wt::WContainerWidget * parent)->eedb::HomePage * {
return nullptr; // new eedb::HomePage();
};
return new eedb::EEDB{std::move(session), std::move(theme), authPageFactory, homePageFactory};
}
int main(int argc, char ** argv) {

View File

@ -1,5 +1,7 @@
file(GLOB SOURCE
EEDB.cpp
Session.cpp
auth/PgUserAuth.cpp
auth/Services.cpp
data/*

View File

@ -3,11 +3,12 @@
#include <Wt/WApplication>
#include <Wt/WEnvironment>
#include <eedb/Session.hpp>
#include <eedb/auth/PgUserAuth.hpp>
#include <eedb/auth/Services.hpp>
#include <eedb/db/connection.hpp>
#include <eedb/widgets/AuthWidget.hpp>
#include <eedb/widgets/MainWindow.hpp>
#include <eedb/widgets/HomePage.hpp>
#include <eedb/widgets/Theme.hpp>
#include <memory>
@ -25,36 +26,49 @@
#include <Wt/WText>
#include <Wt/WTextArea>
using std::move;
namespace eedb {
EEDB::EEDB(const Wt::WEnvironment & env, std::unique_ptr< eedb::db::PgConnection > _database)
: Wt::WApplication(env), _dbConnection(std::move(_database)) {
_login.changed().connect(this, &EEDB::authEvent);
_userDatabase = std::make_unique< eedb::auth::PgUserAuth >(*_dbConnection, env);
EEDB::EEDB(std::unique_ptr< eedb::Session > session,
std::unique_ptr< Theme > theme,
AuthPageFactory authPageFactory,
HomePageFactory homePageFactory)
: Wt::WApplication(session->enviroment()),
_session(move(session)),
_theme(move(theme)),
_authPageFactory(move(authPageFactory)),
_authPage(nullptr),
_homePageFactory(move(homePageFactory)),
_homePage(nullptr) {
// _login.changed().connect(this, &EEDB::authEvent);
// _userDatabase = std::make_unique< eedb::auth::PgUserAuth >(*_dbConnection, env);
root()->addStyleClass("container");
useStyleSheet("/resources/style.css");
setTheme(eedb::BootstrapTheme(this).create());
if(_theme)
setTheme(_theme->create());
_authWidget = new eedb::AuthWidget(eedb::auth::Services{}, *_userDatabase, _login);
root()->addWidget(_authWidget);
_authPage = _authPageFactory(root());
// _authWidget = new eedb::AuthPage(eedb::auth::Services{}, *_userDatabase, _login);
// root()->addWidget(_authWidget);
}
void EEDB::authEvent() {
using namespace Wt;
if(_login.loggedIn()) {
Wt::WContainerWidget * container = root();
setInternalPath("/app");
root()->removeStyleClass("container");
Wt::log("notice") << "Clearing root and creating widgets";
new eedb::Home(container, &_authWidget->login());
// if(_login.loggedIn()) {
// Wt::WContainerWidget * container = root();
// setInternalPath("/app");
// root()->removeStyleClass("container");
// Wt::log("notice") << "Clearing root and creating widgets";
// // new eedb::Home(container, &_authWidget->login());
} else {
Wt::log("notice") << "User logged out.";
redirect(url());
quit();
}
// } else {
// Wt::log("notice") << "User logged out.";
// redirect(url());
// quit();
// }
}
}

View File

@ -1,32 +1,38 @@
#include <functional>
#include <memory>
#include <Wt/WApplication>
#include <eedb/auth/PgUserAuth.hpp>
#include <eedb/widgets/AuthWidget.hpp>
// remove later
#include <Wt/Auth/Login>
namespace Wt {
class WEnvironment;
}
namespace eedb::db {
class PgConnection;
class WContainerWidget;
}
namespace eedb {
class AuthPage;
class HomePage;
class Session;
class Theme;
using AuthPageFactory = std::function< AuthPage *(Wt::WContainerWidget *) >;
using HomePageFactory = std::function< HomePage *(Wt::WContainerWidget *) >;
class EEDB : public Wt::WApplication {
public:
EEDB(const Wt::WEnvironment & env, std::unique_ptr< eedb::db::PgConnection > _database);
EEDB(std::unique_ptr< eedb::Session > session,
std::unique_ptr< Theme > theme,
AuthPageFactory authPageFactory,
HomePageFactory homePageFactory);
void authEvent();
private:
std::unique_ptr< eedb::db::PgConnection > _dbConnection;
std::unique_ptr< eedb::auth::PgUserAuth > _userDatabase;
Wt::Auth::Login _login;
eedb::AuthWidget * _authWidget;
std::unique_ptr< eedb::Session > _session;
std::unique_ptr< eedb::Theme > _theme;
AuthPageFactory _authPageFactory;
eedb::AuthPage * _authPage;
HomePageFactory _homePageFactory;
eedb::HomePage * _homePage;
};
}

27
src/eedb/Session.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <eedb/Session.hpp>
#include <eedb/auth/PgUserAuth.hpp>
#include <eedb/db/connection.hpp>
namespace eedb {
Session::Session(std::unique_ptr< eedb::db::PgConnection > dbConnection,
const Wt::WEnvironment & env,
std::unique_ptr< eedb::auth::PgUserAuth > usrDatabase)
: _dbConnection(std::move(dbConnection)), _env(env), _userDatabase(std::move(usrDatabase)) {}
db::PgConnection & Session::db() {
return *_dbConnection;
}
const Wt::WEnvironment & Session::enviroment() {
return _env;
}
Wt::Auth::AbstractUserDatabase & Session::userDatabase() {
return *_userDatabase;
}
Wt::Auth::Login & Session::login() {
return _login;
}
}

36
src/eedb/Session.hpp Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <Wt/Auth/Login>
#include <Wt/WEnvironment>
namespace eedb::auth {
class PgUserAuth;
}
namespace eedb::db {
class PgConnection;
};
namespace Wt::Auth {
class AbstractUserDatabase;
}
namespace eedb {
class Session {
public:
Session(std::unique_ptr< eedb::db::PgConnection > dbConnection,
const Wt::WEnvironment & env,
std::unique_ptr< eedb::auth::PgUserAuth > usrDatabase);
eedb::db::PgConnection & db();
const Wt::WEnvironment & enviroment();
Wt::Auth::AbstractUserDatabase & userDatabase();
Wt::Auth::Login & login();
private:
std::unique_ptr< eedb::db::PgConnection > _dbConnection;
std::unique_ptr< eedb::auth::PgUserAuth > _userDatabase;
Wt::Auth::Login _login;
const Wt::WEnvironment & _env;
};
}

View File

View File

@ -0,0 +1,30 @@
#include <Wt/WStandardItem>
#include <Wt/WStandardItemModel>
#include <boost/lexical_cast.hpp>
auto topLevelRows = 30;
auto secondLevelRows = 30;
namespace eedb {
class CategoriesModel final : public Wt::WStandardItemModel {
public:
CategoriesModel() : Wt::WStandardItemModel() {
auto model = this;
auto root = model->invisibleRootItem();
for(int row = 0; row < topLevelRows; ++row) {
Wt::WStandardItem * topLevel = new Wt::WStandardItem();
topLevel->setText("Item " + boost::lexical_cast< std::string >(row));
for(int row2 = 0; row2 < secondLevelRows; ++row2) {
Wt::WStandardItem * item = new Wt::WStandardItem();
item->setToolTip("this is a tooltip");
item->setText("Item " + boost::lexical_cast< std::string >(row) + ": " + boost::lexical_cast< std::string >(row2));
topLevel->appendRow(item);
}
root->appendRow(topLevel);
}
}
private:
};
}

View File

@ -1,10 +1,10 @@
#include <eedb/Session.hpp>
#include <eedb/auth/Services.hpp>
#include <eedb/widgets/AuthWidget.hpp>
namespace eedb {
AuthWidget::AuthWidget(
const auth::Services & baseAuth, Wt::Auth::AbstractUserDatabase & users, Wt::Auth::Login & login, Wt::WContainerWidget * parent)
: Wt::Auth::AuthWidget(*baseAuth.authService(), users, login, parent) {
AuthPage::AuthPage(const auth::Services & baseAuth, Session & session, Wt::WContainerWidget * parent)
: Wt::Auth::AuthWidget(*baseAuth.authService(), session.userDatabase(), session.login(), parent) {
this->model()->addPasswordAuth(eedb::auth::Services::passwordService());
this->model()->addOAuth(eedb::auth::Services::oAuthServices());
this->setRegistrationEnabled(true);

Some files were not shown because too many files have changed in this diff Show More