mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
27cb4c7670 | ||
![]() |
2d4acf8cc3 | ||
![]() |
3b4fd93bd0 | ||
![]() |
2122eb2194 | ||
![]() |
22b0f4fc06 | ||
![]() |
37b847692e | ||
![]() |
fa6605dc99 | ||
![]() |
94a8e87c71 |
15
README.md
15
README.md
@@ -437,7 +437,22 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
logger->info("Some info message");
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
#### Mapped Diagnostic Context
|
||||
```c++
|
||||
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread local storage.
|
||||
// Each thread maintains its own MDC, which loggers use to append diagnostic information to log outputs.
|
||||
// Note: it is not supported in asynchronous mode due to its reliance on thread-local storage.
|
||||
#include "spdlog/mdc.h"
|
||||
void mdc_example()
|
||||
{
|
||||
spdlog::mdc::put("key1", "value1");
|
||||
spdlog::mdc::put("key2", "value2");
|
||||
// if not using the default format, use the %& formatter to print mdc data
|
||||
// spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [%&] %v");
|
||||
}
|
||||
```
|
||||
---
|
||||
## Benchmarks
|
||||
|
||||
|
@@ -26,6 +26,7 @@ void udp_example();
|
||||
void custom_flags_example();
|
||||
void file_events_example();
|
||||
void replace_default_logger_example();
|
||||
void mdc_example();
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/cfg/env.h" // support for loading levels from the environment variable
|
||||
@@ -84,6 +85,7 @@ int main(int, char *[]) {
|
||||
custom_flags_example();
|
||||
file_events_example();
|
||||
replace_default_logger_example();
|
||||
mdc_example();
|
||||
|
||||
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||
@@ -376,3 +378,16 @@ void replace_default_logger_example() {
|
||||
|
||||
spdlog::set_default_logger(old_logger);
|
||||
}
|
||||
|
||||
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread local storage.
|
||||
// Each thread maintains its own MDC, which loggers use to append diagnostic information to log outputs.
|
||||
// Note: it is not supported in asynchronous mode due to its reliance on thread-local storage.
|
||||
#include "spdlog/mdc.h"
|
||||
void mdc_example()
|
||||
{
|
||||
spdlog::mdc::put("key1", "value1");
|
||||
spdlog::mdc::put("key2", "value2");
|
||||
// if not using the default format, you can use the %& formatter to print mdc data as well
|
||||
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [%&] %v");
|
||||
spdlog::info("Some log message with context");
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@
|
||||
#if FMT_USE_CONSTEXPR
|
||||
#define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
|
||||
#else
|
||||
#define SPDLOG_CONSTEXPR_FUNC inline
|
||||
#define SPDLOG_CONSTEXPR_FUNC inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@@ -98,7 +98,7 @@ SPDLOG_INLINE void file_helper::close() {
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
|
||||
if(fd_ == nullptr) return;
|
||||
if (fd_ == nullptr) return;
|
||||
size_t msg_size = buf.size();
|
||||
auto data = buf.data();
|
||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size) {
|
||||
|
@@ -534,6 +534,15 @@ SPDLOG_INLINE bool create_dir(const filename_t &path) {
|
||||
}
|
||||
|
||||
auto subdir = path.substr(0, token_pos);
|
||||
#ifdef _WIN32
|
||||
// if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
|
||||
// otherwise path_exists(subdir) returns false (issue #3079)
|
||||
const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
|
||||
if (is_drive) {
|
||||
subdir += '\\';
|
||||
token_pos++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
|
||||
return false; // return error if failed creating dir
|
||||
|
@@ -84,31 +84,6 @@ SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_na
|
||||
return found == loggers_.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
// if the map is small do a sequential search and avoid creating string for find(logger_name)
|
||||
// otherwise use the standard find()
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::get(std::string_view logger_name) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
if (loggers_.size() <= 10) {
|
||||
for (const auto &[key, val]: loggers_) {
|
||||
if (logger_name == key) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// otherwise use the normal map lookup
|
||||
else {
|
||||
auto found = loggers_.find(std::string(logger_name));
|
||||
return found == loggers_.end() ? nullptr : found->second;
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const char *logger_name) {
|
||||
return get(std::string_view(logger_name));
|
||||
}
|
||||
#endif
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
return default_logger_;
|
||||
@@ -123,7 +98,7 @@ SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get()
|
||||
// set default logger.
|
||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
if (new_default_logger != nullptr) {
|
||||
loggers_[new_default_logger->name()] = new_default_logger;
|
||||
}
|
||||
|
@@ -18,10 +18,6 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
|
||||
@@ -37,10 +33,6 @@ public:
|
||||
void register_logger(std::shared_ptr<logger> new_logger);
|
||||
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
std::shared_ptr<logger> get(std::string_view logger_name);
|
||||
std::shared_ptr<logger> get(const char *logger_name);
|
||||
#endif
|
||||
std::shared_ptr<logger> default_logger();
|
||||
|
||||
// Return raw ptr to the default logger.
|
||||
@@ -52,7 +44,8 @@ public:
|
||||
|
||||
// set default logger and add it to the registry if not registered already.
|
||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
// Note: Make sure to unregister it when no longer needed or before calling again with a new logger.
|
||||
// Note: Make sure to unregister it when no longer needed or before calling again with a new
|
||||
// logger.
|
||||
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
||||
|
||||
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||
@@ -78,8 +71,8 @@ public:
|
||||
}
|
||||
|
||||
std::unique_ptr<periodic_worker> &get_flusher() {
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
return periodic_flusher_;
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
return periodic_flusher_;
|
||||
}
|
||||
|
||||
void set_error_handler(err_handler handler);
|
||||
|
@@ -67,9 +67,7 @@ struct async_msg : log_msg_buffer {
|
||||
worker_ptr{std::move(worker)},
|
||||
flush_promise{} {}
|
||||
|
||||
async_msg(async_logger_ptr &&worker,
|
||||
async_msg_type the_type,
|
||||
std::promise<void> &&promise)
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, std::promise<void> &&promise)
|
||||
: log_msg_buffer{},
|
||||
msg_type{the_type},
|
||||
worker_ptr{std::move(worker)},
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef FMT_USE_WINDOWS_H
|
||||
#define FMT_USE_WINDOWS_H 0
|
||||
#endif
|
||||
|
||||
|
||||
#include <spdlog/fmt/bundled/core.h>
|
||||
#include <spdlog/fmt/bundled/format.h>
|
||||
|
||||
|
@@ -1,3 +1,6 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
@@ -5,32 +8,39 @@
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
||||
// MDC is a simple map of key->string values stored in thread local storage whose content will be printed by the loggers.
|
||||
// Note: Not supported in async mode (thread local storage - so the async thread pool have different copy).
|
||||
//
|
||||
// Usage example:
|
||||
// spdlog::mdc::put("mdc_key_1", "mdc_value_1");
|
||||
// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] [mdc_key_1:mdc_value_1] Hello, World!
|
||||
|
||||
namespace spdlog {
|
||||
class SPDLOG_API mdc {
|
||||
public:
|
||||
using mdc_map_t = std::map<std::string, std::string>;
|
||||
class SPDLOG_API mdc {
|
||||
public:
|
||||
using mdc_map_t = std::map<std::string, std::string>;
|
||||
|
||||
static void put(const std::string &key, const std::string &value) {
|
||||
get_context()[key] = value;
|
||||
static void put(const std::string &key, const std::string &value) {
|
||||
get_context()[key] = value;
|
||||
}
|
||||
|
||||
static std::string get(const std::string &key) {
|
||||
auto &context = get_context();
|
||||
auto it = context.find(key);
|
||||
if (it != context.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string get(const std::string &key) {
|
||||
auto &context = get_context();
|
||||
auto it = context.find(key);
|
||||
if (it != context.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
static void remove(const std::string &key) { get_context().erase(key); }
|
||||
|
||||
static void remove(const std::string &key) { get_context().erase(key); }
|
||||
static void clear() { get_context().clear(); }
|
||||
|
||||
static void clear() { get_context().clear(); }
|
||||
|
||||
static mdc_map_t &get_context() {
|
||||
static thread_local mdc_map_t context;
|
||||
return context;
|
||||
}
|
||||
};
|
||||
static mdc_map_t &get_context() {
|
||||
static thread_local mdc_map_t context;
|
||||
return context;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace spdlog
|
||||
|
@@ -790,7 +790,7 @@ template <typename ScopedPadder>
|
||||
class mdc_formatter : public flag_formatter {
|
||||
public:
|
||||
explicit mdc_formatter(padding_info padinfo)
|
||||
: flag_formatter(padinfo) {}
|
||||
: flag_formatter(padinfo) {}
|
||||
|
||||
void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
|
||||
auto &mdc_map = mdc::get_context();
|
||||
@@ -802,7 +802,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void format_mdc(const mdc::mdc_map_t &mdc_map, memory_buf_t &dest){
|
||||
void format_mdc(const mdc::mdc_map_t &mdc_map, memory_buf_t &dest) {
|
||||
auto last_element = --mdc_map.end();
|
||||
for (auto it = mdc_map.begin(); it != mdc_map.end(); ++it) {
|
||||
auto &pair = *it;
|
||||
@@ -825,8 +825,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Full info formatter
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
|
||||
class full_formatter final : public flag_formatter {
|
||||
@@ -921,8 +919,6 @@ private:
|
||||
mdc_formatter<null_scoped_padder> mdc_formatter_{padding_info{}};
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace details
|
||||
|
||||
SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern,
|
||||
|
@@ -20,16 +20,6 @@ SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name) {
|
||||
return details::registry::instance().get(name);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
SPDLOG_INLINE std::shared_ptr<logger> get(std::string_view name) {
|
||||
return details::registry::instance().get(name);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> get(const char *name) {
|
||||
return details::registry::instance().get(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
SPDLOG_INLINE void set_formatter(std::unique_ptr<spdlog::formatter> formatter) {
|
||||
details::registry::instance().set_formatter(std::move(formatter));
|
||||
}
|
||||
|
@@ -20,10 +20,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
using default_factory = synchronous_factory;
|
||||
@@ -54,10 +50,6 @@ SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
|
||||
// exist.
|
||||
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
||||
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
|
||||
#if __cplusplus >= 201703L // C++17
|
||||
SPDLOG_API std::shared_ptr<logger> get(std::string_view name);
|
||||
SPDLOG_API std::shared_ptr<logger> get(const char *name);
|
||||
#endif
|
||||
|
||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||
SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
#define SPDLOG_VER_MAJOR 1
|
||||
#define SPDLOG_VER_MINOR 14
|
||||
#define SPDLOG_VER_PATCH 0
|
||||
#define SPDLOG_VER_PATCH 1
|
||||
|
||||
#define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch)
|
||||
#define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH)
|
||||
|
@@ -132,8 +132,8 @@ TEST_CASE("multithread flush", "[async]") {
|
||||
}
|
||||
REQUIRE(test_sink->flush_counter() >= 1);
|
||||
REQUIRE(test_sink->flush_counter() + errmsgs.size() == n_threads * flush_count);
|
||||
if (errmsgs.size() > 0) {
|
||||
REQUIRE(errmsgs[0] == "Broken promise");
|
||||
if (errmsgs.size() > 0) {
|
||||
REQUIRE(errmsgs[0] == "Broken promise");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -81,3 +81,64 @@ TEST_CASE("dir_name", "[create_dir]") {
|
||||
REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T(".."));
|
||||
REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T("."));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
//
|
||||
// test windows cases when drive letter is given e.g. C:\\some-folder
|
||||
//
|
||||
#include <windows.h>
|
||||
#include <fileapi.h>
|
||||
|
||||
std::string get_full_path(const std::string &relative_folder_path) {
|
||||
char full_path[MAX_PATH];
|
||||
|
||||
DWORD result = ::GetFullPathNameA(relative_folder_path.c_str(), MAX_PATH, full_path, nullptr);
|
||||
// Return an empty string if failed to get full path
|
||||
return result > 0 && result < MAX_PATH ? std::string(full_path) : std::string();
|
||||
}
|
||||
|
||||
std::wstring get_full_path(const std::wstring &relative_folder_path) {
|
||||
wchar_t full_path[MAX_PATH];
|
||||
DWORD result = ::GetFullPathNameW(relative_folder_path.c_str(), MAX_PATH, full_path, nullptr);
|
||||
return result > 0 && result < MAX_PATH ? std::wstring(full_path) : std::wstring();
|
||||
}
|
||||
|
||||
spdlog::filename_t::value_type find_non_existing_drive() {
|
||||
for (char drive = 'A'; drive <= 'Z'; ++drive) {
|
||||
std::string root_path = std::string(1, drive) + ":\\";
|
||||
UINT drive_type = GetDriveTypeA(root_path.c_str());
|
||||
if (drive_type == DRIVE_NO_ROOT_DIR) {
|
||||
return static_cast<spdlog::filename_t::value_type>(drive);
|
||||
}
|
||||
}
|
||||
return '\0'; // No available drive found
|
||||
}
|
||||
|
||||
TEST_CASE("create_abs_path1", "[create_dir]") {
|
||||
prepare_logdir();
|
||||
auto abs_path = get_full_path(SPDLOG_FILENAME_T("test_logs\\logdir1"));
|
||||
REQUIRE(!abs_path.empty());
|
||||
REQUIRE(create_dir(abs_path) == true);
|
||||
}
|
||||
|
||||
TEST_CASE("create_abs_path2", "[create_dir]") {
|
||||
prepare_logdir();
|
||||
auto abs_path = get_full_path(SPDLOG_FILENAME_T("test_logs/logdir2"));
|
||||
REQUIRE(!abs_path.empty());
|
||||
REQUIRE(create_dir(abs_path) == true);
|
||||
}
|
||||
|
||||
TEST_CASE("non_existing_drive", "[create_dir]") {
|
||||
prepare_logdir();
|
||||
spdlog::filename_t path;
|
||||
|
||||
auto non_existing_drive = find_non_existing_drive();
|
||||
path += non_existing_drive;
|
||||
path += SPDLOG_FILENAME_T(":\\");
|
||||
REQUIRE(create_dir(path) == false);
|
||||
path += SPDLOG_FILENAME_T("subdir");
|
||||
REQUIRE(create_dir(path) == false);
|
||||
}
|
||||
// #endif // SPDLOG_WCHAR_FILENAMES
|
||||
#endif // _WIN32
|
||||
|
Reference in New Issue
Block a user