Compare commits

...

90 Commits

Author SHA1 Message Date
Gabi Melman
c060a10c10 update to version 0.16.1 2017-12-20 10:08:49 +02:00
Gabi Melman
79a3a633c7 uupdate version to 0.16.1 2017-12-20 10:07:50 +02:00
Gabi Melman
52dfd478d6 Merge pull request #584 from horar/master
Update version strings to 0.16.0
2017-12-20 10:02:17 +02:00
Gabi Melman
48eca46680 Merge pull request #586 from horar/fix/warnings
Solve compiler warning in tests
2017-12-20 09:50:34 +02:00
Ľubomír Carik
f93277d271 Solve compiler warning in tests
Signed-off-by: Ľubomír Carik <Lubomir.Carik@anritsu.com>
2017-12-20 04:34:08 +01:00
Ľubomír Carik
dda8af0092 Update version strings to 0.16.0
Signed-off-by: Ľubomír Carik <Lubomir.Carik@anritsu.com>
2017-12-20 03:43:47 +01:00
gabime
ed5498a2e9 report unexected exception types 2017-12-20 00:29:15 +02:00
gabime
6dd928dc3c Undo fix #529 (causes a warning under gcc and clang when no args passed) 2017-12-20 00:04:16 +02:00
gabime
de595fe2b7 Fix #529 (SPDLOG_TRACE macro problem) 2017-12-19 23:09:27 +02:00
Gabi Melman
d460c3026a Merge pull request #568 from adubovikov/master
added facilty for syslog
2017-12-19 11:49:51 +02:00
Gabi Melman
88fe218741 Update logger_impl.h 2017-12-06 15:59:27 +02:00
Gabi Melman
b1be7b9fea async log: increased sleep to to 500ms the worker loop 2017-12-05 14:07:13 +02:00
Alexandr Dubovikov
bec6919587 added facilty for syslog 2017-12-04 13:03:40 +01:00
Gabi Melman
2f81ff7f17 Update README.md 2017-12-03 21:01:07 +02:00
Gabi Melman
4db70c2078 Update README.md 2017-12-03 20:58:19 +02:00
gabime
26b390bb19 removed lock from dist_sink::_flush() (moved to base_sink::flush()) 2017-12-02 17:24:02 +02:00
gabime
a9149c6d46 added lock on flush in base_sink 2017-12-02 17:06:59 +02:00
gabime
859b7f1d58 fixed test for visual c++ 2017-12-01 04:42:23 +02:00
gabime
49989e0678 fixed test for gcc 4.8 (no regex) 2017-12-01 04:35:23 +02:00
gabime
70274924b7 fixed test comment 2017-12-01 03:59:35 +02:00
gabime
f5939f9e56 astyle 2017-12-01 03:46:19 +02:00
gabime
84e307521d Fixed test 2017-12-01 03:45:29 +02:00
gabime
3c4a2bf531 Handle file extensions in rotating and daily loggers 2017-12-01 03:40:49 +02:00
gabime
60ce47a814 Removed include_if.cpp from the tests .sln 2017-12-01 01:28:12 +02:00
Gabi Melman
613f024d42 Removed forgotten *_if declarations 2017-11-30 23:42:57 +02:00
gabime
799ba2a57b added SPDLOG_DISABLE_TID_CACHING macro to prevent invalid thread ids after fork 2017-11-26 00:40:47 +02:00
gabime
adbc22096a enable final keyword by default. Can be disabled in tweakme.h for older compilers 2017-11-25 15:53:35 +02:00
gabime
e7cf25a1c0 fixed issue #562 2017-11-25 15:41:55 +02:00
gabime
dcc7b347ca Removed all *_if functions (trace_if, debug_if, info_if,..) because they are redundant and confusing way to preform if 2017-11-25 15:19:41 +02:00
gabime
9a8f5c59e2 Added .idea/ to .gitignore 2017-11-25 15:11:18 +02:00
gabime
c41b6d28b5 astyle 2017-11-24 20:59:58 +02:00
gabime
fd170b0fe1 catch(...) exceptions, report it, and rethrow 2017-11-24 20:58:43 +02:00
Gabi Melman
587b528292 Merge pull request #556 from jpcima/syslog-build
correct include path for sink/syslog_sink.h
2017-11-14 16:29:20 +02:00
Gabi Melman
36c8e79a4a Merge pull request #555 from jpcima/windebug-sink
accept msvc_sink on all compilers, add name windebug_sink (fixes #554)
2017-11-14 16:28:39 +02:00
JP Cimalando
ecec210d0e accept msvc_sink on all compilers, add name windebug_sink (fixes #554) 2017-11-14 14:41:31 +01:00
JP Cimalando
76d2620dad correct include path for sink/syslog_sink.h 2017-11-14 14:25:43 +01:00
gabime
8ca1d84a32 Removed catch(..) from the codebase. Catch only std::exception 2017-11-12 19:46:15 +02:00
Gabi Melman
63d7c64618 Merge pull request #551 from daylanKifky/modified_includes
modified path on quoted #includes
2017-11-11 16:50:06 +02:00
daylanKifky
10772eadae fix wincolor_sink's common.h include 2017-11-11 15:38:08 +01:00
daylanKifky
b220bbb349 fix printf include 2017-11-11 15:21:34 +01:00
daylanKifky
5153b44507 minor fixes 2017-11-11 14:06:01 +01:00
daylanKifky
27e7412640 modified path on quoted #includes
Paths pointing to the root of the library where replaced for ones relatives to each file.

For example, inside /include/spdlog/details/file_helper.h:

This will look for os.h in /include/spdlog/details/spdlog/details/ which doesn't exists.

replaced with:
2017-11-11 13:44:27 +01:00
gabime
93be7713e0 astyle 2017-11-06 12:39:04 +02:00
Gabi Melman
6ab2f0e099 Merge pull request #548 from Subenle/master
Declare variables as size_t rather than unsigned.
2017-11-06 12:37:04 +02:00
Subenlele
34a9f24dba Declare variables as size_t rather than unsigned.
Modify `unsigned front, front1, back;` to `size_t front, front1, back;`
2017-11-05 20:59:37 -06:00
gabime
f70b2ef3b8 Fixed cygwin support 2017-11-05 01:17:21 +02:00
gabime
79e97fa1ec Added the license file of the fmtlib in the bundled folder 2017-11-05 00:34:16 +02:00
gabime
a66e9bbaf1 Minor fix in comment 2017-11-05 00:29:19 +02:00
gabime
f5fe681a41 Fixed issue #546 by adding an "is_empty" method to the queue instead of the buggy approx_size 2017-11-05 00:21:00 +02:00
Gabi Melman
6fd5f3c866 Merge pull request #545 from costinm/patch-1
Allow compilation on platforms with unwind (android)
2017-11-04 10:24:43 +02:00
Costin Manolache
f4f3e3fb66 Use __ANDROID__
Based on review feedback.
2017-11-03 19:37:38 -07:00
Costin Manolache
23dd8d3559 Allow compilation on platforms with unwind (android) 2017-11-02 17:12:08 -07:00
Gabi Melman
47c17be9a1 Merge pull request #457 from rkollataj/appveyor
Adding additional build environments for AppVeyor
2017-10-25 13:09:09 +03:00
Gabi Melman
1f3d939009 Merge pull request #538 from berkus/patch-1
Fix typos. Thanks @berkus
2017-10-25 12:59:38 +03:00
Gabi Melman
fbb8244f7d Merge pull request #533 from manuel-schiller/patch-1
rethrow unwind exception
2017-10-25 12:57:40 +03:00
manuel-schiller
039b34e83a rethrow unwind exception
On Linux with pthread library spdlog causes an SIGABORT and crashes
the application in case it catches a thread specific cancellation
exception in a critical execution phase while in a try/catch block
in spdlog/detail/logger_impl.h

The exception is caught by some general catch(...) clause where
it is NOT rethrown.

However rethrowing these kind of exception is mandatory, otherwise
an abort will be caused by the glibc.
2017-10-25 10:15:27 +02:00
Gabi Melman
69e6af0bf9 Merge pull request #539 from knowledge4igor/fix_cast_warnings
Fix warnings which are caused by C style cast
2017-10-25 04:36:18 +03:00
knowledge4igor
147bdbf591 Fix warnings which are caused by C style cast 2017-10-25 00:40:42 +03:00
Berkus Decker
4974743ee8 Make short month names match in length 2017-10-24 14:10:58 +03:00
Berkus Decker
1c8cc65f6c Fix typos 2017-10-24 14:10:29 +03:00
Berkus Decker
a6b5ef55a4 Fix typo 2017-10-24 13:34:58 +03:00
Berkus Decker
cefea324cb Fix typos 2017-10-24 12:03:59 +03:00
Gabi Melman
7f953c115d Merge pull request #535 from vitor-alves/master
Update README.md
2017-10-17 10:04:18 +03:00
Vitor Alves
20a4143537 Update README.md
Updated install section for Arch Linux. Added yaourt instead of pacman.
2017-10-16 22:23:19 -02:00
gabime
87a44555b6 fixed macro tests 2017-10-13 02:15:49 +03:00
gabime
21ed31844c fixed bug in SPDLOG_TRACE_IF macro and added some related tests 2017-10-13 02:04:31 +03:00
gabime
4a159ad66d Fixed unused variable warning 2017-10-12 19:59:14 +03:00
gabime
19264b8399 Merge branch 'master' of https://github.com/gabime/spdlog 2017-10-12 19:53:07 +03:00
gabime
709948ff4a Fixed issue #527 2017-10-12 19:48:04 +03:00
Gabi Melman
30d5fd16d7 Update README.md 2017-10-12 19:06:01 +03:00
Gabi Melman
9688689938 Merge pull request #531 from fogo/printf
Support for printf-style logging
2017-10-10 13:56:37 +03:00
fogo
e8b7f4194a Moved printf include to fmt.h 2017-10-09 20:05:20 -03:00
fogo
ee525f9bef allow printf style logging
* tests updated for printf style
* tests makefile support printf style
2017-10-05 10:54:06 -03:00
fogo
552d6214e0 updated bundled fmt to contain printf 2017-10-05 08:19:53 -03:00
Gabi Melman
dc8ac4c671 Update tweakme.h 2017-09-29 22:43:48 +03:00
Gabi Melman
cd6a8c9a4c Merge pull request #524 from 2Park/master
Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging
2017-09-28 21:44:55 +03:00
John Andre Hestad
375b7fdda5 Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging 2017-09-28 14:19:04 +02:00
Gabi Melman
3a21b765cb Merge pull request #522 from jasonbeach/feat_add_epoch_formatter
add formatter for unix epoch time in seconds.
2017-09-18 09:46:16 +03:00
Jason Beach
f7fabfb2c4 add formatter for unix epoch time in seconds. 2017-09-17 22:11:23 -04:00
Gabi Melman
a4e6d8877c Update README.md 2017-08-29 12:00:10 +03:00
Gabi Melman
e303f02cce Merge pull request #515 from elelel/trace
Compiler-dependent line numbering
2017-08-25 21:34:23 +03:00
El El
b242fb087d Compiler-dependent line numbering 2017-08-25 15:19:29 +00:00
Gabi Melman
02b9f970d3 Merge pull request #514 from stackforce/feature/improve-cmake-lists
Feature/improve cmake lists
2017-08-25 17:11:33 +03:00
Adrian Antonana
e6b9fa577d cmake: set project version to 0.14.0 2017-08-25 09:12:15 +02:00
Adrian Antonana
fe2fa4087d cmake: add some small comments to point out whats being done 2017-08-24 16:57:07 +02:00
Adrian Antonana
ab25004242 cmake: add some commend blocks to clearly differentiate CMakeLists file sections 2017-08-24 16:55:55 +02:00
Adrian Antonana
5504630e46 cmake: improve CMakeLists.txt
* support CMake user registry package registration
* use GNUInstallDirs to set installation firectories
* use spdlog namespace in both build and install interfaces
2017-08-24 16:55:06 +02:00
Gabi Melman
c4d93ae0b5 Merge pull request #513 from Lectem/patch-1
SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
2017-08-23 17:35:04 +03:00
Lectem
47cf62f878 SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
I encountered an issue when using spdlog through add_subdirectory.
Since SPDLOG_BUILD_TESTING is ON by default, it now adds tests to my project, even if BUILD_TESTING (the official CTest variable) is set to OFF.
cmake_dependent_option makes it so that if someone enables BUILD_TESTING then SPDLOG_BUILD_TESTING will be set to ON by default.
This way one can disable all external tests by setting BUILD_TESTING before using add_subdirectory and then setting it back to its original value.
The only change for those using the library directly is that they now use BUILD_TESTING instead of SPDLOG_BUILD_TESTING when configuring.
2017-08-23 15:12:40 +02:00
Remigiusz Kołłątaj
2e84ca4fa4 Adding additional build environments for AppVeyor
Signed-off-by: Remigiusz Kołłątaj <remigiusz.kollataj@gmail.com>
2017-06-09 19:11:29 +02:00
56 changed files with 4758 additions and 3189 deletions

3
.gitignore vendored
View File

@@ -62,3 +62,6 @@ install_manifest.txt
/tests/tests.VC.db
/tests/tests
/tests/logs/file_helper_test.txt
# idea
.idea/

View File

@@ -85,6 +85,7 @@ script:
- ./"${BIN}"
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
notifications:
email: false

View File

@@ -1,87 +1,116 @@
#
# Copyright(c) 2015 Ruslan Baratov.
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
#
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0)
include(CTest)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
endif()
add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON)
target_include_directories(
spdlog
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:include>"
)
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests)
endif()
### Install ###
# * https://github.com/forexample/package-example
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(config_install_dir "lib/cmake/${PROJECT_NAME}")
set(include_install_dir "include")
set(pkgconfig_install_dir "lib/pkgconfig")
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${version_config}" COMPATIBILITY SameMajorVersion
)
# Note: use 'targets_export_name'
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
install(
TARGETS spdlog
EXPORT "${targets_export_name}"
INCLUDES DESTINATION "${include_install_dir}"
)
install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
install(
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}"
)
install(
FILES "${pkg_config}"
DESTINATION "${pkgconfig_install_dir}"
)
install(
EXPORT "${targets_export_name}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
)
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
#
# Copyright(c) 2015 Ruslan Baratov.
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
#
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 0.16.1)
include(CTest)
include(CMakeDependentOption)
include(GNUInstallDirs)
#---------------------------------------------------------------------------------------
# compiler config
#---------------------------------------------------------------------------------------
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()
#---------------------------------------------------------------------------------------
# spdlog target
#---------------------------------------------------------------------------------------
add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
cmake_dependent_option(SPDLOG_BUILD_TESTING
"Build spdlog tests" ON
"BUILD_TESTING" OFF
)
target_include_directories(
spdlog
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests)
endif()
#---------------------------------------------------------------------------------------
# Install/export targets and files
#---------------------------------------------------------------------------------------
# set files and directories
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${PROJECT_NAME}Config.cmake")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
# generate package version file
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${version_config}" COMPATIBILITY SameMajorVersion
)
# configure pkg config file
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
# install targets
install(
TARGETS spdlog
EXPORT "${targets_export_name}"
INCLUDES DESTINATION "${include_install_dir}"
)
# install headers
install(
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
DESTINATION "${include_install_dir}"
)
# install project version file
install(
FILES "${version_config}"
DESTINATION "${config_install_dir}"
)
# install pkg config file
install(
FILES "${pkg_config}"
DESTINATION "${pkgconfig_install_dir}"
)
# install project config file
install(
EXPORT "${targets_export_name}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
FILE ${project_config}
)
# export build directory config file
export(
EXPORT ${targets_export_name}
NAMESPACE "${namespace}"
FILE ${project_config}
)
# register project in CMake user registry
export(PACKAGE ${PROJECT_NAME})
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})

