Compare commits

..

92 Commits

Author SHA1 Message Date
gabime
f85a08622e version 0.13.0 2017-03-28 02:09:01 +03:00
gabime
0c276beaaf astyle 2017-03-28 02:08:18 +03:00
gabime
5a8cecdfb6 fix unused warning message 2017-03-28 02:05:59 +03:00
gabime
397d4866b3 Fixed issue #396 and added some tests to catch it 2017-03-28 01:54:33 +03:00
Gabi Melman
27df6eb4ca Merge pull request #403 from Falconne/master
Disambiguate fmt logging methods that are using variadic templates.
2017-03-28 00:27:29 +03:00
Anuradha Dissanayake
ad1c18704d Disambiguate fmt logging methods that are using variadic templates.
As variadic template arguments can be zero length, we need to specify that at least one fmt argument is provided, to distinguish these methods from the existing trivial method that takes no fmt arguments.

Without this, static analysers such as ReSharper flag the logging calls as errors.
2017-03-27 08:58:03 +13:00
Gabi Melman
029e6ed40f Merge pull request #399 from devanshdalal/patch-1
Update README.md
2017-03-22 08:41:27 +00:00
Devansh D
a55615c984 Update README.md 2017-03-22 13:52:56 +05:30
Gabi Melman
e8da69ebe1 Merge pull request #394 from zamaudio/spdlog-updatefmt-3.0.1
fmt: update bundled fmt to 3.0.1 (7fa8f8f)
2017-03-20 14:41:06 +02:00
Damien Zammit
8192c13379 fmt: update bundled fmt to 3.0.1 (7fa8f8f)
Signed-off-by: Damien Zammit <damien@zamaudio.com>
2017-03-20 15:25:10 +11:00
Gabi Melman
270c08b275 Merge pull request #377 from tekezo/master
Use double-braces in std::array initialization.
2017-03-02 20:18:55 +02:00
Takayama Fumihiko
a4714a6571 use double-braces in std::array initialization 2017-03-03 01:37:53 +09:00
Gabi Melman
5585299b03 Merge pull request #375 from horar/master
Don't hardcode '.txt.' log file name suffix (resolve #333)
2017-02-28 09:19:07 +02:00
Ľubomír Carik
fd8df5b820 Don't hardcode '.txt.' log file name suffix (resolve #333)
Signed-off-by: Ľubomír Carik <Lubomir.Carik@gmail.com>
2017-02-28 00:59:23 +01:00
Gabi Melman
d7a8db8f63 Update README.md 2017-02-21 12:13:04 +02:00
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
42 changed files with 2344 additions and 1051 deletions

View File

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

View File

