Compare commits

...

77 Commits

Author SHA1 Message Date
gabime
93d84e5d59 v0.12.0 2017-02-17 16:40:59 +02:00
Gabi Melman
311937815e Merge pull request #370 from jcelerier/fix/remove_static_global_strings
Replace static global std::string arrays by Meyer singletons.
2017-02-16 15:39:15 +02:00
Jean-Michaël Celerier
ef665e959f Don't deduce return types for days / months
Required for C++11 support
2017-02-16 13:17:13 +01:00
Gabi Melman
20f4428696 Update README.md 2017-02-16 02:12:50 +02:00
Gabi Melman
b65c7bad9f Update README.md 2017-02-16 02:10:18 +02:00
Jean-Michaël Celerier
06b8193a14 Add missing consts 2017-02-15 14:41:57 +01:00
Jean-Michaël Celerier
a626ebbbec Replace static global std::string arrays by Meyer singletons. This improves thread-safety. 2017-02-15 14:31:51 +01:00
Gabi Melman
054eb555ca Moved SPDLOG_VERSION macro to spdlog.h 2017-02-03 15:28:32 +02:00
Gabi Melman
4f50c5d143 Update spdlog.h 2017-02-03 15:27:06 +02:00
Gabi Melman
7481a8ecf7 Update common.h 2017-02-03 15:26:47 +02:00
Gabi Melman
a76a5cfc9c Update README.md 2017-02-03 15:19:23 +02:00
Gabi Melman
904bed92c3 Added SPDLOG_VERSION macro to common.h 2017-02-03 15:11:08 +02:00
Gabi Melman
8650c15749 Updated install options section 2017-02-03 14:44:03 +02:00
Gabi Melman
35865ee54e Merge pull request #348 from w0land/cmake_prefix
Add prefix for BUILD_TESTING cmake option
2017-01-11 11:29:15 +02:00
Bartosz Taczała
50c181ea4b Add prefix for BUILD_TESTING cmake option
This is helpful when using spdlog as a dependency (git submodule) when a
master project is not interested in spdlog tests. Using
"BUILD_TESTING" name may create a confusion.
Extra: BUILD_EXAMPLE variable already have a prefix.
2017-01-11 09:32:55 +01:00
Gabi Melman
2ec188041f Update README.md 2017-01-06 12:39:12 +02:00
Gabi Melman
e7ec922c0a Update async_log_helper.h
removed empty lines
2017-01-06 12:32:25 +02:00
Gabi Melman
38456118d0 Update null_sink.h 2016-12-31 22:38:00 +02:00
gabime
0a3a3f0ee2 Updated comment on thread safety 2016-12-31 17:54:37 +02:00
Gabi Melman
58853d9c95 Update README.md 2016-12-30 12:16:44 +02:00
Gabi Melman
5ee14906c5 Update README.md 2016-12-30 12:16:09 +02:00
gabime
2d873785a5 astyle 2016-12-10 02:05:05 +02:00
gabime
fec467da7b extra blank line between comments 2016-12-10 02:04:20 +02:00
gabime
b5f34c5320 typo in comment 2016-12-10 02:03:13 +02:00
gabime
92db8115b7 option to prevent child processes from inheriting log file desciptors (#define SPDLOG_PREVENT_CHILD_FD) 2016-12-10 01:43:43 +02:00
Gabi Melman
af35f9c086 Merge pull request #330 from PMExtra/master
Use the feature checking macros to detect thread_local support in clang.
2016-12-09 21:47:58 +02:00
Gabi Melman
0e016882d9 Update os.h 2016-12-06 19:47:33 +02:00
PM_Extra
b1a55ca3a4 Use the feature checking macros to detect thread_local support in clang. 2016-12-04 13:42:52 +08:00
Gabi Melman
68cc3e6856 Merge pull request #329 from PMExtra/master
fixed compilation error in clang before version 8
2016-12-02 20:24:12 +02:00
PM_Extra
f7574eb4c7 fixed compilation error in clang before version 8 (does not support thread_local keyword) 2016-12-03 01:40:52 +08:00
gabime
33494049a8 fixed compilation error in 2013 (does not support thread_local keyword) 2016-12-02 17:48:10 +02:00
gabime
5d23e88c16 astyle 2016-12-02 17:40:40 +02:00
gabime
6d8efa8d7f store thread_id in tls 2016-12-02 17:33:49 +02:00
gabime
343904b56d add missing include to widnows _getpid 2016-12-02 17:12:24 +02:00
gabime
cf73f02eaf pid support (added the %P option to formatter) 2016-12-02 17:09:00 +02:00
Gabi Melman
cda27d2bff Merge pull request #322 from onnodb/FixIssue321
Fix compilation errors in "wincolor_sink.h"
2016-11-25 17:13:55 +02:00
Onno Broekmans
b61be7320a Fix compilation errors in "wincolor_sink.h" 2016-11-25 15:47:21 +01:00
Gabi Melman
b2ce64625d Merge pull request #319 from jktjkt/cmake
cmake: misc improvements
2016-11-22 13:39:32 +02:00
Jan Kundrát
f058d3aa74 cmake: use -Wall on GCC and Clang
These checks come from [1]. The `MATCHES` operator is used for clang
because of Apple's special string.

[1] http://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang/10055571#10055571
2016-11-22 10:31:01 +01:00
Jan Kundrát
1c31800210 cmake: Use a standard option for controlling the tests
As per the docs [1], there's a standard variable for this purpose. This
introduces a behavior change, the tests are now being built by default.

[1] https://cmake.org/cmake/help/v3.0/module/CTest.html
2016-11-22 10:20:13 +01:00
Jan Kundrát
61cdd170fd cmake: List spdlog's content in IDEs
This is a usual CMake way of ensuring that IDEs have a way of showing
all source files which comprise this header-only library. It works in
the Qt Creator, for example.
2016-11-22 10:10:52 +01:00
gabime
d6b34d7b5c Updated to fmt version 796beaaddb5226162fe00c2c55e322d80d26f3d8 2016-11-19 23:43:39 +02:00
gabime
bd6fe569b5 astyle previous commits 2016-11-18 17:17:09 +02:00
gabime
d142f13551 Updated fmto to version def687462c32ec40757e49eb6069f109d50236d6 2016-11-18 17:13:53 +02:00
gabime
e12916c070 Fixed issue #317 (Build error on raspberry pi) 2016-11-18 16:58:57 +02:00
gabime
f6cece206a Merge 2016-11-18 16:58:30 +02:00
gabime
817371b225 Fix issue #317 (Build error on raspberry pi) 2016-11-18 16:56:25 +02:00
Gabi Melman
9eee823041 Fix issue #315 2016-11-14 14:58:10 +02:00
Gabi Melman
4e768c146b Merge pull request #314 from osx2000/master
Compatibility with Oracle Developer Studio 12.5 on  Solaris
2016-11-11 15:38:32 +02:00
osx2000
3cd497ee95 extended conditional compilation to __SUNPRO_CC 2016-11-11 14:28:45 +01:00
osx2000
e9fc4ac095 Fully qualified std::this_thread::yield() 2016-11-11 14:27:07 +01:00
Gabi Melman
9ccb6af2bd Merge pull request #305 from chenhayat/vs2013_fix
Fix Klockwork compilation warning
2016-11-03 16:30:25 +02:00
Chen Hayat
5259b3dbf4 Fix Klockwork compilation warning 2016-11-03 14:19:02 +02:00
Gabi Melman
8c67d6e05e Merge pull request #304 from chenhayat/vs2013_fix
Fix compilation error C2664  on VS2013
2016-11-02 15:59:34 +02:00
Chen Hayat
b4cb1febf2 removed external library changes 2016-11-02 15:43:30 +02:00
Chen Hayat
0c16b9ae1e Remove casting from previous commit and fix the following Klockwork issues:
1. Removing "return" from void functions.
2. Using "const" for operator= argument.
2016-11-01 17:16:07 +02:00
Chen Hayat
83d192b1f1 Fix compilation error C2664 on VS2013
No converting constructor
2016-10-30 17:11:45 +02:00
gabime
87ddb9a6c1 astyle 2016-10-20 12:14:25 +03:00
gabime
6128a87da2 Fix issue #300 2016-10-20 12:11:31 +03:00
Gabi Melman
698783861c Fixed vc warning on x64 build 2016-10-17 12:50:38 +03:00
gabime
f14d1c002b astyle 2016-10-12 23:08:44 +03:00
gabime
0cfdad4d0b Windows console color support. Replaced color param in API with new functions 2016-10-12 15:10:10 +03:00
Gabi Melman
94dbefe9b6 Merge pull request #296 from guoxiao/fix
Fix several compiler warnings
2016-10-09 11:39:49 +03:00
Guo Xiao
73e53c7cb6 Remove extra ';' 2016-10-09 15:36:19 +08:00
Guo Xiao
9b218d4d12 Convert off_t to size_t 2016-10-09 15:36:19 +08:00
Gabi Melman
541dd88a97 Update common.h 2016-10-09 01:55:47 +03:00
Gabi Melman
ca928bc1b0 Fix issue #295
vs 2013 not supports std::atomic_int
2016-10-09 00:41:29 +03:00
Gabi Melman
b642482432 removed empty line 2016-10-08 20:58:29 +03:00
gabime
703a1d9736 added g3log crush example 2016-10-01 18:15:21 +03:00
gabime
6ce507eceb bench: added latency comparison with g3log 2016-10-01 16:55:13 +03:00
gabime
56678a5f6a added set_force_flush(bool) to simple file sink for performance benchmarks 2016-10-01 16:37:33 +03:00
gabime
12800ac466 fixed shadow warnings in gcc 2016-09-30 14:08:31 +03:00
gabime
faa184ce24 Added #ifdef __ANDROID__ to spllog_impl.h 2016-09-29 23:49:03 +03:00
Gabi Melman
fa175d6300 Merge pull request #289 from thomas-frantz/expose_sinks
Exposed logger sinks.
2016-09-25 03:47:36 +03:00
Therenall
66b08294ca Exposed logger sinks. 2016-09-24 15:14:05 -04:00
Gabi Melman
9e37f5cef9 Merge pull request #285 from azadkuh/issue280
update os.h to fix filesize() on older win32
2016-09-20 13:14:44 +03:00
amir zamani
811eeef7a6 update os.h to fix filesize() on older win32
_fstat() always fails under older 32bit WinXP/Win2003 targets.

_filelength() just works for both WinXP SDK and later Win7+ 32bit targets.
2016-09-20 14:13:15 +04:30
37 changed files with 2178 additions and 1269 deletions

View File

@@ -5,14 +5,19 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0) project(spdlog VERSION 1.0.0)
include(CTest)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) 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) add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF) option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
option(SPDLOG_BUILD_TESTS "Build tests" OFF) option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON)
target_include_directories( target_include_directories(
spdlog spdlog
@@ -23,12 +28,11 @@ target_include_directories(
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
include(CTest)
if(SPDLOG_BUILD_EXAMPLES) if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example) add_subdirectory(example)
endif() endif()
if(SPDLOG_BUILD_TESTS) if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
@@ -78,3 +82,6 @@ install(
NAMESPACE "${namespace}" NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}" DESTINATION "${config_install_dir}"
) )
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})