View File

@@ -8,14 +8,14 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* 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:
#### Or use your favorite 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`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog-git`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
@@ -29,6 +29,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* 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.
* Optional printf syntax support.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
@@ -92,19 +93,15 @@ int main(int, char*[])
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Conditional logging example
auto i = 2;
console->warn_if(i != 0, "an important message");
// 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");
// Use global registry to retrieve loggers
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
@@ -112,12 +109,12 @@ int main(int, char*[])
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);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
@@ -129,9 +126,9 @@ int main(int, char*[])
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON

32
appveyor.yml Normal file
View File

@@ -0,0 +1,32 @@
version: 1.0.{build}
image: Visual Studio 2015
environment:
matrix:
- GENERATOR: '"MinGW Makefiles"'
BUILD_TYPE: Debug
- GENERATOR: '"MinGW Makefiles"'
BUILD_TYPE: Release
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
build_script:
- cmd: >-
set
mkdir build
cd build
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
cmake --build . --config %BUILD_TYPE%
test: off

View File

@@ -1,5 +1,5 @@
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g

View File

@@ -31,8 +31,6 @@ int main(int, char*[])
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Conditional logging example
console->info_if(true, "Welcome to spdlog conditional logging!");
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
@@ -41,23 +39,20 @@ int main(int, char*[])
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
SPDLOG_DEBUG_IF(console, true, "This is a debug log");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.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);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
@@ -77,7 +72,6 @@ int main(int, char*[])
// 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);
SPDLOG_DEBUG_IF(console, true, "This is a debug log");
// Asynchronous logging is very fast..
@@ -117,8 +111,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");
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
}