@@ -4,16 +4,27 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## 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 the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
#### Or use your favourite package manager:
* Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Arch Linux: `pacman -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+)
* Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw)
* Mac OSX (clang 3.5+)
* Solaris (gcc 5.2.0+)
* Android
##Features
## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
@@ -25,6 +36,7 @@ Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/inclu
* Daily log files.
* Console logging (colors supported).
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
@@ -74,34 +86,25 @@ int main(int, char*[])
{
try
{
// Multithreaded color console
auto console = spd::stdout_logger_mt("console", true);
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->info("An info message example {}..", 1);
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
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");
// 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)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
@@ -109,42 +112,57 @@ int main(int, char*[])
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// 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_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
// syslog example. linux/osx only..
// syslog example. linux/osx only
syslog_example();
// Log user-defined types example..
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->info("End of example."); });
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
}
void async_example()

View File

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

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

@@ -46,7 +46,7 @@ int main(int argc, char* argv[])
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
@@ -64,7 +64,7 @@ int main(int argc, char* argv[])
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
}
catch (std::exception &ex)
{

View File

@@ -22,31 +22,24 @@ int main(int, char*[])
{
try
{
// Multithreaded color console
auto console = spd::stdout_logger_mt("console", true);
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("An error message example {}..", 1);
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
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");
// 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)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
@@ -64,8 +57,15 @@ int main(int, char*[])
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
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_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
@@ -106,7 +106,7 @@ void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);

View File

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

View File

@@ -63,6 +63,11 @@ public:
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
protected:
void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;

View File

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

View File

@@ -66,8 +66,8 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
msg_type(std::move(other.msg_type))
{}
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
{
@@ -82,10 +82,10 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
// never copy or assign. should only be moved..
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
async_msg(const details::log_msg& m) :
async_msg(const details::log_msg& m):
level(m.level),
time(m.time),
thread_id(m.thread_id),
@@ -135,6 +135,7 @@ public:
void flush(bool wait_for_q);
void set_error_handler(spdlog::log_err_handler err_handler);
private:
formatter_ptr _formatter;
@@ -221,7 +222,8 @@ inline spdlog::details::async_log_helper::~async_log_helper()
_worker_thread.join();
}
catch (...) // don't crash in destructor
{}
{
}
}
@@ -229,8 +231,6 @@ inline spdlog::details::async_log_helper::~async_log_helper()
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));
}
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
@@ -246,45 +246,48 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
}
while (!_q.enqueue(std::move(new_msg)));
}
}
// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
}
inline void spdlog::details::async_log_helper::worker_loop()
{
try
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
auto active = true;
while (active)
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
try
{
active = process_next_msg(last_pop, last_flush);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
}
// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg))
{
last_pop = details::os::now();
@@ -305,7 +308,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if(s->should_log( incoming_log_msg.level))
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
@@ -359,8 +362,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
// yield upto 150 micros
if (time_since_op <= microseconds(100))
return yield();
return std::this_thread::yield();
// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
@@ -378,13 +380,12 @@ inline void spdlog::details::async_log_helper::wait_empty_q()
{
sleep_or_yield(details::os::now(), last_op);
}
}
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}

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,
sinks_init_list sinks,
sinks_init_list sinks_list,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.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,
sink_ptr single_sink,
@@ -57,6 +57,19 @@ inline void spdlog::async_logger::flush()
_async_log_helper->flush(true);
}
// Error handler
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler);
}
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{
return _err_handler;
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;

View File

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

View File

@@ -121,44 +121,42 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
}
template <typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::trace, fmt, args...);
log(level::trace, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::debug, fmt, args...);
log(level::debug, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::info, fmt, args...);
log(level::info, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::warn, fmt, args...);
log(level::warn, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::err, fmt, args...);
log(level::err, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::critical, fmt, args...);
log(level::critical, fmt, arg1, args...);
}
template<typename T>
inline void spdlog::logger::trace(const T& msg)
{
@@ -291,3 +289,8 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

View File

@@ -10,10 +10,12 @@
#include <ctime>
#include <functional>
#include <string>
#include <chrono>
#include <thread>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
@@ -25,27 +27,32 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle support
#ifdef __MINGW32__
#include <share.h>
#endif
#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 <chrono>
#include <fcntl.h>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#else
#include <thread>
#endif
#endif //unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog
{
namespace details
@@ -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 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
@@ -146,13 +165,18 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
return *fp == nullptr;
#else
#else //unix
*fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if(*fp != nullptr)
prevent_child_fd(*fp);
#endif
return *fp == nullptr;
}
inline int remove(const filename_t &filename)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -204,9 +228,9 @@ inline size_t filesize(FILE *f)
return st.st_size;
#else //windows 32 bits
struct _stat st;
if (_fstat(fd, &st) == 0)
return st.st_size;
long ret = _filelength(fd);
if (ret >= 0)
return static_cast<size_t>(ret);
#endif
#else // unix
@@ -215,11 +239,11 @@ inline size_t filesize(FILE *f)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st;
if (fstat64(fd, &st) == 0)
return st.st_size;
return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return st.st_size;
return static_cast<size_t>(st.st_size);
#endif
#endif
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
//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
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)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}
//Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
{
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
return _thread_id();
#else
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -342,8 +377,8 @@ inline std::string errno_str(int err_num)
else
return "Unkown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
@@ -355,6 +390,17 @@ inline std::string errno_str(int err_num)
#endif
}
inline int pid()
{
#ifdef _WIN32
return ::_getpid();
#else
return static_cast<int>(::getpid());
#endif
}
} //os
} //details
} //spdlog

View File

@@ -18,6 +18,7 @@
#include <thread>
#include <utility>
#include <vector>
#include <array>
namespace spdlog
{
@@ -78,42 +79,60 @@ static int to12h(const tm& t)
}
//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
{
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
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
{
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
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
{
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
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
{
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
{
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;
}
};
@@ -356,7 +375,7 @@ private:
//Thread id
// Thread id
class t_formatter:public flag_formatter
{
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
{
@@ -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()));
break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
default: //Unkown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));

View File

@@ -12,9 +12,20 @@
#include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h>
#endif
#ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h>
#else
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h>
#endif
#include <chrono>
#include <functional>
@@ -50,53 +61,105 @@ inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, 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
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_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
// 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)
@@ -105,7 +168,7 @@ inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string&
}
#endif
#if defined(__ANDROID__)
#ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);

View File

@@ -25,9 +25,7 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Commented out by spdlog to use header only
// #include "fmt/format.h"
// #include "fmt/printf.h"
#include "format.h"
#include <string.h>
@@ -107,6 +105,27 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker {
template <typename T>
static bool fits_in_int(T value) {
unsigned max = INT_MAX;
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <>
struct IntChecker<true> {
template <typename T>
static bool fits_in_int(T value) {
return value >= INT_MIN && value <= INT_MAX;
}
static bool fits_in_int(int) { return true; }
};
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
@@ -211,29 +230,222 @@ void report_error(FormatFunc func, int error_code,
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) { return value == 0; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value)) {
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
if (width > INT_MAX)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
template <typename T, typename U>
struct is_same {
enum { value = 0 };
};
template <typename T>
struct is_same<T, T> {
enum { value = 1 };
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} else {
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
} else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
} else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> {
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
} // namespace
namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0.
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
fmt::format_system_error(out, error_code, message);
}
} // namespace internal
template <typename Char>
class PrintfArgFormatter :
public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
FMT_FUNC void SystemError::init(
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
void visit_char(int value) {
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) {
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) {
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
} else {
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
} else {
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
void visit_custom(Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
} // namespace internal
} // namespace fmt
FMT_FUNC void fmt::SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
internal::format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
@@ -247,7 +459,7 @@ int internal::CharTraits<char>::format_float(
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
@@ -261,7 +473,7 @@ int internal::CharTraits<wchar_t>::format_float(
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
const char fmt::internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
@@ -280,34 +492,34 @@ const char internal::BasicData<T>::DIGITS[] =
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
@@ -324,14 +536,14 @@ FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
@@ -347,7 +559,7 @@ FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
return 0;
}
FMT_FUNC void WindowsError::init(
FMT_FUNC void fmt::WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
@@ -356,8 +568,9 @@ FMT_FUNC void WindowsError::init(
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
@@ -384,11 +597,12 @@ FMT_FUNC void internal::format_windows_error(
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_FUNC void fmt::internal::format_system_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
@@ -405,7 +619,7 @@ FMT_FUNC void format_system_error(
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
@@ -450,11 +664,11 @@ void internal::ArgMap<Char>::init(const ArgList &args) {
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
switch (arg.type) {
@@ -470,31 +684,203 @@ FMT_FUNC Arg internal::FormatterBase::do_get_arg(
return arg;
}
FMT_FUNC void report_system_error(
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::parse_flags(
FormatSpec &spec, const Char *&s) {
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char>
Arg fmt::internal::PrintfFormatter<Char>::get_arg(
const Char *s, unsigned arg_index) {
(void)s;
const char *error = 0;
Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char>
unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_MAX;
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
} else {
if (c == '0')
spec.fill_ = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer, start, s);
start = ++s;
continue;
}
write(writer, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(parse_nonnegative_int(s));
} else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
}
}
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i': case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
internal::PrintfArgFormatter<Char>(writer, spec).visit(arg);
}
write(writer, start, s);
}
FMT_FUNC void fmt::report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
fmt::report_error(internal::format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
FMT_FUNC void fmt::report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
fmt::report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
@@ -502,10 +888,7 @@ FMT_FUNC void print_colored(Color c, CStringRef format, ArgList 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) {
FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
@@ -514,44 +897,44 @@ FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
template struct fmt::internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
template void PrintfFormatter<char>::format(CStringRef format);
template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, CStringRef format);
template int internal::CharTraits<char>::format_float(
template int fmt::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 fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, WCStringRef format);
template int internal::CharTraits<wchar_t>::format_float(
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int internal::CharTraits<wchar_t>::format_float(
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

View File

@@ -194,6 +194,17 @@ typedef __int64 intmax_t;
# endif
#endif
#ifndef FMT_OVERRIDE
# if FMT_USE_OVERRIDE || FMT_HAS_FEATURE(cxx_override) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1900
# define FMT_OVERRIDE override
# else
# define FMT_OVERRIDE
# endif
#endif
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef FMT_USE_DELETED_FUNCTIONS
@@ -420,9 +431,6 @@ typedef BasicWriter<wchar_t> WWriter;
template <typename Char>
class ArgFormatter;
template <typename Impl, typename Char>
class BasicPrintfArgFormatter;
template <typename CharType,
typename ArgFormatter = fmt::ArgFormatter<CharType> >
class BasicFormatter;
@@ -766,7 +774,7 @@ private:
}
protected:
void grow(std::size_t size);
void grow(std::size_t size) FMT_OVERRIDE;
public:
explicit MemoryBuffer(const Allocator &alloc = Allocator())
@@ -1163,6 +1171,9 @@ FMT_API void format_windows_error(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT;
#endif
FMT_API void format_system_error(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT;
// A formatting argument value.
struct Value
{
@@ -1308,25 +1319,19 @@ struct Conditional<false, T, F>
};
// For bcc32 which doesn't understand ! in template arguments.
template <bool>
template<bool>
struct Not
{
enum { value = 0 };
};
template <>
template<>
struct Not<false>
{
enum { value = 1 };
};
template <typename T>
struct False
{
enum { value = 0 };
};
template <typename T, T> struct LConvCheck
template<typename T, T> struct LConvCheck
{
LConvCheck(int) {}
};
@@ -1346,36 +1351,6 @@ inline fmt::StringRef thousands_sep(...)
return "";
}
#define FMT_CONCAT(a, b) a##b
#if FMT_GCC_VERSION >= 407
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
#endif
#ifndef FMT_USE_STATIC_ASSERT
# define FMT_USE_STATIC_ASSERT 0
#endif
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
#else
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
# define FMT_STATIC_ASSERT(cond, message) \
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
#endif
template <typename Formatter, typename Char, typename T>
void format_arg(Formatter &, const Char *, const T &)
{
FMT_STATIC_ASSERT(False<T>::value,
"Cannot format argument. To enable the use of ostream "
"operator<< include fmt/ostream.h. Otherwise provide "
"an overload of format_arg.");
}
// Makes an Arg object from any type.
template <typename Formatter>
class MakeValue : public Arg
@@ -1423,9 +1398,9 @@ private:
static void format_custom_arg(
void *formatter, const void *arg, void *format_str_ptr)
{
format_arg(*static_cast<Formatter*>(formatter),
*static_cast<const Char**>(format_str_ptr),
*static_cast<const T*>(arg));
format(*static_cast<Formatter*>(formatter),
*static_cast<const Char**>(format_str_ptr),
*static_cast<const T*>(arg));
}
public:
@@ -1590,6 +1565,9 @@ protected:
~RuntimeError() throw();
};
template <typename Char>
class PrintfArgFormatter;
template <typename Char>
class ArgMap;
} // namespace internal
@@ -2233,29 +2211,29 @@ public:
typedef typename BasicWriter<Char>::CharPtr CharPtr;
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
CharPtr out = CharPtr();
const unsigned CHAR_WIDTH = 1;
if (spec_.width_ > CHAR_WIDTH)
const unsigned CHAR_SIZE = 1;
if (spec_.width_ > CHAR_SIZE)
{
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == ALIGN_RIGHT)
{
std::uninitialized_fill_n(out, spec_.width_ - CHAR_WIDTH, fill);
out += spec_.width_ - CHAR_WIDTH;
std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill);
out += spec_.width_ - CHAR_SIZE;
}
else if (spec_.align_ == ALIGN_CENTER)
{
out = writer_.fill_padding(out, spec_.width_,
internal::const_check(CHAR_WIDTH), fill);
internal::const_check(CHAR_SIZE), fill);
}
else
{
std::uninitialized_fill_n(out + CHAR_WIDTH,
spec_.width_ - CHAR_WIDTH, fill);
std::uninitialized_fill_n(out + CHAR_SIZE,
spec_.width_ - CHAR_SIZE, fill);
}
}
else
{
out = writer_.grow_buffer(CHAR_WIDTH);
out = writer_.grow_buffer(CHAR_SIZE);
}
*out = internal::CharTraits<Char>::cast(value);
}
@@ -2342,6 +2320,27 @@ protected:
w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));
}
};
// A printf formatter.
template <typename Char>
class PrintfFormatter : private FormatterBase
{
private:
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
Arg get_arg(const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {}
FMT_API void format(BasicWriter<Char> &writer,
BasicCStringRef<Char> format_str);
};
} // namespace internal
/**
@@ -2382,7 +2381,7 @@ public:
: internal::ArgFormatterBase<Impl, Char>(formatter.writer(), spec),
formatter_(formatter), format_(fmt) {}
/** Formats an argument of a custom (user-defined) type. */
/** Formats argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
c.format(&formatter_, c.value, &format_);
@@ -2665,10 +2664,17 @@ protected:
public:
/**
\rst
Constructs a :class:`fmt::SystemError` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
Constructs a :class:`fmt::SystemError` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
**Example**::
@@ -2695,25 +2701,6 @@ public:
}
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API void format_system_error(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT;
/**
\rst
This template provides operations for formatting and writing data into
@@ -2848,8 +2835,7 @@ private:
template <typename Impl, typename Char_>
friend class internal::ArgFormatterBase;
template <typename Impl, typename Char_>
friend class BasicPrintfArgFormatter;
friend class internal::PrintfArgFormatter<Char>;
protected:
/**
@@ -3733,6 +3719,60 @@ FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args);
*/
FMT_API void print(CStringRef format_str, ArgList args);
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
{
internal::PrintfFormatter<Char>(args).format(w, format);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
/**
Fast integer formatter.
*/
@@ -3912,6 +3952,7 @@ void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define FMT_CONCAT(a, b) a##b
#define FMT_FOR_EACH_(N, f, ...) \
FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__))
#define FMT_FOR_EACH(f, ...) \
@@ -4026,7 +4067,12 @@ FMT_VARIADIC(std::string, format, CStringRef)
FMT_VARIADIC_W(std::wstring, format, WCStringRef)
FMT_VARIADIC(void, print, CStringRef)
FMT_VARIADIC(void, print, std::FILE *, CStringRef)
FMT_VARIADIC(void, print_colored, Color, CStringRef)
FMT_VARIADIC(std::string, sprintf, CStringRef)
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
FMT_VARIADIC(int, printf, CStringRef)
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
namespace internal
{

View File

@@ -7,13 +7,13 @@
For the license information refer to format.h.
*/
// Commented out by spdlog to use header only
// #include "fmt/ostream.h"
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
namespace {
// Write the content of w to os.
void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
@@ -31,6 +31,13 @@ FMT_FUNC void write(std::ostream &os, Writer &w) {
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
write(os, w);
}
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
} // namespace fmt

View File

@@ -10,8 +10,7 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// Commented out by spdlog to use header only
// #include "fmt/format.h"
#include "format.h"
#include <ostream>
namespace fmt
@@ -77,15 +76,12 @@ struct ConvertToIntImpl<T, true>
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
@@ -109,6 +105,18 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY

View File

@@ -0,0 +1,238 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
// Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix.h"
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# include <windows.h>
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
}
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
}
void fmt::BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = 0;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int fmt::BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
return fd;
}
fmt::File::File(fmt::CStringRef path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(SystemError(errno, "cannot open file {}", path));
}
fmt::File::~File() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
void fmt::File::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
fmt::LongLong fmt::File::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
}
fmt::ULongLong long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(SystemError(errno, "cannot get file attributes"));
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t fmt::File::read(void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd);
}
void fmt::File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
void fmt::File::pipe(File &read_end, File &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
FMT_THROW(SystemError(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
BufferedFile file(f);
fd_ = -1;
return file;
}
long fmt::getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(SystemError(errno, "cannot get memory page size"));
return size;
#endif
}

View File

@@ -0,0 +1,443 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
#if FMT_GCC_VERSION >= 407
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
#endif
#ifndef FMT_USE_STATIC_ASSERT
# define FMT_USE_STATIC_ASSERT 0
#endif
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
#else
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
# define FMT_STATIC_ASSERT(cond, message) \
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt
{
// An error code.
class ErrorCode
{
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class BufferedFile
{
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT :
file_(0) {}
// Destroys the object closing the file it represents if any.
~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT :
file_(p.file) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
file_(f.file_)
{
f.file_ = 0;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p)
{
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other)
{
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = 0;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
file_(other.file_)
{
other.file_ = 0;
}
BufferedFile& operator=(BufferedFile &&other)
{
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
#endif
// Opens a file.
BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
int (fileno)() const;
void print(CStringRef format_str, const ArgList &args)
{
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, CStringRef)
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::SystemError in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File
{
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum
{
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT :
fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT :
fd_(p.fd) {}
// A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p)
{
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
File& operator=(File &&other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
~File() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale
{
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t)
{
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale)
{
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL))
{
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale()
{
freelocale(locale_);
}
Type get() const
{
return locale_;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const
{
char *end = 0;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace std
{
// For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
}
#endif
#endif // FMT_POSIX_H_

View File

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

View File

@@ -0,0 +1,58 @@
/*
Formatting library for C++ - time formatting
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
namespace fmt
{
template <typename ArgFormatter>
void format(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm)
{
if (*format_str == ':')
++format_str;
const char *end = format_str;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1);
format[format.size() - 1] = '\0';
Buffer<char> &buffer = f.writer().buffer();
std::size_t start = buffer.size();
for (;;)
{
std::size_t size = buffer.capacity() - start;
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
if (count != 0)
{
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
}
}
#endif // FMT_TIME_H_

View File

@@ -5,7 +5,7 @@
#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
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
@@ -37,12 +37,12 @@ public:
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
@@ -59,15 +59,17 @@ public:
void set_pattern(const std::string&);
void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr>& sinks() const;
// error handler
virtual void set_error_handler(log_err_handler);
virtual log_err_handler error_handler();
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&);
@@ -90,5 +92,3 @@ protected:
}
#include <spdlog/details/logger_impl.h>

View File

@@ -33,7 +33,7 @@ public:
virtual void log(const details::log_msg& msg) 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
const std::string reset = "\033[00m";
@@ -101,9 +101,9 @@ inline void ansicolor_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()

View File

@@ -29,7 +29,7 @@ template<class Mutex>
class simple_file_sink : public base_sink < Mutex >
{
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);
}
@@ -37,14 +37,21 @@ public:
{
_file_helper.flush();
}
void set_force_flush(bool force_flush)
{
_force_flush = force_flush;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
@@ -57,16 +64,15 @@ template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files ) :
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_file_helper.open(calc_filename(_base_filename, 0));
_current_size = _file_helper.size(); //expensive. called only once
}
@@ -88,21 +94,21 @@ protected:
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
void _rotate()
{
@@ -110,8 +116,8 @@ private:
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
filename_t src = calc_filename(_base_filename, i - 1, _extension);
filename_t target = calc_filename(_base_filename, i, _extension);
filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target))
{
@@ -128,7 +134,6 @@ private:
_file_helper.reopen(true);
}
filename_t _base_filename;
filename_t _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
@@ -143,27 +148,27 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form basename.YYYY-MM-DD_hh-mm
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
* Generator of daily log file names in format basename.YYYY-MM-DD
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
@@ -178,17 +183,15 @@ public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
const filename_t& extension,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
_rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
void flush() override
@@ -201,7 +204,7 @@ protected:
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
@@ -224,7 +227,6 @@ private:
}
filename_t _base_filename;
filename_t _extension;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;

View File

@@ -27,7 +27,7 @@ protected:
};
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
{
public:
sink(): _level( level::trace ) {}
sink()
{
_level = level::trace;
}
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;

View File

@@ -18,11 +18,12 @@ namespace sinks
{
template <class Mutex>
class stdout_sink : public base_sink<Mutex>
class stdout_sink: public base_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() {}
stdout_sink()
{}
static std::shared_ptr<MyType> instance()
{
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>
class stderr_sink : public base_sink<Mutex>
class stderr_sink: public base_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() {}
stderr_sink()
{}
static std::shared_ptr<MyType> instance()
{
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.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// spdlog main header file.
// see example.cpp for usage example
#pragma once
#define SPDLOG_VERSION "0.13.0"
#include <spdlog/tweakme.h>
#include <spdlog/common.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
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_st(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);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
//
@@ -116,7 +124,7 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
// Create and register a logger with templated sink type
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);

View File

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

View File

@@ -6,6 +6,19 @@
#include<iostream>
class failing_sink: public spdlog::sinks::sink
{
void log(const spdlog::details::log_msg& msg) override
{
throw std::runtime_error("some error happened during log");
}
void flush()
{}
};
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
@@ -22,13 +35,16 @@ TEST_CASE("default_error_handler", "[errors]]")
}
struct custom_ex {};
struct custom_ex
{};
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
@@ -39,6 +55,17 @@ TEST_CASE("custom_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("default_error_handler2", "[errors]]")
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]]")
{
prepare_logdir();
@@ -62,3 +89,25 @@ TEST_CASE("async_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
}
// Make sure async error handler is executed
TEST_CASE("async_error_handler2", "[errors]]")
{
prepare_logdir();
std::string err_msg("This is async handler error message");
spdlog::set_async_mode(128);
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
std::ofstream ofs("logs/custom_err2.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
}
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
}

View File

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

View File

@@ -7,7 +7,7 @@
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
@@ -23,21 +23,21 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
}
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
@@ -45,12 +45,12 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
logger->flush();
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
}
@@ -64,14 +64,14 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
logger->flush();
REQUIRE(get_filesize(filename) <= 1024);
auto filename1 = basename + ".1.txt";
auto filename1 = basename + ".1";
REQUIRE(get_filesize(filename1) <= 1024);
}
@@ -83,7 +83,7 @@ TEST_CASE("daily_logger", "[daily_logger]]")
std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info);
@@ -106,23 +106,23 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
struct custom_daily_file_name_calculator
{
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
@@ -138,14 +138,14 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = w.str();
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}

View File

@@ -1,14 +1,17 @@
#include "includes.h"
void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
auto rv = system("del /F /Q logs\\*");
system("if not exist logs mkdir logs");
system("del /F /Q logs\\*");
#else
auto rv = system("rm -f logs/*");
#endif
auto rv = system("mkdir -p logs");
rv = system("rm -f logs/*");
(void)rv;
#endif
}