View File

@@ -4,13 +4,24 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install ## Install
Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler #### Just copy or clone 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 ## Platforms
* Linux (gcc 4.8.1+, clang 3.5+) * Linux, FreeBSD, Solaris
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+) * Windows (vc 2013+, cygwin/mingw)
* Mac OSX (clang 3.5+) * Mac OSX (clang 3.5+)
* Solaris (gcc 5.2.0+)
* Android * Android
##Features ##Features
@@ -25,6 +36,7 @@ Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/inclu
* Daily log files. * Daily log files.
* Console logging (colors supported). * Console logging (colors supported).
* syslog. * syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). * 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. * Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
@@ -74,34 +86,25 @@ int main(int, char*[])
{ {
try try
{ {
// Multithreaded color console // Console logger with color
auto console = spd::stdout_logger_mt("console", true); auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->info("An info message example {}..", 1); console->error("Some error message with arg{}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); 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->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("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// 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..");
// Create basic file logger (not rotated) // Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt"); auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message"); my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files // 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); auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
@@ -109,14 +112,23 @@ int main(int, char*[])
// Create a daily logger - a new file is created every day on 2:30am // 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); 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); daily_logger->info(123.44);
// Customize msg format for all messages // Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format"); rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON // 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_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
@@ -124,22 +136,28 @@ int main(int, char*[])
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example(); async_example();
// syslog example. linux/osx only.. // syslog example. linux/osx only
syslog_example(); syslog_example();
// Log user-defined types example.. // android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example(); user_defined_example();
// Change default log error handler // Change default log error handler
err_handler_example(); err_handler_example();
// Apply a function on all registered loggers // Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->info("End of example."); }); spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers // Release and close all loggers
spdlog::drop_all(); spdlog::drop_all();
} }
// Exceptions will only be thrown upon failed logger or sink construction (not during logging) // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex) catch (const spd::spdlog_ex& ex)
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; std::cout << "Log init failed: " << ex.what() << std::endl;

View File

@@ -2,7 +2,6 @@
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>

32
bench/latency/Makefile Normal file
View File

@@ -0,0 +1,32 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -std=c++11 -pthread
CXX_RELEASE_FLAGS = -O2 -DNDEBUG
binaries=spdlog-latency g3log-latency g3log-crush
all: $(binaries)
spdlog-latency: spdlog-latency.cpp
$(CXX) spdlog-latency.cpp -o spdlog-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../include
g3log-latency: g3log-latency.cpp
$(CXX) g3log-latency.cpp -o g3log-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
g3log-crush: g3log-crush.cpp
$(CXX) g3log-crush.cpp -o g3log-crush $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
.PHONY: clean
clean:
rm -f *.o *.log $(binaries)
rebuild: clean all

13
bench/latency/compare.sh Executable file
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;
}

35
bench/latency/utils.h Normal file
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

@@ -22,28 +22,21 @@ int main(int, char*[])
{ {
try try
{ {
// Multithreaded color console // Console logger with color
auto console = spd::stdout_logger_mt("console", true); auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->error("An error message example {}..", 1); console->error("Some error message with arg{}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); 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->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("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// 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..");
// Create basic file logger (not rotated) // Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt"); auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
@@ -64,8 +57,15 @@ int main(int, char*[])
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format"); rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON // 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_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);

View File

@@ -42,6 +42,7 @@
<ClInclude Include="..\include\spdlog\sinks\sink.h" /> <ClInclude Include="..\include\spdlog\sinks\sink.h" />
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" /> <ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.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\spdlog.h" />
<ClInclude Include="..\include\spdlog\tweakme.h" /> <ClInclude Include="..\include\spdlog\tweakme.h" />
</ItemGroup> </ItemGroup>
@@ -55,13 +56,13 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>

View File

@@ -30,11 +30,11 @@
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated)) #define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated) #define SPDLOG_DEPRECATED __declspec(deprecated)
#else #else
#define DEPRECATED #define SPDLOG_DEPRECATED
#endif #endif
@@ -57,7 +57,7 @@ using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS) #if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int; using level_t = details::null_atomic_int;
#else #else
using level_t = std::atomic_int; using level_t = std::atomic<int>;
#endif #endif
using log_err_handler = std::function<void(const std::string &err_msg)>; using log_err_handler = std::function<void(const std::string &err_msg)>;

View File

@@ -67,7 +67,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
{} {}
async_msg(async_msg_type m_type) :msg_type(m_type) async_msg(async_msg_type m_type) :msg_type(m_type)
{}; {}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{ {
@@ -82,7 +82,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
// never copy or assign. should only be moved.. // never copy or assign. should only be moved..
async_msg(const async_msg&) = delete; async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete; async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg // construct from log_msg
async_msg(const details::log_msg& m) : async_msg(const details::log_msg& m) :
@@ -229,8 +229,6 @@ inline spdlog::details::async_log_helper::~async_log_helper()
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{ {
push_msg(async_msg(msg)); push_msg(async_msg(msg));
} }
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
@@ -246,7 +244,6 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
} }
while (!_q.enqueue(std::move(new_msg))); while (!_q.enqueue(std::move(new_msg)));
} }
} }
// optionally wait for the queue be empty and request flush from the sinks // optionally wait for the queue be empty and request flush from the sinks
@@ -281,10 +278,8 @@ inline void spdlog::details::async_log_helper::worker_loop()
// return true if this thread should still be active (while no terminate msg was received) // 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) 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; async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg)) if (_q.dequeue(incoming_async_msg))
{ {
last_pop = details::os::now(); last_pop = details::os::now();
@@ -359,8 +354,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
// yield upto 150 micros // yield upto 150 micros
if (time_since_op <= microseconds(100)) if (time_since_op <= microseconds(100))
return yield(); return std::this_thread::yield();
// sleep for 20 ms upto 200 ms // sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200)) if (time_since_op <= milliseconds(200))
@@ -378,13 +372,7 @@ inline void spdlog::details::async_log_helper::wait_empty_q()
{ {
sleep_or_yield(details::os::now(), last_op); sleep_or_yield(details::os::now(), last_op);
} }
} }