View File

@@ -15,8 +15,8 @@
// 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing..
#include "spdlog/common.h"
#include "spdlog/logger.h"
#include "common.h"
#include "logger.h"
#include <chrono>
#include <functional>
@@ -79,4 +79,4 @@ private:
}
#include "spdlog/details/async_logger_impl.h"
#include "details/async_logger_impl.h"

View File

@@ -18,7 +18,7 @@
#include <locale>
#endif
#include "spdlog/details/null_mutex.h"
#include "details/null_mutex.h"
//visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
@@ -29,9 +29,11 @@
#define SPDLOG_CONSTEXPR constexpr
#endif
// See tweakme.h
#if !defined(SPDLOG_FINAL)
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__)
@@ -42,8 +44,7 @@
#define SPDLOG_DEPRECATED
#endif
#include "spdlog/fmt/fmt.h"
#include "fmt/fmt.h"
namespace spdlog
{
@@ -82,9 +83,9 @@ typedef enum
} level_enum;
#if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" };
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }
#endif
static const char* level_names[] SPDLOG_LEVEL_NAMES
static const char* level_names[] SPDLOG_LEVEL_NAMES;
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };

View File

@@ -12,12 +12,12 @@
#pragma once
#include "spdlog/common.h"
#include "spdlog/sinks/sink.h"
#include "spdlog/details/mpmc_bounded_q.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/formatter.h"
#include "../common.h"
#include "../sinks/sink.h"
#include "../details/mpmc_bounded_q.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../formatter.h"
#include <chrono>
#include <exception>
@@ -261,7 +261,7 @@ inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
wait_empty_q(); //return when queue is empty
}
inline void spdlog::details::async_log_helper::worker_loop()
@@ -280,9 +280,9 @@ inline void spdlog::details::async_log_helper::worker_loop()
{
_err_handler(ex.what());
}
catch (...)
catch(...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exeption in async logger worker loop.");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
@@ -376,15 +376,15 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20));
// sleep for 200 ms
return sleep_for(milliseconds(200));
// sleep for 500 ms
return sleep_for(milliseconds(500));
}
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (_q.approx_size() > 0)
while (!_q.is_empty())
{
sleep_or_yield(details::os::now(), last_op);
}

