Skip to content

Commit a304837

Browse files
Merge branch 'gabime:v1.x' into v1.x
2 parents 0374935 + ec661f9 commit a304837

15 files changed

+311
-29
lines changed

.clang-tidy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ clang-analyzer-*,
2727

2828
WarningsAsErrors: ''
2929
HeaderFilterRegex: '*spdlog/[^f].*'
30-
AnalyzeTemporaryDtors: false
3130
FormatStyle: none
3231

3332
CheckOptions:

INSTALL

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
Header only version:
1+
Header Only Version
22
==================================================================
3-
Just copy the files to your build tree and use a C++11 compiler.
3+
Just copy the files to your build tree and use a C++11 compiler.
44
Or use CMake:
5+
```
56
add_executable(example_header_only example.cpp)
67
target_link_libraries(example_header_only spdlog::spdlog_header_only)
8+
```
79

8-
9-
Compiled library version:
10+
Compiled Library Version
1011
==================================================================
1112
CMake:
13+
```
1214
add_executable(example example.cpp)
1315
target_link_libraries(example spdlog::spdlog)
16+
```
1417

1518
Or copy files src/*.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler.
1619

17-
Tested on:
20+
Important Information for Compilation:
21+
==================================================================
22+
* If you encounter compilation errors with gcc 4.8.x, please note that gcc 4.8.x does not fully support C++11. In such cases, consider upgrading your compiler or using a different version that fully supports C++11 standards
23+
24+
Tested on:
1825
gcc 4.8.1 and above
1926
clang 3.5
20-
Visual Studio 2013
21-
22-
23-
24-
27+
Visual Studio 2013

bench/latency.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ int main(int argc, char *argv[]) {
121121
tracing_null_logger_st->enable_backtrace(64);
122122
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
123123

124-
#ifdef __linux
124+
#ifdef __linux__
125125
bench_dev_null();
126126
#endif // __linux__
127127

include/spdlog/async_logger-inl.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ SPDLOG_LOGGER_CATCH(msg.source)
4343
}
4444

4545
// send flush request to the thread pool
46-
SPDLOG_INLINE void spdlog::async_logger::flush_(){
47-
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
48-
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
49-
}
50-
else {
46+
SPDLOG_INLINE void spdlog::async_logger::flush_(){SPDLOG_TRY{auto pool_ptr = thread_pool_.lock();
47+
if (!pool_ptr) {
5148
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
5249
}
50+
51+
std::future<void> future = pool_ptr->post_flush(shared_from_this(), overflow_policy_);
52+
// Wait for the flush operation to complete.
53+
// This might throw exception if the flush message get dropped because of overflow.
54+
future.get();
5355
}
5456
SPDLOG_LOGGER_CATCH(source_loc())
5557
}

include/spdlog/details/circular_q.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <cassert>
88
#include <vector>
99

10+
#include "spdlog/common.h"
11+
1012
namespace spdlog {
1113
namespace details {
1214
template <typename T>

include/spdlog/details/os-inl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT {
439439

440440
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
441441
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
442-
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1) {
442+
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 4 - 1) {
443443
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
444444
}
445445

@@ -450,7 +450,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
450450
}
451451

452452
int result_size = static_cast<int>(target.capacity());
453-
if ((wstr_size + 1) * 2 > result_size) {
453+
if ((wstr_size + 1) * 4 > result_size) {
454454
result_size =
455455
::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
456456
}

include/spdlog/details/registry-inl.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,23 @@ SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_na
8585
}
8686

8787
#if __cplusplus >= 201703L // C++17
88+
// if the map is small do a sequential search and avoid creating string for find(logger_name)
89+
// otherwise use the standard find()
8890
SPDLOG_INLINE std::shared_ptr<logger> registry::get(std::string_view logger_name) {
8991
std::lock_guard<std::mutex> lock(logger_map_mutex_);
90-
for (const auto &[key, val] : loggers_) {
91-
if (key == logger_name) {
92-
return val;
92+
if (loggers_.size() <= 10) {
93+
for (const auto &[key, val]: loggers_) {
94+
if (logger_name == key) {
95+
return val;
96+
}
9397
}
98+
return nullptr;
99+
}
100+
// otherwise use the normal map lookup
101+
else {
102+
auto found = loggers_.find(std::string(logger_name));
103+
return found == loggers_.end() ? nullptr : found->second;
94104
}
95-
return nullptr;
96105
}
97106

98107
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const char *logger_name) {

include/spdlog/details/thread_pool-inl.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,13 @@ void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr,
6262
post_async_msg_(std::move(async_m), overflow_policy);
6363
}
6464

65-
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr,
66-
async_overflow_policy overflow_policy) {
67-
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
65+
std::future<void> SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr,
66+
async_overflow_policy overflow_policy) {
67+
std::promise<void> promise;
68+
std::future<void> future = promise.get_future();
69+
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush, std::move(promise)),
70+
overflow_policy);
71+
return future;
6872
}
6973

7074
size_t SPDLOG_INLINE thread_pool::overrun_counter() { return q_.overrun_counter(); }
@@ -108,6 +112,7 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_() {
108112
}
109113
case async_msg_type::flush: {
110114
incoming_async_msg.worker_ptr->backend_flush_();
115+
incoming_async_msg.flush_promise.set_value();
111116
return true;
112117
}
113118

include/spdlog/details/thread_pool.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <chrono>
1111
#include <functional>
12+
#include <future>
1213
#include <memory>
1314
#include <thread>
1415
#include <vector>
@@ -27,6 +28,7 @@ enum class async_msg_type { log, flush, terminate };
2728
struct async_msg : log_msg_buffer {
2829
async_msg_type msg_type{async_msg_type::log};
2930
async_logger_ptr worker_ptr;
31+
std::promise<void> flush_promise;
3032

3133
async_msg() = default;
3234
~async_msg() = default;
@@ -56,12 +58,22 @@ struct async_msg : log_msg_buffer {
5658
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
5759
: log_msg_buffer{m},
5860
msg_type{the_type},
59-
worker_ptr{std::move(worker)} {}
61+
worker_ptr{std::move(worker)},
62+
flush_promise{} {}
6063

6164
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
6265
: log_msg_buffer{},
6366
msg_type{the_type},
64-
worker_ptr{std::move(worker)} {}
67+
worker_ptr{std::move(worker)},
68+
flush_promise{} {}
69+
70+
async_msg(async_logger_ptr &&worker,
71+
async_msg_type the_type,
72+
std::promise<void> &&promise)
73+
: log_msg_buffer{},
74+
msg_type{the_type},
75+
worker_ptr{std::move(worker)},
76+
flush_promise{std::move(promise)} {}
6577

6678
explicit async_msg(async_msg_type the_type)
6779
: async_msg{nullptr, the_type} {}
@@ -88,7 +100,8 @@ class SPDLOG_API thread_pool {
88100
void post_log(async_logger_ptr &&worker_ptr,
89101
const details::log_msg &msg,
90102
async_overflow_policy overflow_policy);
91-
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
103+
std::future<void> post_flush(async_logger_ptr &&worker_ptr,
104+
async_overflow_policy overflow_policy);
92105
size_t overrun_counter();
93106
void reset_overrun_counter();
94107
size_t discard_counter();

include/spdlog/mdc.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <spdlog/common.h>
2+
#include <map>
3+
4+
namespace spdlog {
5+
6+
class SPDLOG_API mdc {
7+
public:
8+
static void put(const std::string &key, const std::string &value) {
9+
get_context()[key] = value;
10+
}
11+
12+
static std::string get(const std::string &key) {
13+
auto &context = get_context();
14+
auto it = context.find(key);
15+
if (it != context.end()) {
16+
return it->second;
17+
}
18+
return "";
19+
}
20+
21+
static void remove(const std::string &key) { get_context().erase(key); }
22+
23+
static void clear() { get_context().clear(); }
24+
25+
static std::map<std::string, std::string> &get_context() {
26+
static thread_local std::map<std::string, std::string> context;
27+
return context;
28+
}
29+
};
30+
31+
} // namespace spdlog

include/spdlog/pattern_formatter-inl.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <spdlog/details/fmt_helper.h>
1111
#include <spdlog/details/log_msg.h>
1212
#include <spdlog/details/os.h>
13+
#include <spdlog/mdc.h>
1314
#include <spdlog/fmt/fmt.h>
1415
#include <spdlog/formatter.h>
1516

@@ -867,6 +868,43 @@ class full_formatter final : public flag_formatter {
867868
memory_buf_t cached_datetime_;
868869
};
869870

871+
// Class for formatting Mapped Diagnostic Context (MDC) in log messages.
872+
// Example: [logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message
873+
template <typename ScopedPadder>
874+
class mdc_formatter : public flag_formatter {
875+
public:
876+
explicit mdc_formatter(padding_info padinfo)
877+
: flag_formatter(padinfo) {}
878+
879+
void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
880+
auto mdc_map = mdc::get_context();
881+
if (mdc_map.empty()) {
882+
ScopedPadder p(0, padinfo_, dest);
883+
return;
884+
} else {
885+
auto last_element = --mdc_map.end();
886+
for (auto it = mdc_map.begin(); it != mdc_map.end(); ++it) {
887+
auto &pair = *it;
888+
const auto &key = pair.first;
889+
const auto &value = pair.second;
890+
size_t content_size = key.size() + value.size() + 1; // 1 for ':'
891+
892+
if (it != last_element) {
893+
content_size++; // 1 for ' '
894+
}
895+
896+
ScopedPadder p(content_size, padinfo_, dest);
897+
fmt_helper::append_string_view(key, dest);
898+
fmt_helper::append_string_view(":", dest);
899+
fmt_helper::append_string_view(value, dest);
900+
if (it != last_element) {
901+
fmt_helper::append_string_view(" ", dest);
902+
}
903+
}
904+
}
905+
}
906+
};
907+
870908
} // namespace details
871909

872910
SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern,
@@ -1159,6 +1197,10 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
11591197
padding));
11601198
break;
11611199

1200+
case ('&'):
1201+
formatters_.push_back(details::make_unique<details::mdc_formatter<Padder>>(padding));
1202+
break;
1203+
11621204
default: // Unknown flag appears as is
11631205
auto unknown_flag = details::make_unique<details::aggregate_formatter>();
11641206

include/spdlog/stopwatch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class stopwatch {
3939
return std::chrono::duration<double>(clock::now() - start_tp_);
4040
}
4141

42+
std::chrono::milliseconds elapsed_ms() const {
43+
return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start_tp_);
44+
}
45+
4246
void reset() { start_tp_ = clock::now(); }
4347
};
4448
} // namespace spdlog

tests/includes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "spdlog/spdlog.h"
2727
#include "spdlog/async.h"
2828
#include "spdlog/details/fmt_helper.h"
29+
#include "spdlog/mdc.h"
2930
#include "spdlog/sinks/basic_file_sink.h"
3031
#include "spdlog/sinks/daily_file_sink.h"
3132
#include "spdlog/sinks/null_sink.h"

tests/test_async.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,49 @@ TEST_CASE("flush", "[async]") {
9393
REQUIRE(test_sink->flush_counter() == 1);
9494
}
9595

96+
TEST_CASE("multithread flush", "[async]") {
97+
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
98+
size_t queue_size = 2;
99+
size_t messages = 10;
100+
size_t n_threads = 10;
101+
size_t flush_count = 2048;
102+
std::mutex mtx;
103+
std::vector<std::string> errmsgs;
104+
{
105+
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
106+
auto logger = std::make_shared<spdlog::async_logger>(
107+
"as", test_sink, tp, spdlog::async_overflow_policy::discard_new);
108+
109+
logger->set_error_handler([&](const std::string &) {
110+
std::unique_lock<std::mutex> lock(mtx);
111+
errmsgs.push_back("Broken promise");
112+
});
113+
114+
for (size_t i = 0; i < messages; i++) {
115+
logger->info("Hello message #{}", i);
116+
}
117+
118+
std::vector<std::thread> threads;
119+
for (size_t i = 0; i < n_threads; i++) {
120+
threads.emplace_back([logger, flush_count] {
121+
for (size_t j = 0; j < flush_count; j++) {
122+
// flush does not throw exception even if failed.
123+
// Instead, the error handler is invoked.
124+
logger->flush();
125+
}
126+
});
127+
}
128+
129+
for (auto &t : threads) {
130+
t.join();
131+
}
132+
}
133+
REQUIRE(test_sink->flush_counter() >= 1);
134+
REQUIRE(test_sink->flush_counter() + errmsgs.size() == n_threads * flush_count);
135+
REQUIRE(errmsgs.size() >= 1);
136+
REQUIRE(errmsgs[0] == "Broken promise");
137+
}
138+
96139
TEST_CASE("async periodic flush", "[async]") {
97140
auto logger = spdlog::create_async<spdlog::sinks::test_sink_mt>("as");
98141
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);

0 commit comments

Comments
 (0)