View File

@@ -31,13 +31,13 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
} }
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks, sinks_init_list sinks_list,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms, const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) : const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} 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, inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink, sink_ptr single_sink,

View File

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

View File

@@ -10,10 +10,12 @@
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono>
#include <thread>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32 #ifdef _WIN32
@@ -25,27 +27,32 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle support
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
#endif #endif
#include <sys/types.h> #else // unix
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <unistd.h> #include <unistd.h>
#include <chrono> #include <fcntl.h>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__ #elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id #include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#else
#include <thread>
#endif #endif
#endif //unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog namespace spdlog
{ {
namespace details namespace details
@@ -135,6 +142,18 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; 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 //fopen_s on non windows for writing
@@ -146,13 +165,18 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else #else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif #endif
return *fp == nullptr; #else //unix
#else
*fp = fopen((filename.c_str()), mode.c_str()); *fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif #endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if(*fp != nullptr)
prevent_child_fd(*fp);
#endif
return *fp == nullptr;
} }
inline int remove(const filename_t &filename) inline int remove(const filename_t &filename)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -204,9 +228,9 @@ inline size_t filesize(FILE *f)
return st.st_size; return st.st_size;
#else //windows 32 bits #else //windows 32 bits
struct _stat st; long ret = _filelength(fd);
if (_fstat(fd, &st) == 0) if (ret >= 0)
return st.st_size; return static_cast<size_t>(ret);
#endif #endif
#else // unix #else // unix
@@ -215,11 +239,11 @@ inline size_t filesize(FILE *f)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
return st.st_size; return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx #else // unix 32 bits or osx
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
return st.st_size; return static_cast<size_t>(st.st_size);
#endif #endif
#endif #endif
throw spdlog_ex("Failed getting file size from fd", errno); throw spdlog_ex("Failed getting file size from fd", errno);
@@ -292,7 +316,7 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
//Return current thread id as size_t //Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) //It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
@@ -308,10 +332,21 @@ inline size_t thread_id()
#else //Default to standard C++11 (OSX and other Unix) #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())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #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) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -342,8 +377,8 @@ inline std::string errno_str(int err_num)
else else
return "Unkown error"; return "Unkown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \ #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0) if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf); return std::string(buf);
@@ -355,6 +390,17 @@ inline std::string errno_str(int err_num)
#endif #endif
} }
inline int pid()
{
#ifdef _WIN32
return ::_getpid();
#else
return static_cast<int>(::getpid());
#endif
}
} //os } //os
} //details } //details
} //spdlog } //spdlog