View File

@@ -8,8 +8,8 @@
// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread
#include "spdlog/details/async_log_helper.h"
#include "spdlog/async_logger.h"
#include "../details/async_log_helper.h"
#include "../async_logger.h"
#include <string>
#include <functional>
@@ -88,7 +88,7 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
try
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
_incr_msg_counter(msg);
#endif
_async_log_helper->log(msg);
if (_should_flush_on(msg))
@@ -98,8 +98,10 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
_err_handler(ex.what());
}
catch (...)
catch(...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}

View File

@@ -9,13 +9,14 @@
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Throw spdlog_ex exception on errors
#include "spdlog/details/os.h"
#include "spdlog/details/log_msg.h"
#include "../details/os.h"
#include "../details/log_msg.h"
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <tuple>
#include <cerrno>
namespace spdlog
@@ -84,14 +85,13 @@ public:
void write(const log_msg& msg)
{
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
}
size_t size()
size_t size() const
{
if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
@@ -103,10 +103,30 @@ public:
return _filename;
}
static bool file_exists(const filename_t& name)
static bool file_exists(const filename_t& fname)
{
return os::file_exists(fname);
}
return os::file_exists(name);
//
// return basename and extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
//
// the starting dot in filenames is ignored (hidden files):
//
// "my_folder/.mylog" => ("my_folder/.mylog")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extenstion(const filename_t& fname)
{
auto index = fname.rfind('.');
bool found_ext = index != filename_t::npos && index !=0 && fname[index - 1] != details::os::folder_sep;
if (found_ext)
return std::make_tuple(fname.substr(0, index), fname.substr(index));
else
return std::make_tuple(fname, filename_t());
}
private:

View File

@@ -5,8 +5,8 @@
#pragma once
#include "spdlog/common.h"
#include "spdlog/details/os.h"
#include "../common.h"
#include "../details/os.h"
#include <string>

View File

@@ -5,13 +5,12 @@
#pragma once
#include "spdlog/logger.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "../logger.h"
#include "../sinks/stdout_sinks.h"
#include <memory>
#include <string>
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
@@ -58,7 +57,6 @@ inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time
_set_pattern(pattern, pattern_time);
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{
@@ -67,16 +65,22 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
try
{
details::log_msg log_msg(&_name, lvl);
#if defined(SPDLOG_FMT_PRINTF)
fmt::printf(log_msg.raw, fmt, args...);
#else
log_msg.raw.write(fmt, args...);
#endif
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
catch(...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}
@@ -94,11 +98,6 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
template<typename T>
@@ -115,10 +114,6 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
@@ -158,78 +153,6 @@ inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Ar
log(level::critical, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg)
{
if (flag)
{
log(lvl, msg);
}
}
template<typename T>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg)
{
if (flag)
{
log(lvl, msg);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::trace, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::debug, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::info, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
{
if (flag)
{
log(level::warn, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::err, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, arg1, args...);
}
}
template<typename T>
inline void spdlog::logger::trace(const T& msg)
@@ -269,63 +192,11 @@ inline void spdlog::logger::critical(const T& msg)
log(level::critical, msg);
}
template<typename T>
inline void spdlog::logger::trace_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::trace, msg);
}
}
template<typename T>
inline void spdlog::logger::debug_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::debug, msg);
}
}
template<typename T>
inline void spdlog::logger::info_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::info, msg);
}
}
template<typename T>
inline void spdlog::logger::warn_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::warn, msg);
}
}
template<typename T>
inline void spdlog::logger::error_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::err, msg);
}
}
template<typename T>
inline void spdlog::logger::critical_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::critical, msg);
}
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <codecvt>
#include <locale>
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
@@ -381,83 +252,6 @@ inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
log(level::critical, fmt, args...);
}
//
// conditional logging
//
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
{
if (flag)
{
log(lvl, msg);
}
}
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(lvl, fmt, args);
}
}
template <typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::trace, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::debug, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::info, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::warn, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::err, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, args...);
}
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
@@ -507,7 +301,7 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
inline void spdlog::logger::_sink_it(details::log_msg& msg)
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
_incr_msg_counter(msg);
#endif
_formatter->format(msg);
for (auto &sink : _sinks)
@@ -557,7 +351,13 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
{
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

View File

@@ -43,7 +43,7 @@ Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include "spdlog/common.h"
#include "../common.h"
#include <atomic>
#include <utility>
@@ -61,11 +61,11 @@ public:
using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_(new cell_t[buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1)
@@ -76,7 +76,7 @@ public:
~mpmc_bounded_queue()
{
delete [] buffer_;
delete[] buffer_;
}
@@ -88,7 +88,7 @@ public:
{
cell = &buffer_[pos & buffer_mask_];
size_t seq = cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
@@ -117,7 +117,7 @@ public:
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
@@ -133,14 +133,18 @@ public:
return true;
}
size_t approx_size()
bool is_empty()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
size_t front, front1, back;
// try to take a consistent snapshot of front/tail.
do
{
front = enqueue_pos_.load(std::memory_order_acquire);
back = dequeue_pos_.load(std::memory_order_acquire);
front1 = enqueue_pos_.load(std::memory_order_relaxed);
}
while (front != front1);
return back == front;
}
private:
@@ -153,7 +157,7 @@ private:
size_t const max_size_;
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];
typedef char cacheline_pad_t[cacheline_size];
cacheline_pad_t pad0_;
cell_t* const buffer_;

View File

@@ -4,7 +4,7 @@
//
#pragma once
#include "spdlog/common.h"
#include "../common.h"
#include <cstdio>
#include <ctime>
@@ -143,6 +143,16 @@ 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;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
inline void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
@@ -221,7 +231,7 @@ inline size_t filesize(FILE *f)
{
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
#if defined ( _WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
@@ -236,12 +246,12 @@ inline size_t filesize(FILE *f)
#else // unix
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
//64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st;
if (fstat64(fd, &st) == 0)
return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx
#else // unix 32 bits or cygwin
struct stat st;
if (fstat(fd, &st) == 0)
return static_cast<size_t>(st.st_size);
@@ -316,7 +326,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)
//It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
inline size_t _thread_id()
{
#ifdef _WIN32
@@ -342,17 +352,17 @@ inline size_t _thread_id()
//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)
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || (defined(__clang__) && !__has_feature(cxx_thread_local))
return _thread_id();
#else
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s
@@ -424,7 +434,7 @@ inline int pid()
}
// Detrmine if the terminal supports colors
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal()
{

View File

@@ -5,10 +5,10 @@
#pragma once
#include "spdlog/formatter.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/fmt/fmt.h"
#include "../formatter.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../fmt/fmt.h"
#include <chrono>
#include <ctime>
@@ -99,7 +99,7 @@ class A_formatter:public flag_formatter
};
//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
@@ -119,14 +119,14 @@ class B_formatter:public flag_formatter
};
//write 2 ints seperated by sep with padding of 2
//write 2 ints separated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}
//write 3 ints seperated by sep with padding of 2
//write 3 ints separated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
@@ -262,6 +262,16 @@ class F_formatter SPDLOG_FINAL:public flag_formatter
}
};
class E_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
msg.formatted << seconds;
}
};
// AM/PM
class p_formatter SPDLOG_FINAL:public flag_formatter
{
@@ -374,6 +384,14 @@ class pid_formatter SPDLOG_FINAL:public flag_formatter
}
};
// message counter formatter
class i_formatter SPDLOG_FINAL :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
}
};
class v_formatter SPDLOG_FINAL:public flag_formatter
{
@@ -598,6 +616,10 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('E'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::E_formatter()));
break;
case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
@@ -627,11 +649,10 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
case ('i'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
break;
#endif
default: //Unknown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));

View File

@@ -10,10 +10,10 @@
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include "spdlog/details/null_mutex.h"
#include "spdlog/logger.h"
#include "spdlog/async_logger.h"
#include "spdlog/common.h"
#include "../details/null_mutex.h"
#include "../logger.h"
#include "../async_logger.h"
#include "../common.h"
#include <chrono>
#include <functional>

View File

@@ -8,23 +8,23 @@
//
// Global registry functions
//
#include "spdlog/spdlog.h"
#include "spdlog/details/registry.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "../spdlog.h"
#include "../details/registry.h"
#include "../sinks/file_sinks.h"
#include "../sinks/stdout_sinks.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/syslog_sink.h"
#include "../sinks/syslog_sink.h"
#endif
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink.h"
#include "../sinks/wincolor_sink.h"
#else
#include "spdlog/sinks/ansicolor_sink.h"
#include "../sinks/ansicolor_sink.h"
#endif
#ifdef __ANDROID__
#include "spdlog/sinks/android_sink.h"
#include "../sinks/android_sink.h"
#endif
#include <chrono>
@@ -162,9 +162,9 @@ inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string
#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)
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option, int syslog_facility)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
}
#endif

View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

View File

@@ -13,57 +13,65 @@
#include "format.h"
#include <ostream>
namespace fmt {
namespace fmt
{
namespace internal {
namespace internal
{
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
class FormatBuf : public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
{
buffer_.append(s, s + count);
return count;
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
struct DummyStream : std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
struct ConvertToIntImpl<T, true>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
@@ -73,16 +81,17 @@ FMT_API void write(std::ostream &os, Writer &w);
// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**

View File

@@ -64,112 +64,134 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt {
namespace fmt
{
// An error code.
class ErrorCode {
private:
int value_;
class ErrorCode
{
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class BufferedFile {
private:
FILE *file_;
class BufferedFile
{
private:
FILE *file_;
friend class 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_(FMT_NULL) {}
// Destroys the object closing the file it represents if any.
FMT_API ~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;
};
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT :
file_(FMT_NULL) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT;
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
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_ = FMT_NULL;
}
// 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_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
}
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
file_(other.file_)
{
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
BufferedFile& operator=(BufferedFile &&other)
{
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
FMT_API void close();
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
// 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.
FMT_API int (fileno)() const;
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int (fileno)() const;
void print(CStringRef format_str, const ArgList &args) {
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, CStringRef)
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.
@@ -178,125 +200,141 @@ public:
// 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.
class File
{
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
// 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.
};
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) {}
// 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.
FMT_API File(CStringRef path, int oflag);
// Opens a file and constructs a File object representing this file.
FMT_API 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.
// 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;
};
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) {}
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 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 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;
}
// 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;
}
// 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);
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
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;
}
File& operator=(File &&other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT;
// Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file.
FMT_API void close();
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API LongLong size() const;
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API 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.
FMT_API static File dup(int fd);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API 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.
FMT_API static void pipe(File &read_end, File &write_end);
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API 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.
FMT_API BufferedFile fdopen(const char *mode);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
FMT_API BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
@@ -309,58 +347,77 @@ long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
class Locale
{
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
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 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 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);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
{
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale()
{
freelocale(locale_);
}
Type get() const { return 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 = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const
{
char *end = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
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; }
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
}
#endif

View File

@@ -0,0 +1,32 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "format.h"
#include "printf.h"
namespace fmt {
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template void PrintfFormatter<char>::format(CStringRef format);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
#endif // FMT_HEADER_ONLY
} // namespace fmt

View File

@@ -0,0 +1,712 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt
{
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker
{
template <typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template <>
struct IntChecker<true>
{
template <typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg()
{
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value)
{
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
// returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char>
{
public:
char visit_char(int)
{
return 'c';
}
char visit_bool(bool)
{
return 's';
}
char visit_pointer(const void *)
{
return 'p';
}
template <typename T>
char visit_any_int(T)
{
return 'd';
}
template <typename T>
char visit_any_double(T)
{
return 'g';
}
char visit_unhandled_arg()
{
return 's';
}
};
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);
}
void visit_char(char value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's')
{
is_signed = std::numeric_limits<U>::is_signed;
}
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, typename Spec>
class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec>
{
private:
void write_null_pointer()
{
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
{
Spec &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 Spec &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, FormatSpec>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{
for (;;)
{
switch (*s++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index
{
++s;
arg_index = value;
}
else
{
if (c == '0')
spec.fill_ = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9')
{
spec.width_ = internal::parse_nonnegative_int(s);
}
else if (*s == '*')
{
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{
const Char *start = format_str.c_str();
const Char *s = start;
while (*s)
{
Char c = *s++;
if (c != '%') continue;
if (*s == c)
{
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
else
{
spec.precision_ = 0;
}
}
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 (spec.type_ == 's')
{
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
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);
}
inline void printf(Writer &w, CStringRef format, ArgList args)
{
PrintfFormatter<char>(args, w).format(format);
}
FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args)
{
PrintfFormatter<wchar_t>(args, w).format(format);
}
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@@ -19,120 +19,160 @@
# pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt {
namespace fmt
{
template <typename ArgFormatter>
void format_arg(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;
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));
}
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;
format_str = end + 1;
}
namespace internal{
inline Null<> localtime_r(...) { return Null<>(); }
inline Null<> localtime_s(...) { return Null<>(); }
inline Null<> gmtime_r(...) { return Null<>(); }
inline Null<> gmtime_s(...) { return Null<>(); }
namespace internal
{
inline Null<> localtime_r(...)
{
return Null<>();
}
inline Null<> localtime_s(...)
{
return Null<>();
}
inline Null<> gmtime_r(...)
{
return Null<>();
}
inline Null<> gmtime_s(...)
{
return Null<>();
}
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
std::time_t time_;
std::tm tm_;
inline std::tm localtime(std::time_t time)
{
struct LocalTime
{
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
LocalTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool run()
{
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
bool fallback(internal::Null<>)
{
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct GMTime {
std::time_t time_;
std::tm tm_;
inline std::tm gmtime(std::time_t time)
{
struct GMTime
{
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
GMTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool run()
{
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
bool fallback(internal::Null<>)
{
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt

View File

@@ -18,11 +18,17 @@
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include "spdlog/fmt/bundled/format.h"
#include "bundled/format.h"
#if defined(SPDLOG_FMT_PRINTF)
#include "bundled/printf.h"
#endif
#else //external fmtlib
#include <fmt/format.h>
#if defined(SPDLOG_FMT_PRINTF)
#include <fmt/printf.h>
#endif
#endif

View File

@@ -8,8 +8,8 @@
// include external or bundled copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/fmt.h"
#include "spdlog/fmt/bundled/ostream.h"
#include "fmt.h"
#include "bundled/ostream.h"
#else
#include <fmt/ostream.h>
#endif

View File

@@ -5,7 +5,7 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "details/log_msg.h"
#include <vector>
#include <string>
@@ -43,5 +43,5 @@ private:
};
}
#include "spdlog/details/pattern_formatter_impl.h"
#include "details/pattern_formatter_impl.h"

View File

@@ -12,8 +12,8 @@
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include "spdlog/sinks/base_sink.h"
#include "spdlog/common.h"
#include "sinks/base_sink.h"
#include "common.h"
#include <vector>
#include <memory>
@@ -44,14 +44,6 @@ public:
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... Args> void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* msg);
template <typename Arg1, typename... Args> void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg);
@@ -62,15 +54,6 @@ public:
template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args);
template <typename... Args> void trace_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void debug_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void info_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void warn_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void error_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void critical_if(const bool flag, const wchar_t* fmt, const Args&... args);
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename T> void log(level::level_enum lvl, const T&);
@@ -81,14 +64,6 @@ public:
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
template <typename T> void log_if(const bool flag, level::level_enum lvl, const T&);
template <typename T> void trace_if(const bool flag, const T&);
template <typename T> void debug_if(const bool flag, const T&);
template <typename T> void info_if(const bool flag, const T&);
template <typename T> void warn_if(const bool flag, const T&);
template <typename T> void error_if(const bool flag, const T&);
template <typename T> void critical_if(const bool flag, const T&);
bool should_log(level::level_enum) const;
void set_level(level::level_enum);
level::level_enum level() const;
@@ -118,6 +93,9 @@ protected:
// return true if the given message level should trigger a flush
bool _should_flush_on(const details::log_msg&);
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
void _incr_msg_counter(details::log_msg &msg);
const std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
@@ -129,4 +107,4 @@ protected:
};
}
#include "spdlog/details/logger_impl.h"
#include "details/logger_impl.h"

View File

@@ -7,7 +7,7 @@
#if defined(__ANDROID__)
#include "spdlog/sinks/sink.h"
#include "sink.h"
#include <mutex>
#include <string>

View File

@@ -5,9 +5,9 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/common.h"
#include "spdlog/details/os.h"
#include "base_sink.h"
#include "../common.h"
#include "../details/os.h"
#include <string>
#include <map>

View File

@@ -10,10 +10,10 @@
// all locking is taken care of here so no locking needed by the implementers..
//
#include "spdlog/sinks/sink.h"
#include "spdlog/formatter.h"
#include "spdlog/common.h"
#include "spdlog/details/log_msg.h"
#include "sink.h"
#include "../formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
#include <mutex>
@@ -38,6 +38,7 @@ public:
}
void flush() SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(_mutex);
_flush();
}

View File

@@ -5,10 +5,10 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/sinks/sink.h"
#include "../details/log_msg.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include "sink.h"
#include <algorithm>
#include <mutex>
@@ -46,7 +46,6 @@ protected:
void _flush() override
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto &sink : _sinks)
sink->flush();
}

View File

@@ -5,10 +5,10 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/file_helper.h"
#include "spdlog/fmt/fmt.h"
#include "base_sink.h"
#include "../details/null_mutex.h"
#include "../details/file_helper.h"
#include "../fmt/fmt.h"
#include <algorithm>
#include <chrono>
@@ -77,6 +77,23 @@ public:
_current_size = _file_helper.size(); //expensive. called only once
}
// calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
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)
{
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
}
else
{
w.write(SPDLOG_FILENAME_T("{}"), filename);
}
return w.str();
}
protected:
void _sink_it(const details::log_msg& msg) override
@@ -95,23 +112,13 @@ protected:
_file_helper.flush();
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
// log.txt -> log.1.txt
// log.1.txt -> log.2.txt
// log.2.txt -> log.3.txt
// log.3.txt -> delete
void _rotate()
{
using details::os::filename_to_str;
@@ -150,27 +157,31 @@ 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
static filename_t calc_filename(const filename_t& basename)
// Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
static filename_t calc_filename(const filename_t& filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
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);
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, ext);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
static filename_t calc_filename(const filename_t& filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
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);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
return w.str();
}
};

View File

@@ -5,12 +5,12 @@
#pragma once
#if defined(_MSC_VER)
#if defined(_WIN32)
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <WinBase.h>
#include <winbase.h>
#include <mutex>
#include <string>

View File

@@ -5,8 +5,8 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <mutex>

View File

@@ -5,8 +5,8 @@
#pragma once
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <ostream>
#include <mutex>

View File

@@ -6,7 +6,7 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "../details/log_msg.h"
namespace spdlog
{

View File

@@ -5,8 +5,8 @@
#pragma once
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <cstdio>
#include <memory>

View File

@@ -5,12 +5,12 @@
#pragma once
#include "spdlog/common.h"
#include "../common.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/sink.h"
#include "spdlog/details/log_msg.h"
#include "sink.h"
#include "../details/log_msg.h"
#include <array>
#include <string>

View File

@@ -5,9 +5,9 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/common.h"
#include "base_sink.h"
#include "../details/null_mutex.h"
#include "../common.h"
#include <mutex>
#include <string>

View File

@@ -0,0 +1,29 @@
//
// Copyright(c) 2017 Alexander Dalshov.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(_WIN32)
#include "msvc_sink.h"
namespace spdlog
{
namespace sinks
{
/*
* Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink)
*/
template<class Mutex>
using windebug_sink = msvc_sink<Mutex>;
typedef msvc_sink_mt windebug_sink_mt;
typedef msvc_sink_st windebug_sink_st;
}
}
#endif

View File

@@ -7,11 +7,11 @@
#pragma once
#define SPDLOG_VERSION "0.14.0"
#define SPDLOG_VERSION "0.16.1"
#include "spdlog/tweakme.h"
#include "spdlog/common.h"
#include "spdlog/logger.h"
#include "tweakme.h"
#include "common.h"
#include "logger.h"
#include <memory>
#include <functional>
@@ -106,7 +106,7 @@ std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
// Create and register a syslog logger
//
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1<<3));
#endif
#if defined(__ANDROID__)
@@ -154,7 +154,7 @@ void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
// SPDLOG_TRACE(..) will also print current file and line.
//
// Example:
@@ -162,28 +162,27 @@ void drop_all();
// SPDLOG_TRACE(my_logger, "some trace message");
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4);
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#ifdef _MSC_VER
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#else
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif
#else
#define SPDLOG_TRACE(logger, ...)
#define SPDLOG_TRACE_IF(logger, flag, ...)
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__)
#else
#define SPDLOG_DEBUG(logger, ...)
#define SPDLOG_DEBUG_IF(logger, flag, ...)
#endif
}
#include "spdlog/details/spdlog_impl.h"
#include "details/spdlog_impl.h"

View File

@@ -23,7 +23,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from quering the clock on each log call.
// This will prevent spdlog from querying the clock on each log call.
//
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
@@ -34,7 +34,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
// This will prevent spdlog from quering the thread id on each log call.
// This will prevent spdlog from querying the thread id on each log call.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
//
@@ -42,6 +42,16 @@
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent spdlog from caching thread ids in thread local storage.
// By default spdlog saves thread ids in tls to gain a few micros for each call.
//
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs.
//
// #define SPDLOG_DISABLE_TID_CACHING
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
@@ -59,7 +69,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifes concurrently the registry.
// Use only if your code never modifies concurrently the registry.
// Note that upon creating a logger the registry is modified by spdlog..
//
// #define SPDLOG_NO_REGISTRY_MUTEX
@@ -96,6 +106,14 @@
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use printf-style messages in your logs instead of the usual
// format-style used by default.
//
// #define SPDLOG_FMT_PRINTF
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default)
//
@@ -118,24 +136,25 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment to mark some types as final, allowing more optimizations in release
// Uncomment if your compiler doesn't support the "final" keyword.
// The final keyword allows more optimizations in release
// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
// for instance.
//
// #define SPDLOG_FINAL final
// #define SPDLOG_NO_FINAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable message counting feature. Adds %i logger pattern that
// prints log message sequence id.
// Uncomment to enable message counting feature.
// Use the %i in the logger pattern to display log message sequence id.
//
// #define SPDLOG_ENABLE_MESSAGE_COUNTER
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable user defined tag names
// Uncomment to customize level names (e.g. "MT TRACE")
//
// #define SPDLOG_LEVEL_NAMES { " TRACE", " DEBUG", " INFO",
// " WARNING", " ERROR", "CRITICAL", "OFF" };
///////////////////////////////////////////////////////////////////////////////
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
///////////////////////////////////////////////////////////////////////////////

View File

@@ -1,5 +1,11 @@
CXX ?= g++
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
ifeq ($(STYLE),printf)
$(info *** PRINTF STYLE ***)
CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O2 -I../include
else
$(info *** FORMAT STYLE ***)
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
endif
LDPFALGS = -pthread
CPP_FILES := $(wildcard *.cpp)

View File

@@ -1,154 +0,0 @@
#include "includes.h"
template<class T>
std::string conditional_log(const bool flag, const T& what, spdlog::level::level_enum logger_level)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_level(logger_level);
oss_logger.set_pattern("%v");
switch (logger_level)
{
case spdlog::level::trace:
oss_logger.trace_if(flag, what);
break;
case spdlog::level::debug:
oss_logger.debug_if(flag, what);
break;
case spdlog::level::info:
oss_logger.info_if(flag, what);
break;
case spdlog::level::warn:
oss_logger.warn_if(flag, what);
break;
case spdlog::level::err:
oss_logger.error_if(flag, what);
break;
case spdlog::level::critical:
oss_logger.critical_if(flag, what);
break;
default:
break;
}
return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
}
template <typename Arg1, typename... Args>
std::string conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_level(logger_level);
oss_logger.set_pattern("%v");
switch (logger_level)
{
case spdlog::level::trace:
oss_logger.trace_if(flag, fmt, arg1, args...);
break;
case spdlog::level::debug:
oss_logger.debug_if(flag, fmt, arg1, args...);
break;
case spdlog::level::info:
oss_logger.info_if(flag, fmt, arg1, args...);
break;
case spdlog::level::warn:
oss_logger.warn_if(flag, fmt, arg1, args...);
break;
case spdlog::level::err:
oss_logger.error_if(flag, fmt, arg1, args...);
break;
case spdlog::level::critical:
oss_logger.critical_if(flag, fmt, arg1, args...);
break;
default:
break;
}
return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename Arg1, typename... Args>
std::wstring conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const wchar_t* fmt, const Arg1& arg1, const Args&... args)
{
std::wstringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_level(logger_level);
oss_logger.set_pattern("%v");
switch (logger_level)
{
case spdlog::level::trace:
oss_logger.trace_if(flag, fmt, arg1, args...);
break;
case spdlog::level::debug:
oss_logger.debug_if(flag, fmt, arg1, args...);
break;
case spdlog::level::info:
oss_logger.info_if(flag, fmt, arg1, args...);
break;
case spdlog::level::warn:
oss_logger.warn_if(flag, fmt, arg1, args...);
break;
case spdlog::level::err:
oss_logger.error_if(flag, fmt, arg1, args...);
break;
case spdlog::level::critical:
oss_logger.critical_if(flag, fmt, arg1, args...);
break;
default:
break;
}
return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
TEST_CASE("conditional_trace_simple", "[conditional_trace_simple]")
{
//const char
for (auto i = 0; i < 2; i++)
{
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::trace) == ( i % 2 == 0 ? "Hello" : ""));
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::debug) == (i % 2 == 0 ? "Hello" : ""));
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::info) == (i % 2 == 0 ? "Hello" : ""));
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::warn) == (i % 2 == 0 ? "Hello" : ""));
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::err) == (i % 2 == 0 ? "Hello" : ""));
REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::critical) == (i % 2 == 0 ? "Hello" : ""));
}
}
TEST_CASE("conditional_trace_varargs", "[conditional_trace_varargs]")
{
//const char
for (auto i = 0; i < 2; i++)
{
REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
}
}

View File

@@ -15,7 +15,7 @@ class failing_sink: public spdlog::sinks::sink
throw std::runtime_error("some error happened during log");
}
void flush()
void flush() override
{}
};
@@ -26,8 +26,13 @@ TEST_CASE("default_error_handler", "[errors]]")
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
@@ -50,7 +55,11 @@ TEST_CASE("custom_error_handler", "[errors]]")
throw custom_ex();
});
logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
#else
REQUIRE_THROWS_AS(logger->info("Bad format msg %s %s", "xxx"), custom_ex);
#endif
logger->info("Good message #2");
REQUIRE(count_lines(filename) == 2);
}
@@ -81,7 +90,11 @@ TEST_CASE("async_error_handler", "[errors]]")
ofs << err_msg;
});
logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Bad format msg {} {}", "xxx");
#else
logger->info("Bad format msg %s %s", "xxx");
#endif
logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();

View File

@@ -73,6 +73,77 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE(helper.size() == expected_size);
}
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.txt");
REQUIRE(basename == "mylog");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion2", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog");
REQUIRE(basename == "mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion3", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz.txt");
REQUIRE(basename == "mylog.xyz");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion4", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz....txt");
REQUIRE(basename == "mylog.xyz...");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion5", "[file_helper::split_by_extenstion(hidden_file)]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(".mylog");
REQUIRE(basename == ".mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion6", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion7", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog.txt";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog.txt";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == ".txt");
}

View File

@@ -12,9 +12,13 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2);
@@ -33,8 +37,13 @@ TEST_CASE("flush_on", "[flush_on]]")
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
@@ -47,7 +56,13 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = basename;
@@ -67,7 +82,13 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
REQUIRE(get_filesize(filename) <= 1024);
@@ -88,7 +109,13 @@ TEST_CASE("daily_logger", "[daily_logger]]")
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
@@ -110,7 +137,13 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
@@ -142,10 +175,74 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
/*
* File name calculations
*/
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 3);
REQUIRE(filename == "rotated.3.txt");
}
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated", 3);
REQUIRE(filename == "rotated.3");
}
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 0);
REQUIRE(filename == "rotated.txt");
}
// regex supported only from gcc 4.9 and above
#if defined (_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
#include <regex>
TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily.txt");
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9].txt$)");
std::smatch match;
REQUIRE(std::regex_match(filename, match, re));
}
TEST_CASE("daily_file_sink::default_daily_file_name_calculator2", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily");
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9]$)");
std::smatch match;
REQUIRE(std::regex_match(filename, match, re));
}
TEST_CASE("daily_file_sink::dateonly_daily_file_name_calculator", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::dateonly_daily_file_name_calculator::calc_filename("daily.txt");
// date regex based on https://www.regular-expressions.info/dates.html
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)");
std::smatch match;
REQUIRE(std::regex_match(filename, match, re));
}
#endif

View File

@@ -6,10 +6,12 @@
#include <ostream>
#include <chrono>
#include <exception>
#include "catch.hpp"
#include "utils.h"
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h"
#include "../include/spdlog/sinks/ostream_sink.h"

50
tests/test_macros.cpp Normal file
View File

@@ -0,0 +1,50 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
TEST_CASE("debug and trace w/o format string", "[macros]]")
{
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);
SPDLOG_TRACE(logger, "Test message 1");
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message 2");
logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("debug and trace with format strings", "[macros]]")
{
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);
#if !defined(SPDLOG_FMT_PRINTF)
SPDLOG_TRACE(logger, "Test message {}", 1);
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message {}", 222);
#else
SPDLOG_TRACE(logger, "Test message %d", 1);
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message %d", 222);
#endif
logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
REQUIRE(count_lines(filename) == 2);
}

View File

@@ -125,13 +125,13 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="cond_logging.cpp" />
<ClCompile Include="errors.cpp" />
<ClCompile Include="file_helper.cpp" />
<ClCompile Include="file_log.cpp" />
<ClCompile Include="format.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="registry.cpp" />
<ClCompile Include="test_macros.cpp" />
<ClCompile Include="utils.cpp" />
</ItemGroup>
<ItemGroup>

View File

@@ -36,7 +36,7 @@
<ClCompile Include="errors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="cond_logging.cpp">
<ClCompile Include="test_macros.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

View File

@@ -46,3 +46,11 @@ std::size_t get_filesize(const std::string& filename)
return static_cast<std::size_t>(ifs.tellg());
}
// source: https://stackoverflow.com/a/2072890/192001
bool ends_with(std::string const & value, std::string const & ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}

View File

@@ -13,3 +13,4 @@ std::size_t count_lines(const std::string& filename);
std::size_t get_filesize(const std::string& filename);
bool ends_with(std::string const & value, std::string const & ending);