Merge branch 'v1.x' of git://github.com/gabime/spdlog into v1.x

pull/903/head
Shai Levi 7 years ago
commit da21a7a535

@ -305,7 +305,7 @@ void syslog_example()
--- ---
#### Android example #### Android example
```c++ ```c++
#incude "spdlog/sinks/android_sink.h" #include "spdlog/sinks/android_sink.h"
void android_example() void android_example()
{ {
std::string tag = "spdlog-android"; std::string tag = "spdlog-android";

@ -26,7 +26,7 @@ build_script:
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH% 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 .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_BENCH=OFF
cmake --build . --config %BUILD_TYPE% cmake --build . --config %BUILD_TYPE%
test: off test: off

@ -38,6 +38,12 @@ add_executable(async_bench async_bench.cpp)
target_link_libraries(async_bench spdlog::spdlog Threads::Threads) target_link_libraries(async_bench spdlog::spdlog Threads::Threads)
add_executable(latency latency.cpp) add_executable(latency latency.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
target_link_libraries(latency spdlog::spdlog Threads::Threads) target_link_libraries(latency spdlog::spdlog Threads::Threads)
add_executable(padder_bench padder_bench.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
target_link_libraries(padder_bench spdlog::spdlog Threads::Threads)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")

@ -1,9 +1,9 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 CXXFLAGS = -march=native -Wall -Wextra -pedantic -Wconversion -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -Ofast -flto -Wl,--no-as-needed CXX_RELEASE_FLAGS = -O3 -flto -Wl,--no-as-needed
binaries=bench latency async_bench binaries=bench.cpp async_bench latency padder_bench
all: $(binaries) all: $(binaries)
@ -16,12 +16,16 @@ async_bench: async_bench.cpp
latency: latency.cpp latency: latency.cpp
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
padder_bench: padder_bench.cpp
$(CXX) padder_bench.cpp -o padder_bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
.PHONY: clean .PHONY: clean
clean: clean:
rm -f *.o logs/* $(binaries) rm -f *.o logs/* latecy_logs $(binaries)
rebuild: clean all rebuild: clean all

@ -94,6 +94,7 @@ int main(int argc, char *argv[])
spdlog::info("Line count OK ({:n})\n", count); spdlog::info("Line count OK ({:n})\n", count);
} }
} }
spdlog::shutdown();
} }
catch (std::exception &ex) catch (std::exception &ex)
{ {
@ -101,6 +102,7 @@ int main(int argc, char *argv[])
perror("Last error"); perror("Last error");
return 1; return 1;
} }
return 0; return 0;
} }

@ -29,6 +29,7 @@ using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log); void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log); void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -81,6 +82,21 @@ int main(int argc, char *argv[])
bench_default_api(howmany, spdlog::create<null_sink_st>("null_st")); bench_default_api(howmany, spdlog::create<null_sink_st>("null_st"));
spdlog::info("**************************************************************");
spdlog::info("C-string (500 bytes). Single thread, {:n} iterations", howmany);
spdlog::info("**************************************************************");
basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true);
bench_c_string(howmany, std::move(basic_st));
rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files);
bench_c_string(howmany, std::move(rotating_st));
daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log");
bench_c_string(howmany, std::move(daily_st));
bench_c_string(howmany, spdlog::create<null_sink_st>("null_st"));
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany); spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany);
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
@ -173,3 +189,26 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
spdlog::set_default_logger(std::move(orig_default)); spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
} }
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
{
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
using std::chrono::high_resolution_clock;
auto orig_default = spdlog::default_logger();
spdlog::set_default_logger(log);
auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i)
{
spdlog::log(level::info, msg);
}
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
}

@ -7,6 +7,8 @@
// latency.cpp : spdlog latency benchmarks // latency.cpp : spdlog latency benchmarks
// //
#include "benchmark/benchmark.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/async.h" #include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"
@ -14,140 +16,129 @@
#include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h"
#include "utils.h" void prepare_logdir()
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int, char *[])
{ {
std::srand(static_cast<unsigned>(std::time(nullptr))); // use current time as seed for random generator spdlog::info("Preparing latency_logs directory..");
int howmany = 1000000; #ifdef _WIN32
int queue_size = howmany + 2; system("if not exist logs mkdir latency_logs");
int threads = 10; system("del /F /Q logs\\*");
size_t file_size = 30 * 1024 * 1024; #else
size_t rotating_files = 5; auto rv = system("mkdir -p latency_logs");
if (rv != 0)
try
{ {
throw std::runtime_error("Failed to mkdir -p latency_logs");
cout << "******************************************************************"
"*************\n";
cout << "Single thread\n";
cout << "******************************************************************"
"*************\n";
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n****************************************************************"
"***************\n";
cout << threads << " threads sharing same logger\n";
cout << "******************************************************************"
"*************\n";
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, basic_mt, threads);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
cout << "\n****************************************************************"
"***************\n";
cout << "async logging.. " << threads << " threads sharing same logger\n";
cout << "******************************************************************"
"*************\n";
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads);
spdlog::drop("async");
} }
} rv = system("rm -f latency_logs/*");
catch (std::exception &ex) if (rv != 0)
{ {
std::cerr << "Error: " << ex.what() << std::endl; throw std::runtime_error("Failed to rm -f latency_logs/*");
perror("Last error");
return EXIT_FAILURE;
} }
return EXIT_SUCCESS; #endif
} }
void bench(int howmany, std::shared_ptr<spdlog::logger> log) void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
{ {
using namespace std::chrono; const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
using chrono::high_resolution_clock; "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
using chrono::milliseconds; "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
using chrono::nanoseconds; "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
cout << log->name() << "...\t\t" << flush;
nanoseconds total_nanos = nanoseconds::zero(); for (auto _ : state)
for (auto i = 0; i < howmany; ++i)
{ {
auto start = high_resolution_clock::now(); logger->info(msg);
log->info("Hello logger: msg number {}", i);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos += delta_nanos;
} }
auto avg = total_nanos.count() / howmany;
cout << format(avg) << " ns/call" << endl;
} }
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count) void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
{ {
using namespace std::chrono; int i = 0;
using chrono::high_resolution_clock; for (auto _ : state)
using chrono::milliseconds;
using chrono::nanoseconds;
cout << log->name() << "...\t\t" << flush;
vector<thread> threads;
std::atomic<nanoseconds::rep> total_nanos{0};
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (int j = 0; j < howmany / thread_count; j++)
{ {
auto start = high_resolution_clock::now(); logger->info("Hello logger: msg number {}...............", ++i);
log->info("Hello logger: msg number {}", j);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos += delta_nanos.count();
}
}));
} }
}
for (auto &t : threads) void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
{
int i = 0;
benchmark::DoNotOptimize(i); // prevent unused warnings
benchmark::DoNotOptimize(logger); // prevent unused warnings
for (auto _ : state)
{ {
t.join(); SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++);
}; SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
}
}
int main(int argc, char *argv[])
{
spdlog::set_pattern("[tid %t] %v");
using spdlog::sinks::basic_file_sink_mt;
using spdlog::sinks::basic_file_sink_st;
using spdlog::sinks::null_sink_mt;
using spdlog::sinks::null_sink_st;
auto avg = total_nanos / howmany; size_t file_size = 30 * 1024 * 1024;
cout << format(avg) << " ns/call" << endl; size_t rotating_files = 5;
int n_threads = benchmark::CPUInfo::Get().num_cpus;
prepare_logdir();
// disabled loggers
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
disabled_logger->set_level(spdlog::level::off);
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
// basic_st
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
spdlog::drop("basic_st");
// rotating st
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
spdlog::drop("rotating_st");
// daily st
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
spdlog::drop("daily_st");
// //
// // Multi threaded bench, 10 loggers using same logger concurrently
// //
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
// basic_mt
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("basic_mt");
// rotating mt
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("rotating_mt");
// daily mt
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("daily_mt");
// async
auto queue_size = 1024 * 1024 * 3;
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto async_logger = std::make_shared<spdlog::async_logger>(
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime();
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
} }

@ -0,0 +1,51 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "benchmark/benchmark.h"
#include "spdlog/spdlog.h"
#include "spdlog/details/pattern_formatter.h"
void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo)
{
fmt::memory_buffer dest;
for (auto _ : state)
{
{
spdlog::details::scoped_pad p(wrapped_size, padinfo, dest);
benchmark::DoNotOptimize(p);
}
// if(dest.size() != (padinfo.width_-wrapped_size))
// {
// printf("NOT GOOD wrapped_size=%zu\t padinfo.width= %zu\tdest = %zu\n", wrapped_size, padinfo.width_, dest.size());
// }
dest.clear();
}
}
int main(int argc, char *argv[])
{
using spdlog::details::padding_info;
std::vector<size_t> sizes = {0, 2, 4, 8, 16, 32, 64, 128};
for (auto size : sizes)
{
size_t wrapped_size = 8;
size_t padding_size = wrapped_size + size;
std::string title = "scoped_pad::left::" + std::to_string(size);
benchmark::RegisterBenchmark(title.c_str(), bench_scoped_pad, wrapped_size, padding_info(padding_size, padding_info::left));
title = "scoped_pad::right::" + std::to_string(size);
benchmark::RegisterBenchmark(title.c_str(), bench_scoped_pad, wrapped_size, padding_info(padding_size, padding_info::right));
title = "scoped_pad::center::" + std::to_string(size);
benchmark::RegisterBenchmark(title.c_str(), bench_scoped_pad, wrapped_size, padding_info(padding_size, padding_info::center));
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
}

@ -22,8 +22,6 @@ void err_handler_example();
void syslog_example(); void syslog_example();
void clone_example(); void clone_example();
#define SPDLOG_TRACE_ON
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
int main(int, char *[]) int main(int, char *[])
@ -146,7 +144,11 @@ void async_example()
#include "spdlog/fmt/bin_to_hex.h" #include "spdlog/fmt/bin_to_hex.h"
void binary_example() void binary_example()
{ {
std::array<char, 80> buf; std::vector<char> buf;
for (int i = 0; i < 80; i++)
{
buf.push_back(static_cast<char>(i & 0xff));
}
spdlog::info("Binary example: {}", spdlog::to_hex(buf)); spdlog::info("Binary example: {}", spdlog::to_hex(buf));
spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
// more examples: // more examples:
@ -156,12 +158,18 @@ void binary_example()
} }
// Compile time log levels. // Compile time log levels.
// Must define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON before including spdlog.h to turn them on. // define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
void trace_example() void trace_example()
{ {
// trace from default logger
SPDLOG_TRACE("Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
// debug from default logger
SPDLOG_DEBUG("Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// trace from logger object
auto logger = spdlog::get("file_logger"); auto logger = spdlog::get("file_logger");
SPDLOG_TRACE(logger, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_LOGGER_TRACE(logger, "another trace message");
SPDLOG_DEBUG(logger, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
} }
// A logger with multiple sinks (stdout and file) - each with a different format and log level. // A logger with multiple sinks (stdout and file) - each with a different format and log level.

@ -25,7 +25,6 @@
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid> <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace> <RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

@ -52,7 +52,7 @@ struct async_factory_impl
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...); auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy); auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.register_and_init(new_logger); registry_inst.initialize_logger(new_logger);
return new_logger; return new_logger;
} }
}; };

@ -56,23 +56,42 @@ using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>; using sinks_init_list = std::initializer_list<sink_ptr>;
using log_err_handler = std::function<void(const std::string &err_msg)>; using log_err_handler = std::function<void(const std::string &err_msg)>;
// string_view type - either std::string_view or fmt::string_view (pre c++17)
#if defined(FMT_USE_STD_STRING_VIEW)
using string_view_t = std::string_view;
#else
using string_view_t = fmt::string_view;
#endif
#if defined(SPDLOG_NO_ATOMIC_LEVELS) #if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int; using level_t = details::null_atomic_int;
#else #else
using level_t = std::atomic<int>; using level_t = std::atomic<int>;
#endif #endif
#define SPDLOG_LEVEL_TRACE 0
#define SPDLOG_LEVEL_DEBUG 1
#define SPDLOG_LEVEL_INFO 2
#define SPDLOG_LEVEL_WARN 3
#define SPDLOG_LEVEL_ERROR 4
#define SPDLOG_LEVEL_CRITICAL 5
#define SPDLOG_LEVEL_OFF 6
#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif
// Log level enum // Log level enum
namespace level { namespace level {
enum level_enum enum level_enum
{ {
trace = 0, trace = SPDLOG_LEVEL_TRACE,
debug = 1, debug = SPDLOG_LEVEL_DEBUG,
info = 2, info = SPDLOG_LEVEL_INFO,
warn = 3, warn = SPDLOG_LEVEL_WARN,
err = 4, err = SPDLOG_LEVEL_ERROR,
critical = 5, critical = SPDLOG_LEVEL_CRITICAL,
off = 6 off = SPDLOG_LEVEL_OFF,
}; };
#if !defined(SPDLOG_LEVEL_NAMES) #if !defined(SPDLOG_LEVEL_NAMES)
@ -159,16 +178,6 @@ using filename_t = std::wstring;
using filename_t = std::string; using filename_t = std::string;
#endif #endif
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exeption in logger"); \
}
namespace details { namespace details {
// make_unique support for pre c++14 // make_unique support for pre c++14

@ -43,7 +43,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
#endif #endif
if (auto pool_ptr = thread_pool_.lock()) if (auto pool_ptr = thread_pool_.lock())
{ {
pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_); pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
} }
else else
{ {

@ -13,19 +13,10 @@ namespace details {
namespace fmt_helper { namespace fmt_helper {
template<size_t Buffer_Size> template<size_t Buffer_Size>
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
{ {
auto *str_ptr = str.data(); return spdlog::string_view_t(buf.data(), buf.size());
dest.append(str_ptr, str_ptr + str.size());
} }
template<size_t Buffer_Size>
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto len = std::char_traits<char>::length(c_str);
dest.append(c_str, c_str + len);
}
template<size_t Buffer_Size1, size_t Buffer_Size2> template<size_t Buffer_Size1, size_t Buffer_Size2>
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest) inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
{ {
@ -33,6 +24,16 @@ inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf,
dest.append(buf_ptr, buf_ptr + buf.size()); dest.append(buf_ptr, buf_ptr + buf.size());
} }
template<size_t Buffer_Size>
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto *buf_ptr = view.data();
if (buf_ptr != nullptr)
{
dest.append(buf_ptr, buf_ptr + view.size());
}
}
template<typename T, size_t Buffer_Size> template<typename T, size_t Buffer_Size>
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{ {

@ -15,9 +15,8 @@ namespace spdlog {
namespace details { namespace details {
struct log_msg struct log_msg
{ {
log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
: logger_name(loggers_name) : logger_name(loggers_name)
, level(lvl) , level(lvl)
#ifndef SPDLOG_NO_DATETIME #ifndef SPDLOG_NO_DATETIME
@ -26,24 +25,25 @@ struct log_msg
#ifndef SPDLOG_NO_THREAD_ID #ifndef SPDLOG_NO_THREAD_ID
, thread_id(os::thread_id()) , thread_id(os::thread_id())
, payload(view)
#endif #endif
{ {
} }
log_msg(const log_msg &other) = delete; log_msg(const log_msg &other) = default;
log_msg(log_msg &&other) = delete; log_msg &operator=(const log_msg &other) = default;
log_msg &operator=(log_msg &&other) = delete;
const std::string *logger_name{nullptr}; const std::string *logger_name{nullptr};
level::level_enum level; level::level_enum level{level::off};
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id{0};
fmt::memory_buffer raw; size_t msg_id{0};
size_t msg_id;
// info about wrapping the formatted text with color (updated by pattern_formatter). // wrapping the formatted text with color (updated by pattern_formatter).
mutable size_t color_range_start{0}; mutable size_t color_range_start{0};
mutable size_t color_range_end{0}; mutable size_t color_range_end{0};
const string_view_t payload;
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog

@ -10,19 +10,23 @@
#include <memory> #include <memory>
#include <string> #include <string>
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exception in logger"); \
}
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<typename It> template<typename It>
inline spdlog::logger::logger(std::string logger_name, It begin, It end) inline spdlog::logger::logger(std::string logger_name, It begin, It end)
: name_(std::move(logger_name)) : name_(std::move(logger_name))
, sinks_(begin, end) , sinks_(begin, end)
, level_(level::info)
, flush_level_(level::off)
, last_err_time_(0)
, msg_counter_(1) // message counter will start from 1. 0-message id will be
// reserved for controll messages
{ {
err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
} }
// ctor with sinks as init list // ctor with sinks as init list
@ -63,30 +67,31 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar
try try
{ {
details::log_msg log_msg(&name_, lvl); using details::fmt_helper::to_string_view;
fmt::format_to(log_msg.raw, fmt, args...); fmt::memory_buffer buf;
fmt::format_to(buf, fmt, args...);
details::log_msg log_msg(&name_, lvl, to_string_view(buf));
sink_it_(log_msg); sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
} }
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *msg) inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{ {
if (!should_log(lvl)) if (!should_log(lvl))
{ {
return; return;
} }
try try
{ {
details::log_msg log_msg(&name_, lvl); details::log_msg log_msg(&name_, lvl, spdlog::string_view_t(msg));
details::fmt_helper::append_c_str(msg, log_msg.raw);
sink_it_(log_msg); sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
} }
template<typename T> template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg) inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{ {
if (!should_log(lvl)) if (!should_log(lvl))
@ -95,8 +100,25 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
} }
try try
{ {
details::log_msg log_msg(&name_, lvl); details::log_msg log_msg(&name_, lvl, msg);
fmt::format_to(log_msg.raw, "{}", msg); sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
if (!should_log(lvl))
{
return;
}
try
{
using details::fmt_helper::to_string_view;
fmt::memory_buffer buf;
fmt::format_to(buf, "{}", msg);
details::log_msg log_msg(&name_, lvl, to_string_view(buf));
sink_it_(log_msg); sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
@ -208,10 +230,12 @@ inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const
try try
{ {
// format to wmemory_buffer and convert to utf8 // format to wmemory_buffer and convert to utf8
details::log_msg log_msg(&name_, lvl); using details::fmt_helper::to_string_view;
fmt::wmemory_buffer wbuf; fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, args...); fmt::format_to(wbuf, fmt, args...);
wbuf_to_utf8buf(wbuf, log_msg.raw); fmt::memory_buffer buf;
wbuf_to_utf8buf(wbuf, buf);
details::log_msg log_msg(&name_, lvl, to_string_view(buf));
sink_it_(log_msg); sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
@ -273,7 +297,7 @@ inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handle
err_handler_ = std::move(err_handler); err_handler_ = std::move(err_handler);
} }
inline spdlog::log_err_handler spdlog::logger::error_handler() inline spdlog::log_err_handler spdlog::logger::error_handler() const
{ {
return err_handler_; return err_handler_;
} }
@ -303,6 +327,11 @@ inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
return (msg.level >= flush_level) && (msg.level != level::off); return (msg.level >= flush_level) && (msg.level != level::off);
} }
inline spdlog::level::level_enum spdlog::logger::default_level()
{
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
}
inline spdlog::level::level_enum spdlog::logger::level() const inline spdlog::level::level_enum spdlog::logger::level() const
{ {
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed)); return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));

@ -14,6 +14,7 @@
#include <array> #include <array>
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <cctype>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
@ -24,39 +25,148 @@
namespace spdlog { namespace spdlog {
namespace details { namespace details {
// padding information.
struct padding_info
{
enum pad_side
{
left,
right,
center
};
padding_info() = default;
padding_info(size_t width, padding_info::pad_side side)
: width_(width)
, side_(side)
{
}
const size_t width_ = 0;
const pad_side side_ = left;
};
class scoped_pad
{
public:
scoped_pad(size_t wrapped_size, padding_info &padinfo, fmt::memory_buffer &dest)
: padinfo_(padinfo)
, dest_(dest)
{
if (padinfo_.width_ <= wrapped_size)
{
total_pad_ = 0;
return;
}
total_pad_ = padinfo.width_ - wrapped_size;
if (padinfo_.side_ == padding_info::left)
{
pad_it(total_pad_);
total_pad_ = 0;
}
else if (padinfo_.side_ == padding_info::center)
{
auto half_pad = total_pad_ / 2;
auto reminder = total_pad_ & 1;
pad_it(half_pad);
total_pad_ = half_pad + reminder; // for the right side
}
}
scoped_pad(spdlog::string_view_t txt, padding_info &padinfo, fmt::memory_buffer &dest)
: scoped_pad(txt.size(), padinfo, dest)
{
}
~scoped_pad()
{
if (total_pad_)
{
pad_it(total_pad_);
}
}
private:
void pad_it(size_t count)
{
// count = std::min(count, spaces_.size());
assert(count <= spaces_.size());
fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_);
}
const padding_info &padinfo_;
fmt::memory_buffer &dest_;
size_t total_pad_;
string_view_t spaces_{" "
" ",
128};
};
class flag_formatter class flag_formatter
{ {
public: public:
explicit flag_formatter(padding_info padinfo)
: padinfo_(padinfo)
{
}
flag_formatter() = default;
virtual ~flag_formatter() = default; virtual ~flag_formatter() = default;
virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0; virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0;
protected:
padding_info padinfo_;
}; };
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// name & level pattern appenders // name & level pattern appender
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
class name_formatter : public flag_formatter class name_formatter : public flag_formatter
{ {
public:
explicit name_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_str(*msg.logger_name, dest); scoped_pad p(*msg.logger_name, padinfo_, dest);
fmt_helper::append_string_view(*msg.logger_name, dest);
} }
}; };
// log level appender // log level appender
class level_formatter : public flag_formatter class level_formatter : public flag_formatter
{ {
public:
explicit level_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(level::to_c_str(msg.level), dest); string_view_t level_name{level::to_c_str(msg.level)};
scoped_pad p(level_name, padinfo_, dest);
fmt_helper::append_string_view(level_name, dest);
} }
}; };
// short log level appender // short log level appender
class short_level_formatter : public flag_formatter class short_level_formatter : public flag_formatter
{ {
public:
explicit short_level_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(level::to_short_c_str(msg.level), dest); string_view_t level_name{level::to_short_c_str(msg.level)};
scoped_pad p(level_name, padinfo_, dest);
fmt_helper::append_string_view(level_name, dest);
} }
}; };
@ -78,9 +188,17 @@ static int to12h(const tm &t)
static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
class a_formatter : public flag_formatter class a_formatter : public flag_formatter
{ {
public:
explicit a_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(days[tm_time.tm_wday], dest); string_view_t field_value{days[tm_time.tm_wday]};
scoped_pad p(field_value, padinfo_, dest);
fmt_helper::append_string_view(field_value, dest);
} }
}; };
@ -88,9 +206,17 @@ class a_formatter : public flag_formatter
static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
class A_formatter : public flag_formatter class A_formatter : public flag_formatter
{ {
public:
explicit A_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(full_days[tm_time.tm_wday], dest); string_view_t field_value{full_days[tm_time.tm_wday]};
scoped_pad p(field_value, padinfo_, dest);
fmt_helper::append_string_view(field_value, dest);
} }
}; };
@ -98,9 +224,17 @@ class A_formatter : public flag_formatter
static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
class b_formatter : public flag_formatter class b_formatter : public flag_formatter
{ {
public:
explicit b_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(months[tm_time.tm_mon], dest); string_view_t field_value{months[tm_time.tm_mon]};
scoped_pad p(field_value, padinfo_, dest);
fmt_helper::append_string_view(field_value, dest);
} }
}; };
@ -109,23 +243,37 @@ static const char *full_months[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
class B_formatter : public flag_formatter class B_formatter : public flag_formatter
{ {
public:
explicit B_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(full_months[tm_time.tm_mon], dest); string_view_t field_value{full_months[tm_time.tm_mon]};
scoped_pad p(field_value, padinfo_, dest);
fmt_helper::append_string_view(field_value, dest);
} }
}; };
// Date and time representation (Thu Aug 23 15:35:46 2014) // Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter final : public flag_formatter class c_formatter final : public flag_formatter
{ {
public:
explicit c_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
// fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday], const size_t field_size = 24;
// months[tm_time.tm_mon], tm_time.tm_mday); scoped_pad p(field_size, padinfo_, dest);
// date
fmt_helper::append_c_str(days[tm_time.tm_wday], dest); fmt_helper::append_string_view(days[tm_time.tm_wday], dest);
dest.push_back(' '); dest.push_back(' ');
fmt_helper::append_c_str(months[tm_time.tm_mon], dest); fmt_helper::append_string_view(months[tm_time.tm_mon], dest);
dest.push_back(' '); dest.push_back(' ');
fmt_helper::append_int(tm_time.tm_mday, dest); fmt_helper::append_int(tm_time.tm_mday, dest);
dest.push_back(' '); dest.push_back(' ');
@ -144,8 +292,16 @@ class c_formatter final : public flag_formatter
// year - 2 digit // year - 2 digit
class C_formatter final : public flag_formatter class C_formatter final : public flag_formatter
{ {
public:
explicit C_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_year % 100, dest); fmt_helper::pad2(tm_time.tm_year % 100, dest);
} }
}; };
@ -153,8 +309,17 @@ class C_formatter final : public flag_formatter
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter final : public flag_formatter class D_formatter final : public flag_formatter
{ {
public:
explicit D_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 10;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_mon + 1, dest); fmt_helper::pad2(tm_time.tm_mon + 1, dest);
dest.push_back('/'); dest.push_back('/');
fmt_helper::pad2(tm_time.tm_mday, dest); fmt_helper::pad2(tm_time.tm_mday, dest);
@ -166,8 +331,14 @@ class D_formatter final : public flag_formatter
// year - 4 digit // year - 4 digit
class Y_formatter final : public flag_formatter class Y_formatter final : public flag_formatter
{ {
public:
explicit Y_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 4;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::append_int(tm_time.tm_year + 1900, dest); fmt_helper::append_int(tm_time.tm_year + 1900, dest);
} }
}; };
@ -175,8 +346,16 @@ class Y_formatter final : public flag_formatter
// month 1-12 // month 1-12
class m_formatter final : public flag_formatter class m_formatter final : public flag_formatter
{ {
public:
explicit m_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_mon + 1, dest); fmt_helper::pad2(tm_time.tm_mon + 1, dest);
} }
}; };
@ -184,8 +363,16 @@ class m_formatter final : public flag_formatter
// day of month 1-31 // day of month 1-31
class d_formatter final : public flag_formatter class d_formatter final : public flag_formatter
{ {
public:
explicit d_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_mday, dest); fmt_helper::pad2(tm_time.tm_mday, dest);
} }
}; };
@ -193,8 +380,16 @@ class d_formatter final : public flag_formatter
// hours in 24 format 0-23 // hours in 24 format 0-23
class H_formatter final : public flag_formatter class H_formatter final : public flag_formatter
{ {
public:
explicit H_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_hour, dest); fmt_helper::pad2(tm_time.tm_hour, dest);
} }
}; };
@ -202,8 +397,14 @@ class H_formatter final : public flag_formatter
// hours in 12 format 1-12 // hours in 12 format 1-12
class I_formatter final : public flag_formatter class I_formatter final : public flag_formatter
{ {
public:
explicit I_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(to12h(tm_time), dest); fmt_helper::pad2(to12h(tm_time), dest);
} }
}; };
@ -211,8 +412,14 @@ class I_formatter final : public flag_formatter
// minutes 0-59 // minutes 0-59
class M_formatter final : public flag_formatter class M_formatter final : public flag_formatter
{ {
public:
explicit M_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_min, dest); fmt_helper::pad2(tm_time.tm_min, dest);
} }
}; };
@ -220,8 +427,14 @@ class M_formatter final : public flag_formatter
// seconds 0-59 // seconds 0-59
class S_formatter final : public flag_formatter class S_formatter final : public flag_formatter
{ {
public:
explicit S_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_sec, dest); fmt_helper::pad2(tm_time.tm_sec, dest);
} }
}; };
@ -229,8 +442,15 @@ class S_formatter final : public flag_formatter
// milliseconds // milliseconds
class e_formatter final : public flag_formatter class e_formatter final : public flag_formatter
{ {
public:
explicit e_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 3;
scoped_pad p(field_size, padinfo_, dest);
auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time); auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
fmt_helper::pad3(static_cast<int>(millis.count()), dest); fmt_helper::pad3(static_cast<int>(millis.count()), dest);
} }
@ -239,8 +459,15 @@ class e_formatter final : public flag_formatter
// microseconds // microseconds
class f_formatter final : public flag_formatter class f_formatter final : public flag_formatter
{ {
public:
explicit f_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 6;
scoped_pad p(field_size, padinfo_, dest);
auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time); auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
fmt_helper::pad6(static_cast<size_t>(micros.count()), dest); fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
} }
@ -249,8 +476,15 @@ class f_formatter final : public flag_formatter
// nanoseconds // nanoseconds
class F_formatter final : public flag_formatter class F_formatter final : public flag_formatter
{ {
public:
explicit F_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 9;
scoped_pad p(field_size, padinfo_, dest);
auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time); auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
fmt::format_to(dest, "{:09}", ns.count()); fmt::format_to(dest, "{:09}", ns.count());
} }
@ -259,8 +493,15 @@ class F_formatter final : public flag_formatter
// seconds since epoch // seconds since epoch
class E_formatter final : public flag_formatter class E_formatter final : public flag_formatter
{ {
public:
explicit E_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 10;
scoped_pad p(field_size, padinfo_, dest);
auto duration = msg.time.time_since_epoch(); auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count(); auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
fmt_helper::append_int(seconds, dest); fmt_helper::append_int(seconds, dest);
@ -270,32 +511,52 @@ class E_formatter final : public flag_formatter
// AM/PM // AM/PM
class p_formatter final : public flag_formatter class p_formatter final : public flag_formatter
{ {
public:
explicit p_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_c_str(ampm(tm_time), dest); const size_t field_size = 2;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::append_string_view(ampm(tm_time), dest);
} }
}; };
// 12 hour clock 02:55:02 pm // 12 hour clock 02:55:02 pm
class r_formatter final : public flag_formatter class r_formatter final : public flag_formatter
{ {
public:
explicit r_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 11;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(to12h(tm_time), dest); fmt_helper::pad2(to12h(tm_time), dest);
dest.push_back(':'); dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest); fmt_helper::pad2(tm_time.tm_min, dest);
dest.push_back(':'); dest.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, dest); fmt_helper::pad2(tm_time.tm_sec, dest);
dest.push_back(' '); dest.push_back(' ');
fmt_helper::append_c_str(ampm(tm_time), dest); fmt_helper::append_string_view(ampm(tm_time), dest);
} }
}; };
// 24-hour HH:MM time, equivalent to %H:%M // 24-hour HH:MM time, equivalent to %H:%M
class R_formatter final : public flag_formatter class R_formatter final : public flag_formatter
{ {
public:
explicit R_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 5;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_hour, dest); fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':'); dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest); fmt_helper::pad2(tm_time.tm_min, dest);
@ -305,10 +566,16 @@ class R_formatter final : public flag_formatter
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter final : public flag_formatter class T_formatter final : public flag_formatter
{ {
public:
explicit T_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
// fmt::format_to(dest, "{:02}:{:02}:{:02}", tm_time.tm_hour,
// tm_time.tm_min, tm_time.tm_sec); const size_t field_size = 8;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad2(tm_time.tm_hour, dest); fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':'); dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest); fmt_helper::pad2(tm_time.tm_min, dest);
@ -321,6 +588,9 @@ class T_formatter final : public flag_formatter
class z_formatter final : public flag_formatter class z_formatter final : public flag_formatter
{ {
public: public:
explicit z_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
const std::chrono::seconds cache_refresh = std::chrono::seconds(5); const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter() = default; z_formatter() = default;
@ -329,6 +599,9 @@ public:
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 6;
scoped_pad p(field_size, padinfo_, dest);
#ifdef _WIN32 #ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time); int total_minutes = get_cached_offset(msg, tm_time);
#else #else
@ -373,8 +646,14 @@ private:
// Thread id // Thread id
class t_formatter final : public flag_formatter class t_formatter final : public flag_formatter
{ {
public:
explicit t_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 6;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad6(msg.thread_id, dest); fmt_helper::pad6(msg.thread_id, dest);
} }
}; };
@ -382,38 +661,59 @@ class t_formatter final : public flag_formatter
// Current pid // Current pid
class pid_formatter final : public flag_formatter class pid_formatter final : public flag_formatter
{ {
public:
explicit pid_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_int(details::os::pid(), dest); const size_t field_size = 6;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad6(static_cast<size_t>(details::os::pid()), dest);
} }
}; };
// message counter formatter // message counter formatter
class i_formatter final : public flag_formatter class i_formatter final : public flag_formatter
{ {
public:
explicit i_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 6;
scoped_pad p(field_size, padinfo_, dest);
fmt_helper::pad6(msg.msg_id, dest); fmt_helper::pad6(msg.msg_id, dest);
} }
}; };
class v_formatter final : public flag_formatter class v_formatter final : public flag_formatter
{ {
public:
explicit v_formatter(padding_info padinfo)
: flag_formatter(padinfo){};
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_buf(msg.raw, dest); scoped_pad p(msg.payload, padinfo_, dest);
fmt_helper::append_string_view(msg.payload, dest);
} }
}; };
class ch_formatter final : public flag_formatter class ch_formatter final : public flag_formatter
{ {
public:
public: public:
explicit ch_formatter(char ch) explicit ch_formatter(char ch)
: ch_(ch) : ch_(ch)
{ {
} }
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{ {
const size_t field_size = 1;
scoped_pad p(field_size, padinfo_, dest);
dest.push_back(ch_); dest.push_back(ch_);
} }
@ -433,7 +733,7 @@ public:
} }
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{ {
fmt_helper::append_str(str_, dest); fmt_helper::append_string_view(str_, dest);
} }
private: private:
@ -443,6 +743,12 @@ private:
// mark the color range. expect it to be in the form of "%^colored text%$" // mark the color range. expect it to be in the form of "%^colored text%$"
class color_start_formatter final : public flag_formatter class color_start_formatter final : public flag_formatter
{ {
public:
explicit color_start_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
msg.color_range_start = dest.size(); msg.color_range_start = dest.size();
@ -450,6 +756,12 @@ class color_start_formatter final : public flag_formatter
}; };
class color_stop_formatter final : public flag_formatter class color_stop_formatter final : public flag_formatter
{ {
public:
explicit color_stop_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
msg.color_range_end = dest.size(); msg.color_range_end = dest.size();
@ -460,6 +772,12 @@ class color_stop_formatter final : public flag_formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter final : public flag_formatter class full_formatter final : public flag_formatter
{ {
public:
explicit full_formatter(padding_info padinfo)
: flag_formatter(padinfo)
{
}
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
using std::chrono::duration_cast; using std::chrono::duration_cast;
@ -511,7 +829,8 @@ class full_formatter final : public flag_formatter
if (!msg.logger_name->empty()) if (!msg.logger_name->empty())
{ {
dest.push_back('['); dest.push_back('[');
fmt_helper::append_str(*msg.logger_name, dest); // fmt_helper::append_str(*msg.logger_name, dest);
fmt_helper::append_string_view(*msg.logger_name, dest);
dest.push_back(']'); dest.push_back(']');
dest.push_back(' '); dest.push_back(' ');
} }
@ -520,11 +839,13 @@ class full_formatter final : public flag_formatter
dest.push_back('['); dest.push_back('[');
// wrap the level name with color // wrap the level name with color
msg.color_range_start = dest.size(); msg.color_range_start = dest.size();
fmt_helper::append_c_str(level::to_c_str(msg.level), dest); // fmt_helper::append_string_view(level::to_c_str(msg.level), dest);
fmt_helper::append_string_view(level::to_c_str(msg.level), dest);
msg.color_range_end = dest.size(); msg.color_range_end = dest.size();
dest.push_back(']'); dest.push_back(']');
dest.push_back(' '); dest.push_back(' ');
fmt_helper::append_buf(msg.raw, dest); // fmt_helper::append_string_view(msg.msg(), dest);
fmt_helper::append_string_view(msg.payload, dest);
} }
private: private:
@ -548,6 +869,17 @@ public:
compile_pattern_(pattern_); compile_pattern_(pattern_);
} }
// use by default full formatter for if pattern is not given
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol)
: pattern_("%+")
, eol_(std::move(eol))
, pattern_time_type_(time_type)
, last_log_secs_(0)
{
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
}
pattern_formatter(const pattern_formatter &other) = delete; pattern_formatter(const pattern_formatter &other) = delete;
pattern_formatter &operator=(const pattern_formatter &other) = delete; pattern_formatter &operator=(const pattern_formatter &other) = delete;
@ -571,7 +903,7 @@ public:
f->format(msg, cached_tm_, dest); f->format(msg, cached_tm_, dest);
} }
// write eol // write eol
details::fmt_helper::append_c_str(eol_.c_str(), dest); details::fmt_helper::append_string_view(eol_, dest);
} }
private: private:
@ -592,149 +924,198 @@ private:
return details::os::gmtime(log_clock::to_time_t(msg.time)); return details::os::gmtime(log_clock::to_time_t(msg.time));
} }
void handle_flag_(char flag) void handle_flag_(char flag, details::padding_info padding)
{ {
switch (flag) switch (flag)
{ {
// logger name // logger name
case 'n': case 'n':
formatters_.push_back(details::make_unique<details::name_formatter>()); formatters_.push_back(details::make_unique<details::name_formatter>(padding));
break; break;
case 'l': case 'l':
formatters_.push_back(details::make_unique<details::level_formatter>()); formatters_.push_back(details::make_unique<details::level_formatter>(padding));
break; break;
case 'L': case 'L':
formatters_.push_back(details::make_unique<details::short_level_formatter>()); formatters_.push_back(details::make_unique<details::short_level_formatter>(padding));
break; break;
case ('t'): case ('t'):
formatters_.push_back(details::make_unique<details::t_formatter>()); formatters_.push_back(details::make_unique<details::t_formatter>(padding));
break; break;
case ('v'): case ('v'):
formatters_.push_back(details::make_unique<details::v_formatter>()); formatters_.push_back(details::make_unique<details::v_formatter>(padding));
break; break;
case ('a'): case ('a'):
formatters_.push_back(details::make_unique<details::a_formatter>()); formatters_.push_back(details::make_unique<details::a_formatter>(padding));
break; break;
case ('A'): case ('A'):
formatters_.push_back(details::make_unique<details::A_formatter>()); formatters_.push_back(details::make_unique<details::A_formatter>(padding));
break; break;
case ('b'): case ('b'):
case ('h'): case ('h'):
formatters_.push_back(details::make_unique<details::b_formatter>()); formatters_.push_back(details::make_unique<details::b_formatter>(padding));
break; break;
case ('B'): case ('B'):
formatters_.push_back(details::make_unique<details::B_formatter>()); formatters_.push_back(details::make_unique<details::B_formatter>(padding));
break; break;
case ('c'): case ('c'):
formatters_.push_back(details::make_unique<details::c_formatter>()); formatters_.push_back(details::make_unique<details::c_formatter>(padding));
break; break;
case ('C'): case ('C'):
formatters_.push_back(details::make_unique<details::C_formatter>()); formatters_.push_back(details::make_unique<details::C_formatter>(padding));
break; break;
case ('Y'): case ('Y'):
formatters_.push_back(details::make_unique<details::Y_formatter>()); formatters_.push_back(details::make_unique<details::Y_formatter>(padding));
break; break;
case ('D'): case ('D'):
case ('x'): case ('x'):
formatters_.push_back(details::make_unique<details::D_formatter>()); formatters_.push_back(details::make_unique<details::D_formatter>(padding));
break; break;
case ('m'): case ('m'):
formatters_.push_back(details::make_unique<details::m_formatter>()); formatters_.push_back(details::make_unique<details::m_formatter>(padding));
break; break;
case ('d'): case ('d'):
formatters_.push_back(details::make_unique<details::d_formatter>()); formatters_.push_back(details::make_unique<details::d_formatter>(padding));
break; break;
case ('H'): case ('H'):
formatters_.push_back(details::make_unique<details::H_formatter>()); formatters_.push_back(details::make_unique<details::H_formatter>(padding));
break; break;
case ('I'): case ('I'):
formatters_.push_back(details::make_unique<details::I_formatter>()); formatters_.push_back(details::make_unique<details::I_formatter>(padding));
break; break;
case ('M'): case ('M'):
formatters_.push_back(details::make_unique<details::M_formatter>()); formatters_.push_back(details::make_unique<details::M_formatter>(padding));
break; break;
case ('S'): case ('S'):
formatters_.push_back(details::make_unique<details::S_formatter>()); formatters_.push_back(details::make_unique<details::S_formatter>(padding));
break; break;
case ('e'): case ('e'):
formatters_.push_back(details::make_unique<details::e_formatter>()); formatters_.push_back(details::make_unique<details::e_formatter>(padding));
break; break;
case ('f'): case ('f'):
formatters_.push_back(details::make_unique<details::f_formatter>()); formatters_.push_back(details::make_unique<details::f_formatter>(padding));
break; break;
case ('F'): case ('F'):
formatters_.push_back(details::make_unique<details::F_formatter>()); formatters_.push_back(details::make_unique<details::F_formatter>(padding));
break; break;
case ('E'): case ('E'):
formatters_.push_back(details::make_unique<details::E_formatter>()); formatters_.push_back(details::make_unique<details::E_formatter>(padding));
break; break;
case ('p'): case ('p'):
formatters_.push_back(details::make_unique<details::p_formatter>()); formatters_.push_back(details::make_unique<details::p_formatter>(padding));
break; break;
case ('r'): case ('r'):
formatters_.push_back(details::make_unique<details::r_formatter>()); formatters_.push_back(details::make_unique<details::r_formatter>(padding));
break; break;
case ('R'): case ('R'):
formatters_.push_back(details::make_unique<details::R_formatter>()); formatters_.push_back(details::make_unique<details::R_formatter>(padding));
break; break;
case ('T'): case ('T'):
case ('X'): case ('X'):
formatters_.push_back(details::make_unique<details::T_formatter>()); formatters_.push_back(details::make_unique<details::T_formatter>(padding));
break; break;
case ('z'): case ('z'):
formatters_.push_back(details::make_unique<details::z_formatter>()); formatters_.push_back(details::make_unique<details::z_formatter>(padding));
break; break;
case ('+'): case ('+'):
formatters_.push_back(details::make_unique<details::full_formatter>()); formatters_.push_back(details::make_unique<details::full_formatter>(padding));
break; break;
case ('P'): case ('P'):
formatters_.push_back(details::make_unique<details::pid_formatter>()); formatters_.push_back(details::make_unique<details::pid_formatter>(padding));
break; break;
#ifdef SPDLOG_ENABLE_MESSAGE_COUNTER #ifdef SPDLOG_ENABLE_MESSAGE_COUNTER
case ('i'): case ('i'):
formatters_.push_back(details::make_unique<details::i_formatter>()); formatters_.push_back(details::make_unique<details::i_formatter>(padding));
break; break;
#endif #endif
case ('^'): case ('^'):
formatters_.push_back(details::make_unique<details::color_start_formatter>()); formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
break; break;
case ('$'): case ('$'):
formatters_.push_back(details::make_unique<details::color_stop_formatter>()); formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
break; break;
default: // Unknown flag appears as is case ('%'):
formatters_.push_back(details::make_unique<details::ch_formatter>('%')); formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
formatters_.push_back(details::make_unique<details::ch_formatter>(flag)); break;
default: // Unknown flag appears as is
auto unknown_flag = details::make_unique<details::aggregate_formatter>();
unknown_flag->add_ch('%');
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
break;
}
}
// Extract given pad spec (e.g. %8X)
// Advance the given it pass the end of the padding spec found (if any)
// Return padding.
details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
{
using details::padding_info;
using details::scoped_pad;
const size_t max_width = 128;
if (it == end)
{
return padding_info{};
}
padding_info::pad_side side;
switch (*it)
{
case '-':
side = padding_info::right;
++it;
break;
case '=':
side = padding_info::center;
++it;
break;
default:
side = details::padding_info::left;
break; break;
} }
if (it == end || !std::isdigit(static_cast<unsigned char>(*it)))
{
return padding_info{0, side};
}
auto width = static_cast<size_t>(*it - '0');
for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it)
{
auto digit = static_cast<size_t>(*it - '0');
width = width * 10 + digit;
}
return details::padding_info{std::min<size_t>(width, max_width), side};
} }
void compile_pattern_(const std::string &pattern) void compile_pattern_(const std::string &pattern)
@ -750,9 +1131,12 @@ private:
{ {
formatters_.push_back(std::move(user_chars)); formatters_.push_back(std::move(user_chars));
} }
if (++it != end)
auto padding = handle_padspec_(++it, end);
if (it != end)
{ {
handle_flag_(*it); handle_flag_(*it, padding);
} }
else else
{ {

@ -10,7 +10,7 @@
// //
// RAII over the owned thread: // RAII over the owned thread:
// creates the thread on construction. // creates the thread on construction.
// stops and joins the thread on destruction. // stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>

@ -42,18 +42,12 @@ public:
void register_logger(std::shared_ptr<logger> new_logger) void register_logger(std::shared_ptr<logger> new_logger)
{ {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = new_logger->name(); register_logger_(std::move(new_logger));
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
} }
void register_and_init(std::shared_ptr<logger> new_logger) void initialize_logger(std::shared_ptr<logger> new_logger)
{ {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
// set the global formatter pattern
new_logger->set_formatter(formatter_->clone()); new_logger->set_formatter(formatter_->clone());
if (err_handler_) if (err_handler_)
@ -64,8 +58,10 @@ public:
new_logger->set_level(level_); new_logger->set_level(level_);
new_logger->flush_on(flush_level_); new_logger->flush_on(flush_level_);
// add to registry if (automatic_registration_)
loggers_[logger_name] = std::move(new_logger); {
register_logger_(std::move(new_logger));
}
} }
std::shared_ptr<logger> get(const std::string &logger_name) std::shared_ptr<logger> get(const std::string &logger_name)
@ -223,6 +219,12 @@ public:
return tp_mutex_; return tp_mutex_;
} }
void set_automatic_registration(bool automatic_regsistration)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
automatic_registration_ = automatic_regsistration;
}
static registry &instance() static registry &instance()
{ {
static registry s_instance; static registry s_instance;
@ -231,7 +233,7 @@ public:
private: private:
registry() registry()
: formatter_(new pattern_formatter("%+")) : formatter_(new pattern_formatter())
{ {
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
@ -259,16 +261,24 @@ private:
} }
} }
void register_logger_(std::shared_ptr<logger> new_logger)
{
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
}
std::mutex logger_map_mutex_, flusher_mutex_; std::mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_; std::recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_; std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
std::unique_ptr<formatter> formatter_; std::unique_ptr<formatter> formatter_;
level::level_enum level_ = level::info; level::level_enum level_ = spdlog::logger::default_level();
level::level_enum flush_level_ = level::off; level::level_enum flush_level_ = level::off;
log_err_handler err_handler_; log_err_handler err_handler_;
std::shared_ptr<thread_pool> tp_; std::shared_ptr<thread_pool> tp_;
std::unique_ptr<periodic_worker> periodic_flusher_; std::unique_ptr<periodic_worker> periodic_flusher_;
std::shared_ptr<logger> default_logger_; std::shared_ptr<logger> default_logger_;
bool automatic_registration_ = true;
}; };
} // namespace details } // namespace details

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "spdlog/details/fmt_helper.h"
#include "spdlog/details/log_msg.h" #include "spdlog/details/log_msg.h"
#include "spdlog/details/mpmc_blocking_q.h" #include "spdlog/details/mpmc_blocking_q.h"
#include "spdlog/details/os.h" #include "spdlog/details/os.h"
@ -69,7 +70,7 @@ struct async_msg
#endif #endif
// construct from log_msg with given type // construct from log_msg with given type
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m) async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
: msg_type(the_type) : msg_type(the_type)
, level(m.level) , level(m.level)
, time(m.time) , time(m.time)
@ -77,30 +78,34 @@ struct async_msg
, msg_id(m.msg_id) , msg_id(m.msg_id)
, worker_ptr(std::move(worker)) , worker_ptr(std::move(worker))
{ {
fmt_helper::append_buf(m.raw, raw); fmt_helper::append_string_view(m.payload, raw);
} }
async_msg(async_logger_ptr &&worker, async_msg_type the_type) async_msg(async_logger_ptr &&worker, async_msg_type the_type)
: async_msg(std::move(worker), the_type, details::log_msg()) : msg_type(the_type)
, level(level::off)
, time()
, thread_id(0)
, msg_id(0)
, worker_ptr(std::move(worker))
{ {
} }
explicit async_msg(async_msg_type the_type) explicit async_msg(async_msg_type the_type)
: async_msg(nullptr, the_type, details::log_msg()) : async_msg(nullptr, the_type)
{ {
} }
// copy into log_msg // copy into log_msg
void to_log_msg(log_msg &msg) log_msg to_log_msg()
{ {
msg.logger_name = &worker_ptr->name(); log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
msg.level = level;
msg.time = time; msg.time = time;
msg.thread_id = thread_id; msg.thread_id = thread_id;
fmt_helper::append_buf(raw, msg.raw);
msg.msg_id = msg_id; msg.msg_id = msg_id;
msg.color_range_start = 0; msg.color_range_start = 0;
msg.color_range_end = 0; msg.color_range_end = 0;
return msg;
} }
}; };
@ -158,9 +163,9 @@ public:
thread_pool(const thread_pool &) = delete; thread_pool(const thread_pool &) = delete;
thread_pool &operator=(thread_pool &&) = delete; thread_pool &operator=(thread_pool &&) = delete;
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy) void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy)
{ {
async_msg async_m(std::move(worker_ptr), async_msg_type::log, std::move(msg)); async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
post_async_msg_(std::move(async_m), overflow_policy); post_async_msg_(std::move(async_m), overflow_policy);
} }
@ -230,8 +235,7 @@ private:
{ {
case async_msg_type::log: case async_msg_type::log:
{ {
log_msg msg; auto msg = incoming_async_msg.to_log_msg();
incoming_async_msg.to_log_msg(msg);
incoming_async_msg.worker_ptr->backend_log_(msg); incoming_async_msg.worker_ptr->backend_log_(msg);
return true; return true;
} }

@ -46,7 +46,6 @@ public:
template<typename... Args> template<typename... Args>
void log(level::level_enum lvl, const char *fmt, const Args &... args); void log(level::level_enum lvl, const char *fmt, const Args &... args);
template<typename... Args>
void log(level::level_enum lvl, const char *msg); void log(level::level_enum lvl, const char *msg);
template<typename... Args> template<typename... Args>
@ -94,7 +93,12 @@ public:
#endif // _WIN32 #endif // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename T> // T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &);
// T cannot be statically converted to string_view
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &); void log(level::level_enum lvl, const T &);
template<typename T> template<typename T>
@ -117,6 +121,8 @@ public:
bool should_log(level::level_enum msg_level) const; bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level); void set_level(level::level_enum log_level);
static level::level_enum default_level();
level::level_enum level() const; level::level_enum level() const;
const std::string &name() const; const std::string &name() const;
@ -136,7 +142,7 @@ public:
// error handler // error handler
void set_error_handler(log_err_handler err_handler); void set_error_handler(log_err_handler err_handler);
log_err_handler error_handler(); log_err_handler error_handler() const;
// create new logger with same sinks and configuration. // create new logger with same sinks and configuration.
virtual std::shared_ptr<logger> clone(std::string logger_name); virtual std::shared_ptr<logger> clone(std::string logger_name);
@ -147,8 +153,8 @@ protected:
bool should_flush_(const details::log_msg &msg); bool should_flush_(const details::log_msg &msg);
// default error handler: print the error to stderr with the max rate of 1 // default error handler.
// message/minute // print the error to stderr with the max rate of 1 message/minute.
void default_err_handler_(const std::string &msg); void default_err_handler_(const std::string &msg);
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
@ -156,11 +162,11 @@ protected:
const std::string name_; const std::string name_;
std::vector<sink_ptr> sinks_; std::vector<sink_ptr> sinks_;
spdlog::level_t level_; spdlog::level_t level_{spdlog::logger::default_level()};
spdlog::level_t flush_level_; spdlog::level_t flush_level_{level::off};
log_err_handler err_handler_; log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
std::atomic<time_t> last_err_time_; std::atomic<time_t> last_err_time_{0};
std::atomic<size_t> msg_counter_; std::atomic<size_t> msg_counter_{1};
}; };
} // namespace spdlog } // namespace spdlog

@ -47,7 +47,7 @@ protected:
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
if (use_raw_msg_) if (use_raw_msg_)
{ {
details::fmt_helper::append_buf(msg.raw, formatted); details::fmt_helper::append_string_view(msg.payload, formatted);
} }
else else
{ {

@ -16,7 +16,7 @@ class sink
public: public:
sink() sink()
: level_(level::trace) : level_(level::trace)
, formatter_(new pattern_formatter("%+")) , formatter_(new pattern_formatter())
{ {
} }

@ -53,7 +53,7 @@ public:
protected: protected:
void sink_it_(const details::log_msg &msg) override void sink_it_(const details::log_msg &msg) override
{ {
::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str()); ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.payload).c_str());
} }
void flush_() override {} void flush_() override {}

@ -29,7 +29,7 @@ struct synchronous_factory
{ {
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...); auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink)); auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
details::registry::instance().register_and_init(new_logger); details::registry::instance().initialize_logger(new_logger);
return new_logger; return new_logger;
} }
}; };
@ -125,6 +125,12 @@ inline void shutdown()
details::registry::instance().shutdown(); details::registry::instance().shutdown();
} }
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
inline void set_automatic_registration(bool automatic_registation)
{
details::registry::instance().set_automatic_registration(automatic_registation);
}
// API for using default logger (stdout_color_mt), // API for using default logger (stdout_color_mt),
// e.g: spdlog::info("Message {}", 1); // e.g: spdlog::info("Message {}", 1);
// //
@ -285,35 +291,56 @@ inline void critical(const wchar_t *fmt, const Args &... args)
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// //
// Trace & Debug can be switched on/off at compile time with zero cost. // compile time macros.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable. // can be enabled/disabled using SPDLOG_ACTIVE_LEVEL (info by default).
// SPDLOG_TRACE(..) will also print current file and line.
//
// Example:
// spdlog::set_level(spdlog::level::trace);
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// //
#ifdef SPDLOG_TRACE_ON #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
#define SPDLOG_STR_H(x) #x #define SPDLOG_LOGGER_TRACE(logger, ...) logger->trace(__VA_ARGS__)
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) #define SPDLOG_TRACE(...) spdlog::trace(__VA_ARGS__)
#ifdef _MSC_VER #else
#define SPDLOG_TRACE(logger, ...) \ #define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
logger->trace("[ "__FILE__ \ #define SPDLOG_TRACE(...) (void)0
"(" SPDLOG_STR_HELPER(__LINE__) ")] " __VA_ARGS__) #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
#define SPDLOG_LOGGER_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#define SPDLOG_DEBUG(...) spdlog::debug(__VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) \ #define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
logger->trace("[" __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) "]" \ #define SPDLOG_DEBUG(...) (void)0
" " __VA_ARGS__)
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
#define SPDLOG_LOGGER_INFO(logger, ...) logger->info(__VA_ARGS__)
#define SPDLOG_INFO(...) spdlog::info(__VA_ARGS__)
#else
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0
#define SPDLOG_INFO(...) (void)0
#endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
#define SPDLOG_LOGGER_WARN(logger, ...) logger->warn(__VA_ARGS__)
#define SPDLOG_WARN(...) spdlog::warn(__VA_ARGS__)
#else
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0
#define SPDLOG_WARN(...) (void)0
#endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
#define SPDLOG_LOGGER_ERROR(logger, ...) logger->error(__VA_ARGS__)
#define SPDLOG_ERROR(...) spdlog::error(__VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) (void)0 #define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
#define SPDLOG_ERROR(...) (void)0
#endif #endif
#ifdef SPDLOG_DEBUG_ON #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) #define SPDLOG_LOGGER_CRITICAL(logger, ...) logger->critical(__VA_ARGS__)
#define SPDLOG_CRITICAL(...) spdlog::critical(__VA_ARGS__)
#else #else
#define SPDLOG_DEBUG(logger, ...) (void)0 #define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
#define SPDLOG_CRITICAL(...) (void)0
#endif #endif
} // namespace spdlog } // namespace spdlog

@ -128,3 +128,10 @@
// //
// #define SPDLOG_DISABLE_DEFAULT_LOGGER // #define SPDLOG_DISABLE_DEFAULT_LOGGER
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment and set to compile time level with zero cost (default is INFO).
// Macros like SPDLOG_DEBUG(..), SPDLOG_INFO(..) will expand to empty statements if not enabled
//
// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
///////////////////////////////////////////////////////////////////////////////

@ -10,7 +10,6 @@ static const std::string target_filename = "logs/file_helper_test.txt";
static void write_with_helper(file_helper &helper, size_t howmany) static void write_with_helper(file_helper &helper, size_t howmany)
{ {
log_msg msg;
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
fmt::format_to(formatted, "{}", std::string(howmany, '1')); fmt::format_to(formatted, "{}", std::string(howmany, '1'));
helper.write(formatted); helper.write(formatted);

@ -10,8 +10,7 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#define SPDLOG_TRACE_ON #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#define SPDLOG_DEBUG_ON
#define SPDLOG_ENABLE_MESSAGE_COUNTER #define SPDLOG_ENABLE_MESSAGE_COUNTER
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"

@ -4,8 +4,13 @@
#include "includes.h" #include "includes.h"
#if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG
#error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
#endif
TEST_CASE("debug and trace w/o format string", "[macros]]") TEST_CASE("debug and trace w/o format string", "[macros]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log"; std::string filename = "logs/simple_log";
@ -13,28 +18,15 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
SPDLOG_TRACE(logger, "Test message 1"); SPDLOG_LOGGER_TRACE(logger, "Test message 1");
SPDLOG_DEBUG(logger, "Test message 2"); SPDLOG_LOGGER_DEBUG(logger, "Test message 2");
logger->flush(); logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 2\n")); REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 1);
} }
TEST_CASE("debug and trace with format strings", "[macros]]") TEST_CASE("disable param evaluation", "[macros]")
{ {
prepare_logdir(); SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated"));
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::basic_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 {}", 222);
logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
REQUIRE(count_lines(filename) == 2);
} }

@ -60,9 +60,12 @@ TEST_CASE("date MM/DD/YY ", "[pattern_formatter]")
TEST_CASE("color range test1", "[pattern_formatter]") TEST_CASE("color range test1", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%v%$", spdlog::pattern_time_type::local, "\n"); auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%v%$", spdlog::pattern_time_type::local, "\n");
spdlog::details::log_msg msg;
fmt::format_to(msg.raw, "Hello"); fmt::memory_buffer buf;
fmt::format_to(buf, "Hello");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
std::string logger_name = "test";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, spdlog::string_view_t(buf.data(), buf.size()));
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_start == 0);
REQUIRE(msg.color_range_end == 5); REQUIRE(msg.color_range_end == 5);
@ -72,7 +75,8 @@ TEST_CASE("color range test1", "[pattern_formatter]")
TEST_CASE("color range test2", "[pattern_formatter]") TEST_CASE("color range test2", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%$", spdlog::pattern_time_type::local, "\n"); auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%$", spdlog::pattern_time_type::local, "\n");
spdlog::details::log_msg msg; std::string logger_name = "test";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_start == 0);
@ -83,7 +87,8 @@ TEST_CASE("color range test2", "[pattern_formatter]")
TEST_CASE("color range test3", "[pattern_formatter]") TEST_CASE("color range test3", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^***%$"); auto formatter = std::make_shared<spdlog::pattern_formatter>("%^***%$");
spdlog::details::log_msg msg; std::string logger_name = "test";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_start == 0);
@ -93,8 +98,9 @@ TEST_CASE("color range test3", "[pattern_formatter]")
TEST_CASE("color range test4", "[pattern_formatter]") TEST_CASE("color range test4", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("XX%^YYY%$", spdlog::pattern_time_type::local, "\n"); auto formatter = std::make_shared<spdlog::pattern_formatter>("XX%^YYY%$", spdlog::pattern_time_type::local, "\n");
spdlog::details::log_msg msg; std::string logger_name = "test";
fmt::format_to(msg.raw, "ignored"); spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 2); REQUIRE(msg.color_range_start == 2);
@ -105,7 +111,8 @@ TEST_CASE("color range test4", "[pattern_formatter]")
TEST_CASE("color range test5", "[pattern_formatter]") TEST_CASE("color range test5", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%^"); auto formatter = std::make_shared<spdlog::pattern_formatter>("**%^");
spdlog::details::log_msg msg; std::string logger_name = "test";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 2); REQUIRE(msg.color_range_start == 2);
@ -115,9 +122,75 @@ TEST_CASE("color range test5", "[pattern_formatter]")
TEST_CASE("color range test6", "[pattern_formatter]") TEST_CASE("color range test6", "[pattern_formatter]")
{ {
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%$"); auto formatter = std::make_shared<spdlog::pattern_formatter>("**%$");
spdlog::details::log_msg msg; std::string logger_name = "test";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
formatter->format(msg, formatted); formatter->format(msg, formatted);
REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_start == 0);
REQUIRE(msg.color_range_end == 2); REQUIRE(msg.color_range_end == 2);
} }
//
// Test padding
//
TEST_CASE("level_left_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%8l] %v", spdlog::pattern_time_type::local, "\n") == "[ info] Some message\n");
}
TEST_CASE("level_right_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%-8l] %v", spdlog::pattern_time_type::local, "\n") == "[info ] Some message\n");
}
TEST_CASE("level_center_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%=8l] %v", spdlog::pattern_time_type::local, "\n") == "[ info ] Some message\n");
}
TEST_CASE("short level_left_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%3L] %v", spdlog::pattern_time_type::local, "\n") == "[ I] Some message\n");
}
TEST_CASE("short level_right_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%-3L] %v", spdlog::pattern_time_type::local, "\n") == "[I ] Some message\n");
}
TEST_CASE("short level_center_padded", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%=3L] %v", spdlog::pattern_time_type::local, "\n") == "[ I ] Some message\n");
}
TEST_CASE("left_padded_short", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
}
TEST_CASE("right_padded_short", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%-3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
}
TEST_CASE("center_padded_short", "[pattern_formatter]")
{
REQUIRE(log_to_str("Some message", "[%=3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
}
TEST_CASE("left_padded_huge", "[pattern_formatter]")
{
REQUIRE(
log_to_str("Some message", "[%-300n] %v", spdlog::pattern_time_type::local, "\n") ==
"[pattern_tester ]"
" Some message\n");
}
TEST_CASE("left_padded_max", "[pattern_formatter]")
{
REQUIRE(
log_to_str("Some message", "[%-128n] %v", spdlog::pattern_time_type::local, "\n") ==
"[pattern_tester ]"
" Some message\n");
}

@ -93,3 +93,21 @@ TEST_CASE("set_default_logger(nullptr)", "[registry]")
spdlog::set_default_logger(nullptr); spdlog::set_default_logger(nullptr);
REQUIRE_FALSE(spdlog::default_logger()); REQUIRE_FALSE(spdlog::default_logger());
} }
TEST_CASE("disable automatic registration", "[registry]")
{
// set some global parameters
spdlog::level::level_enum log_level = spdlog::level::level_enum::warn;
spdlog::set_level(log_level);
// but disable automatic registration
spdlog::set_automatic_registration(false);
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, "filename", 11, 59);
auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
// loggers should not be part of the registry
REQUIRE_FALSE(spdlog::get(tested_logger_name));
REQUIRE_FALSE(spdlog::get(tested_logger_name2));
// but make sure they are still initialized according to global defaults
REQUIRE(logger1->level() == log_level);
REQUIRE(logger2->level() == log_level);
spdlog::set_level(spdlog::level::info);
}

Loading…
Cancel
Save