View File

@@ -18,6 +18,7 @@
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <array>
namespace spdlog namespace spdlog
{ {
@@ -78,42 +79,60 @@ static int to12h(const tm& t)
} }
//Abbreviated weekday name //Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 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 class a_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << days[tm_time.tm_wday]; msg.formatted << days()[tm_time.tm_wday];
} }
}; };
//Full weekday name //Full weekday name
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; 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 class A_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << full_days[tm_time.tm_wday]; msg.formatted << full_days()[tm_time.tm_wday];
} }
}; };
//Abbreviated month //Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; 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 class b_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << months[tm_time.tm_mon]; msg.formatted << months()[tm_time.tm_mon];
} }
}; };
//Full month name //Full month name
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; 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 class B_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << full_months[tm_time.tm_mon]; msg.formatted << full_months()[tm_time.tm_mon];
} }
}; };
@@ -138,7 +157,7 @@ class c_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override 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 << ' '; 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; pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
} }
}; };
@@ -356,7 +375,7 @@ private:
//Thread id // Thread id
class t_formatter:public flag_formatter class t_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override void format(details::log_msg& msg, const std::tm&) override
@@ -365,6 +384,15 @@ class t_formatter:public flag_formatter
} }
}; };
// 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 class v_formatter:public flag_formatter
{ {
@@ -453,6 +481,8 @@ class full_formatter:public flag_formatter
} }
}; };
} }
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -611,6 +641,10 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break; break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
default: //Unkown flag appears as is 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('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag))); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));

View File

@@ -12,9 +12,20 @@
#include <spdlog/details/registry.h> #include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h> #include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h> #include <spdlog/sinks/stdout_sinks.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h> #include <spdlog/sinks/syslog_sink.h>
#endif
#ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h>
#else
#include <spdlog/sinks/ansicolor_sink.h> #include <spdlog/sinks/ansicolor_sink.h>
#endif
#ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h> #include <spdlog/sinks/android_sink.h>
#endif
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@@ -69,34 +80,86 @@ inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute); return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
} }
// Create stdout/stderr loggers (with optinal color support)
inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color) //
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{ {
if (color) //use color wrapper sink return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink); }
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); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color) inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{ {
return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color); 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::stdout_logger_st(const std::string& logger_name, bool color) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{ {
return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color); 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_logger_mt(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{ {
return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color); auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color) #else //ansi terminal colors
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{ {
return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color); 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 #ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger // 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) inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
@@ -105,7 +168,7 @@ inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string&
} }
#endif #endif
#if defined(__ANDROID__) #ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag) inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{ {
return create<spdlog::sinks::android_sink>(logger_name, tag); return create<spdlog::sinks::android_sink>(logger_name, tag);

View File

@@ -1,33 +1,31 @@
/* /*
Formatting library for C++ Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this 1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, 2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 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 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
// Commented out by spdlog to use header only #include "format.h"
// #include "fmt/format.h"
// #include "fmt/printf.h"
#include <string.h> #include <string.h>
@@ -73,31 +71,37 @@ using fmt::internal::Arg;
// Dummy implementations of strerror_r and strerror_s called if corresponding // Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available. // system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) { static inline fmt::internal::Null<> strerror_r(int, char *, ...)
return fmt::internal::Null<>(); {
return fmt::internal::Null<>();
} }
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...)
return fmt::internal::Null<>(); {
return fmt::internal::Null<>();
} }
namespace fmt { namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {} FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT
FMT_FUNC FormatError::~FormatError() throw() {} {}
FMT_FUNC SystemError::~SystemError() throw() {} FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT
{}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT
{}
namespace { namespace {
#ifndef _MSC_VER #ifndef _MSC_VER
# define FMT_SNPRINTF snprintf # define FMT_SNPRINTF snprintf
#else // _MSC_VER #else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...)
va_list args; {
va_start(args, format); va_list args;
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); va_start(args, format);
va_end(args); int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
return result; va_end(args);
} return result;
}
# define FMT_SNPRINTF fmt_snprintf # define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER #endif // _MSC_VER
@@ -107,166 +111,183 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
# define FMT_SWPRINTF swprintf # define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m"; const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef); typedef void(*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror. // Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code. // Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer, // This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string. // or a pointer to some static immutable string.
// Returns one of the following values: // Returns one of the following values:
// 0 - success // 0 - success
// ERANGE - buffer is not large enough to store the error message // ERANGE - buffer is not large enough to store the error message
// other - failure // other - failure
// Buffer should be at least of size 1. // Buffer should be at least of size 1.
int safe_strerror( int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError { class StrError
private: {
int error_code_; private:
char *&buffer_; int error_code_;
std::size_t buffer_size_; char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings. // A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {} void operator=(const StrError &)
{}
// Handle the result of XSI-compliant version of strerror_r. // Handle the result of XSI-compliant version of strerror_r.
int handle(int result) { int handle(int result)
// glibc versions before 2.13 return result in errno. {
return result == -1 ? errno : 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. // Handle the result of GNU-specific version of strerror_r.
int handle(char *message) { int handle(char *message)
// If the buffer is full then the message is probably truncated. {
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) // If the buffer is full then the message is probably truncated.
return ERANGE; if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
buffer_ = message; return ERANGE;
return 0; buffer_ = message;
} return 0;
}
// Handle the case when strerror_r is not available. // Handle the case when strerror_r is not available.
int handle(internal::Null<>) { int handle(internal::Null<>)
return fallback(strerror_s(buffer_, buffer_size_, error_code_)); {
} return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available. // Fallback to strerror_s when strerror_r is not available.
int fallback(int result) { int fallback(int result)
// If the buffer is full then the message is probably truncated. {
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? // If the buffer is full then the message is probably truncated.
ERANGE : result; return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
} ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available. // Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) { int fallback(internal::Null<>)
errno = 0; {
buffer_ = strerror(error_code_); errno = 0;
return errno; buffer_ = strerror(error_code_);
} return errno;
}
public: public:
StrError(int err_code, char *&buf, std::size_t buf_size) StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} : error_code_(err_code), buffer_(buf), buffer_size_(buf_size)
{}
int run() { int run()
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. {
return handle(strerror_r(error_code_, buffer_, buffer_size_)); // 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(); }
} };
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code, void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT { StringRef message) FMT_NOEXCEPT
// Report error code making sure that the output fits into {
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential // Report error code making sure that the output fits into
// bad_alloc. // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
out.clear(); // bad_alloc.
static const char SEP[] = ": "; out.clear();
static const char ERROR_STR[] = "error "; static const char SEP[] = ": ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR. static const char ERROR_STR[] = "error ";
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
typedef internal::IntTraits<int>::MainType MainType; std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
MainType abs_value = static_cast<MainType>(error_code); typedef internal::IntTraits<int>::MainType MainType;
if (internal::is_negative(error_code)) { MainType abs_value = static_cast<MainType>(error_code);
abs_value = 0 - abs_value; if (internal::is_negative(error_code)) {
++error_code_size; 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) error_code_size += internal::count_digits(abs_value);
out << message << SEP; if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << ERROR_STR << error_code; out << message << SEP;
assert(out.size() <= internal::INLINE_BUFFER_SIZE); out << ERROR_STR << error_code;
} assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code, void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT { StringRef message) FMT_NOEXCEPT
MemoryWriter full_message; {
func(full_message, error_code, message); MemoryWriter full_message;
// Use Writer::data instead of Writer::c_str to avoid potential memory func(full_message, error_code, message);
// allocation. // Use Writer::data instead of Writer::c_str to avoid potential memory
std::fwrite(full_message.data(), full_message.size(), 1, stderr); // allocation.
std::fputc('\n', stderr); std::fwrite(full_message.data(), full_message.size(), 1, stderr);
} std::fputc('\n', stderr);
} // namespace }
} // namespace
namespace internal { namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0. // This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0. // It can be removed in 4.0.
FMT_FUNC void format_system_error( FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
fmt::format_system_error(out, error_code, message); {
} fmt::format_system_error(out, error_code, message);
} // namespace internal }
} // namespace internal
FMT_FUNC void SystemError::init( FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) { int err_code, CStringRef format_str, ArgList args)
error_code_ = err_code; {
MemoryWriter w; error_code_ = err_code;
format_system_error(w, err_code, format(format_str, args)); MemoryWriter w;
std::runtime_error &base = *this; format_system_error(w, err_code, format(format_str, args));
base = std::runtime_error(w.str()); std::runtime_error &base = *this;
} base = std::runtime_error(w.str());
}
template <typename T> template <typename T>
int internal::CharTraits<char>::format_float( int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) { unsigned width, int precision, T value)
if (width == 0) { {
return precision < 0 ? if (width == 0) {
FMT_SNPRINTF(buffer, size, format, value) : return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, precision, value); FMT_SNPRINTF(buffer, size, format, value) :
} FMT_SNPRINTF(buffer, size, format, precision, value);
return precision < 0 ? }
FMT_SNPRINTF(buffer, size, format, width, value) : return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, precision, value); FMT_SNPRINTF(buffer, size, format, width, value) :
} FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T> template <typename T>
int internal::CharTraits<wchar_t>::format_float( int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) { unsigned width, int precision, T value)
if (width == 0) { {
return precision < 0 ? if (width == 0) {
FMT_SWPRINTF(buffer, size, format, value) : return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, precision, value); FMT_SWPRINTF(buffer, size, format, value) :
} FMT_SWPRINTF(buffer, size, format, precision, value);
return precision < 0 ? }
FMT_SWPRINTF(buffer, size, format, width, value) : return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, precision, value); FMT_SWPRINTF(buffer, size, format, width, value) :
} FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T> template <typename T>
const char internal::BasicData<T>::DIGITS[] = const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819" "0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839" "2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859" "4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879" "6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"; "8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \ #define FMT_POWERS_OF_10(factor) \
factor * 10, \ factor * 10, \
@@ -279,274 +300,279 @@ const char internal::BasicData<T>::DIGITS[] =
factor * 100000000, \ factor * 100000000, \
factor * 1000000000 factor * 1000000000
template <typename T> template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = { const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1) 0, FMT_POWERS_OF_10(1)
}; };
template <typename T> template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = { const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0, 0,
FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)), FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant // Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long. // to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10 ULongLong(1000000000) * ULongLong(1000000000) * 10
}; };
FMT_FUNC void internal::report_unknown_type(char code, const char *type) { FMT_FUNC void internal::report_unknown_type(char code, const char *type)
(void)type; {
if (std::isprint(static_cast<unsigned char>(code))) { (void)type;
FMT_THROW(FormatError( if (std::isprint(static_cast<unsigned char>(code))) {
format("unknown format code '{}' for {}", code, type))); FMT_THROW(FormatError(
} format("unknown format code '{}' for {}", code, type)));
FMT_THROW(FormatError( }
format("unknown format code '\\x{:02x}' for {}", FMT_THROW(FormatError(
static_cast<unsigned>(code), type))); format("unknown format code '\\x{:02x}' for {}",
} static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H #if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { 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) static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); if (s.size() > INT_MAX)
int s_size = static_cast<int>(s.size()); FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int length = MultiByteToWideChar( int s_size = static_cast<int>(s.size());
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0); int length = MultiByteToWideChar(
if (length == 0) CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); if (length == 0)
buffer_.resize(length + 1); FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
length = MultiByteToWideChar( buffer_.resize(length + 1);
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); length = MultiByteToWideChar(
if (length == 0) CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); if (length == 0)
buffer_[length] = 0; FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
} buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s)
if (int error_code = convert(s)) { {
FMT_THROW(WindowsError(error_code, if (int error_code = convert(s)) {
"cannot convert string from UTF-16 to UTF-8")); FMT_THROW(WindowsError(error_code,
} "cannot convert string from UTF-16 to UTF-8"));
} }
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s)
if (s.size() > INT_MAX) {
return ERROR_INVALID_PARAMETER; if (s.size() > INT_MAX)
int s_size = static_cast<int>(s.size()); return ERROR_INVALID_PARAMETER;
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); int s_size = static_cast<int>(s.size());
if (length == 0) int length = WideCharToMultiByte(
return GetLastError(); CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
buffer_.resize(length + 1); if (length == 0)
length = WideCharToMultiByte( return GetLastError();
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); buffer_.resize(length + 1);
if (length == 0) length = WideCharToMultiByte(
return GetLastError(); CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
buffer_[length] = 0; if (length == 0)
return 0; return GetLastError();
} buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init( FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) { int err_code, CStringRef format_str, ArgList args)
error_code_ = err_code; {
MemoryWriter w; error_code_ = err_code;
internal::format_windows_error(w, err_code, format(format_str, args)); MemoryWriter w;
std::runtime_error &base = *this; internal::format_windows_error(w, err_code, format(format_str, args));
base = std::runtime_error(w.str()); std::runtime_error &base = *this;
} base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error( FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
FMT_TRY { {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer; FMT_TRY{
buffer.resize(INLINE_BUFFER_SIZE); MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
for (;;) { buffer.resize(INLINE_BUFFER_SIZE);
wchar_t *system_message = &buffer[0]; for (;;) {
int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, wchar_t *system_message = &buffer[0];
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), int result = FormatMessageW(
system_message, static_cast<uint32_t>(buffer.size()), 0); FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
if (result != 0) { FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
UTF16ToUTF8 utf8_message; system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (utf8_message.convert(system_message) == ERROR_SUCCESS) { if (result != 0) {
out << message << ": " << utf8_message; UTF16ToUTF8 utf8_message;
return; if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
} out << message << ": " << utf8_message;
break; return;
} }
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break;
break; // Can't get error message, report error code instead. }
buffer.resize(buffer.size() * 2); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
} break; // Can't get error message, report error code instead.
} FMT_CATCH(...) {} buffer.resize(buffer.size() * 2);
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. }
} } FMT_CATCH(...)
{}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H #endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error( FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { Writer &out, int error_code, StringRef message) FMT_NOEXCEPT
FMT_TRY { {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer; FMT_TRY{
buffer.resize(internal::INLINE_BUFFER_SIZE); internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
for (;;) { buffer.resize(internal::INLINE_BUFFER_SIZE);
char *system_message = &buffer[0]; for (;;) {
int result = safe_strerror(error_code, system_message, buffer.size()); char *system_message = &buffer[0];
if (result == 0) { int result = safe_strerror(error_code, system_message, buffer.size());
out << message << ": " << system_message; if (result == 0) {
return; out << message << ": " << system_message;
} return;
if (result != ERANGE) }
break; // Can't get error message, report error code instead. if (result != ERANGE)
buffer.resize(buffer.size() * 2); 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. } FMT_CATCH(...)
} {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char> template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) { void internal::ArgMap<Char>::init(const ArgList &args)
if (!map_.empty()) {
return; if (!map_.empty())
typedef internal::NamedArg<Char> NamedArg; return;
const NamedArg *named_arg = 0; typedef internal::NamedArg<Char> NamedArg;
bool use_values = const NamedArg *named_arg = FMT_NULL;
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; bool use_values =
if (use_values) { args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
for (unsigned i = 0;/*nothing*/; ++i) { if (use_values) {
internal::Arg::Type arg_type = args.type(i); for (unsigned i = 0;/*nothing*/; ++i) {
switch (arg_type) { internal::Arg::Type arg_type = args.type(i);
case internal::Arg::NONE: switch (arg_type) {
return; case internal::Arg::NONE:
case internal::Arg::NAMED_ARG: return;
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer); case internal::Arg::NAMED_ARG:
map_.push_back(Pair(named_arg->name, *named_arg)); named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
break; map_.push_back(Pair(named_arg->name, *named_arg));
default: break;
/*nothing*/; default:
} /*nothing*/;
} }
return; }
} return;
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { }
internal::Arg::Type arg_type = args.type(i); for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
if (arg_type == internal::Arg::NAMED_ARG) { internal::Arg::Type arg_type = args.type(i);
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer); if (arg_type == internal::Arg::NAMED_ARG) {
map_.push_back(Pair(named_arg->name, *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) { for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
case internal::Arg::NONE: switch (args.args_[i].type) {
return; case internal::Arg::NONE:
case internal::Arg::NAMED_ARG: return;
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer); case internal::Arg::NAMED_ARG:
map_.push_back(Pair(named_arg->name, *named_arg)); named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
break; map_.push_back(Pair(named_arg->name, *named_arg));
default: break;
/*nothing*/; default:
} /*nothing*/;
} }
} }
}
template <typename Char> template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) { void internal::FixedBuffer<Char>::grow(std::size_t)
FMT_THROW(std::runtime_error("buffer overflow")); {
} FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg( FMT_FUNC Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) { unsigned arg_index, const char *&error)
Arg arg = args_[arg_index]; {
switch (arg.type) { Arg arg = args_[arg_index];
case Arg::NONE: switch (arg.type) {
error = "argument index out of range"; case Arg::NONE:
break; error = "argument index out of range";
case Arg::NAMED_ARG: break;
arg = *static_cast<const internal::Arg*>(arg.pointer); case Arg::NAMED_ARG:
break; arg = *static_cast<const internal::Arg*>(arg.pointer);
default: break;
/*nothing*/; default:
} /*nothing*/;
return arg; }
} return arg;
}
FMT_FUNC void report_system_error( FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT { int error_code, fmt::StringRef message) FMT_NOEXCEPT
// 'fmt::' is for bcc32. {
report_error(format_system_error, error_code, message); // 'fmt::' is for bcc32.
} report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H #if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error( FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT { int error_code, fmt::StringRef message) FMT_NOEXCEPT
// 'fmt::' is for bcc32. {
report_error(internal::format_windows_error, error_code, message); // 'fmt::' is for bcc32.
} report_error(internal::format_windows_error, error_code, message);
}
#endif #endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args)
MemoryWriter w; {
w.write(format_str, args); MemoryWriter w;
std::fwrite(w.data(), 1, w.size(), f); w.write(format_str, args);
} std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) { FMT_FUNC void print(CStringRef format_str, ArgList args)
print(stdout, format_str, args); {
} print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args)
char escape[] = "\x1b[30m"; {
escape[3] = static_cast<char>('0' + c); char escape[] = "\x1b[30m";
std::fputs(escape, stdout); escape[3] = static_cast<char>('0' + c);
print(format, args); std::fputs(escape, stdout);
std::fputs(RESET_COLOR, stdout); print(format, args);
} std::fputs(RESET_COLOR, stdout);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>; template struct internal::BasicData<void>;
// Explicit instantiations for char. // Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t); template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args); template void internal::ArgMap<char>::init(const ArgList &args);
template void PrintfFormatter<char>::format(CStringRef format); 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( template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value); unsigned width, int precision, long double value);
template int internal::CharTraits<char>::format_float( // Explicit instantiations for wchar_t.
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::FixedBuffer<wchar_t>::grow(std::size_t); template void internal::ArgMap<wchar_t>::init(const ArgList &args);
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 void PrintfFormatter<wchar_t>::format(WCStringRef format); template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
template int internal::CharTraits<wchar_t>::format_float( unsigned width, int precision, long double value);
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 #endif // FMT_HEADER_ONLY

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,17 +1,17 @@
/* /*
Formatting library for C++ - std::ostream support Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved. All rights reserved.
For the license information refer to format.h. For the license information refer to format.h.
*/ */
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
// Commented out by spdlog to use header only // commented out by spdlog
// #include "fmt/format.h" // #include "format.h"
#include <ostream> #include <ostream>
namespace fmt namespace fmt
@@ -21,7 +21,7 @@ namespace internal
{ {
template <class Char> template <class Char>
class FormatBuf : public std::basic_streambuf<Char> class FormatBuf: public std::basic_streambuf<Char>
{ {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
@@ -31,7 +31,7 @@ private:
Char *start_; Char *start_;
public: public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) FormatBuf(Buffer<Char> &buffer): buffer_(buffer), start_(&buffer[0])
{ {
this->setp(start_, start_ + buffer_.capacity()); this->setp(start_, start_ + buffer_.capacity());
} }
@@ -46,7 +46,7 @@ public:
start_ = &buffer_[0]; start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch); start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2); this->setp(start_ + buf_size + 1, start_ + buf_size * 2);
} }
return ch; return ch;
} }
@@ -59,7 +59,7 @@ public:
Yes &convert(std::ostream &); Yes &convert(std::ostream &);
struct DummyStream : std::ostream struct DummyStream: std::ostream
{ {
DummyStream(); // Suppress a bogus warning in MSVC. DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream. // Hide all operator<< overloads from std::ostream.
@@ -99,14 +99,14 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
} }
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
**Example**:: **Example**::
print(cerr, "Don't {}!", "panic"); print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef) FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt

View File

@@ -1,11 +1,11 @@
/* /*
Formatting library for C++ Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved. All rights reserved.
For the license information refer to format.h. For the license information refer to format.h.
*/ */
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
@@ -13,7 +13,7 @@
#include <algorithm> // std::fill_n #include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include "fmt/ostream.h" #include "ostream.h"
namespace fmt namespace fmt
{ {
@@ -52,7 +52,7 @@ struct IntChecker<true>
} }
}; };
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> class PrecisionHandler: public ArgVisitor<PrecisionHandler, int>
{ {
public: public:
void report_unhandled_arg() void report_unhandled_arg()
@@ -70,7 +70,7 @@ public:
}; };
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> class IsZeroInt: public ArgVisitor<IsZeroInt, bool>
{ {
public: public:
template <typename T> template <typename T>
@@ -83,13 +83,19 @@ public:
template <typename T, typename U> template <typename T, typename U>
struct is_same struct is_same
{ {
enum { value = 0 }; enum
{
value = 0
};
}; };
template <typename T> template <typename T>
struct is_same<T, T> struct is_same<T, T>
{ {
enum { value = 1 }; enum
{
value = 1
};
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
@@ -97,7 +103,7 @@ struct is_same<T, T>
// corresponding signed or unsigned type depending on the type specifier: // corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> class ArgConverter: public ArgVisitor<ArgConverter<T>, void>
{ {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
@@ -107,7 +113,8 @@ private:
public: public:
ArgConverter(internal::Arg &arg, wchar_t type) ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {} : arg_(arg), type_(type)
{}
void visit_bool(bool value) void visit_bool(bool value)
{ {
@@ -158,7 +165,7 @@ public:
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> class CharConverter: public ArgVisitor<CharConverter, void>
{ {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
@@ -166,7 +173,8 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {} explicit CharConverter(internal::Arg &arg): arg_(arg)
{}
template <typename T> template <typename T>
void visit_any_int(T value) void visit_any_int(T value)
@@ -178,7 +186,7 @@ public:
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> class WidthHandler: public ArgVisitor<WidthHandler, unsigned>
{ {
private: private:
FormatSpec &spec_; FormatSpec &spec_;
@@ -186,7 +194,8 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public: public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} explicit WidthHandler(FormatSpec &spec): spec_(spec)
{}
void report_unhandled_arg() void report_unhandled_arg()
{ {
@@ -212,24 +221,24 @@ public:
} // namespace internal } // namespace internal
/** /**
\rst \rst
A ``printf`` argument formatter based on the `curiously recurring template A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_. pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass ``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, will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called. superclass will be called.
\endrst \endrst
*/ */
template <typename Impl, typename Char> template <typename Impl, typename Char>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char> class BasicPrintfArgFormatter: public internal::ArgFormatterBase<Impl, Char>
{ {
private: private:
void write_null_pointer() void write_null_pointer()
@@ -242,14 +251,15 @@ private:
public: public:
/** /**
\rst \rst
Constructs an argument formatter object. Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format *writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types. specifier information for standard argument types.
\endrst \endrst
*/ */
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec) BasicPrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {} : internal::ArgFormatterBase<Impl, Char>(w, s)
{}
/** Formats an argument of type ``bool``. */ /** Formats an argument of type ``bool``. */
void visit_bool(bool value) void visit_bool(bool value)
@@ -315,7 +325,7 @@ public:
void visit_custom(internal::Arg::CustomValue c) void visit_custom(internal::Arg::CustomValue c)
{ {
BasicFormatter<Char> formatter(ArgList(), this->writer()); BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0}; const Char format_str[] = { '}', 0 };
const Char *format = format_str; const Char *format = format_str;
c.format(&formatter, c.value, &format); c.format(&formatter, c.value, &format);
} }
@@ -329,12 +339,13 @@ class PrintfArgFormatter
public: public:
/** Constructs an argument formatter object. */ /** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {} : BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s)
{}
}; };
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> > template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase class PrintfFormatter: private internal::FormatterBase
{ {
private: private:
BasicWriter<Char> &writer_; BasicWriter<Char> &writer_;
@@ -352,14 +363,15 @@ private:
public: public:
/** /**
\rst \rst
Constructs a ``PrintfFormatter`` object. References to the arguments and Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have the writer are stored in the formatter object so make sure they have
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w) explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {} : FormatterBase(al), writer_(w)
{}
/** Formats stored arguments and writes the output to the writer. */ /** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str); FMT_API void format(BasicCStringRef<Char> format_str);
@@ -399,7 +411,7 @@ internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) unsigned arg_index)
{ {
(void)s; (void)s;
const char *error = 0; const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error) if (error)
@@ -569,13 +581,13 @@ void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
} }
/** /**
\rst \rst
Formats arguments and returns the result as a string. Formats arguments and returns the result as a string.
**Example**:: **Example**::
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
inline std::string sprintf(CStringRef format, ArgList args) inline std::string sprintf(CStringRef format, ArgList args)
{ {
@@ -594,26 +606,26 @@ inline std::wstring sprintf(WCStringRef format, ArgList args)
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/** /**
\rst \rst
Prints formatted data to the file *f*. Prints formatted data to the file *f*.
**Example**:: **Example**::
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/** /**
\rst \rst
Prints formatted data to ``stdout``. Prints formatted data to ``stdout``.
**Example**:: **Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
inline int printf(CStringRef format, ArgList args) inline int printf(CStringRef format, ArgList args)
{ {
return fprintf(stdout, format, args); return fprintf(stdout, format, args);
@@ -621,14 +633,14 @@ inline int printf(CStringRef format, ArgList args)
FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, printf, CStringRef)
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
**Example**:: **Example**::
fprintf(cerr, "Don't %s!", "panic"); fprintf(cerr, "Don't %s!", "panic");
\endrst \endrst
*/ */
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{ {
MemoryWriter w; MemoryWriter w;
@@ -639,4 +651,8 @@ inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

View File

@@ -5,7 +5,7 @@
#pragma once #pragma once
// Thread safe logger // 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 // Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger: // Upon each log write the logger:
// 1. Checks if its log level is enough to log the message // 1. Checks if its log level is enough to log the message
@@ -68,6 +68,8 @@ public:
virtual void flush(); virtual void flush();
const std::vector<sink_ptr>& sinks() const;
protected: protected:
virtual void _sink_it(details::log_msg&); virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&); virtual void _set_pattern(const std::string&);
@@ -90,5 +92,3 @@ protected:
} }
#include <spdlog/details/logger_impl.h> #include <spdlog/details/logger_impl.h>

View File

@@ -33,7 +33,7 @@ public:
virtual void log(const details::log_msg& msg) override; virtual void log(const details::log_msg& msg) override;
virtual void flush() override; virtual void flush() override;
void set_color(level::level_enum level, const std::string& color); void set_color(level::level_enum color_level, const std::string& color);
/// Formatting codes /// Formatting codes
const std::string reset = "\033[00m"; const std::string reset = "\033[00m";
@@ -101,9 +101,9 @@ inline void ansicolor_sink::flush()
sink_->flush(); sink_->flush();
} }
inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color) inline void ansicolor_sink::set_color(level::level_enum color_level, const std::string& color)
{ {
colors_[level] = color; colors_[color_level] = color;
} }
inline ansicolor_sink::~ansicolor_sink() inline ansicolor_sink::~ansicolor_sink()

View File

@@ -29,7 +29,7 @@ template<class Mutex>
class simple_file_sink : public base_sink < Mutex > class simple_file_sink : public base_sink < Mutex >
{ {
public: public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false) explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{ {
_file_helper.open(filename, truncate); _file_helper.open(filename, truncate);
} }
@@ -37,14 +37,21 @@ public:
{ {
_file_helper.flush(); _file_helper.flush();
} }
void set_force_flush(bool force_flush)
{
_force_flush = force_flush;
}
protected: protected:
void _sink_it(const details::log_msg& msg) override void _sink_it(const details::log_msg& msg) override
{ {
_file_helper.write(msg); _file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
} }
private: private:
details::file_helper _file_helper; details::file_helper _file_helper;
bool _force_flush;
}; };
typedef simple_file_sink<std::mutex> simple_file_sink_mt; typedef simple_file_sink<std::mutex> simple_file_sink_mt;

View File

@@ -27,7 +27,7 @@ protected:
}; };
typedef null_sink<details::null_mutex> null_sink_st; typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt; typedef null_sink<details::null_mutex> null_sink_mt;
} }
} }

View File

@@ -15,7 +15,10 @@ namespace sinks
class sink class sink
{ {
public: public:
sink(): _level( level::trace ) {} sink()
{
_level = level::trace;
}
virtual ~sink() {} virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0; virtual void log(const details::log_msg& msg) = 0;

View File

@@ -18,11 +18,12 @@ namespace sinks
{ {
template <class Mutex> template <class Mutex>
class stdout_sink : public base_sink<Mutex> class stdout_sink: public base_sink<Mutex>
{ {
using MyType = stdout_sink<Mutex>; using MyType = stdout_sink<Mutex>;
public: public:
stdout_sink() {} stdout_sink()
{}
static std::shared_ptr<MyType> instance() static std::shared_ptr<MyType> instance()
{ {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
@@ -46,11 +47,12 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex> template <class Mutex>
class stderr_sink : public base_sink<Mutex> class stderr_sink: public base_sink<Mutex>
{ {
using MyType = stderr_sink<Mutex>; using MyType = stderr_sink<Mutex>;
public: public:
stderr_sink() {} stderr_sink()
{}
static std::shared_ptr<MyType> instance() static std::shared_ptr<MyType> instance()
{ {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); static std::shared_ptr<MyType> instance = std::make_shared<MyType>();

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

@@ -2,12 +2,13 @@
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
// spdlog main header file. // spdlog main header file.
// see example.cpp for usage example // see example.cpp for usage example
#pragma once #pragma once
#define SPDLOG_VERSION "0.12.0"
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#include <spdlog/common.h> #include <spdlog/common.h>
#include <spdlog/logger.h> #include <spdlog/logger.h>
@@ -88,10 +89,17 @@ std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const fi
// //
// Create and register stdout/stderr loggers // Create and register stdout/stderr loggers
// //
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false); std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false); std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false); std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false); 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);
// //

View File

@@ -101,3 +101,8 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors
//
// #define SPDLOG_PREVENT_CHILD_FD
///////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -12,7 +12,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
log_msg msg; log_msg msg;
msg.formatted << std::string(howmany, '1'); msg.formatted << std::string(howmany, '1');
helper.write(msg); helper.write(msg);
helper.flush(); helper.flush();
} }
@@ -45,7 +45,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{ {
prepare_logdir(); prepare_logdir();
REQUIRE(!file_helper::file_exists(target_filename)); REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper; file_helper helper;
helper.open(target_filename); helper.open(target_filename);
REQUIRE(file_helper::file_exists(target_filename)); REQUIRE(file_helper::file_exists(target_filename));
} }
@@ -65,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{ {
prepare_logdir(); prepare_logdir();
size_t expected_size = 14; size_t expected_size = 14;
file_helper helper; file_helper helper;
helper.open(target_filename); helper.open(target_filename);
write_with_helper(helper, expected_size); write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size); REQUIRE(helper.size() == expected_size);

View File

@@ -23,21 +23,21 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("flush_on", "[flush_on]]") TEST_CASE("flush_on", "[flush_on]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log.txt"; std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info); logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed"); logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0); REQUIRE(count_lines(filename) == 0);
logger->info("Test message {}", 1); logger->info("Test message {}", 1);
logger->info("Test message {}", 2); logger->info("Test message {}", 2);
logger->flush(); logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n")); REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3); REQUIRE(count_lines(filename) == 3);
} }
TEST_CASE("rotating_file_logger1", "[rotating_logger]]") TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
@@ -49,7 +49,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush(); logger->flush();
auto filename = basename + ".txt"; auto filename = basename + ".txt";
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
@@ -111,7 +111,7 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0); auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush(); logger->flush();
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
@@ -144,7 +144,7 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush(); logger->flush();
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }