pull/3482/head
xxx 1 month ago
parent 486b55554f
commit 265de6186d

@ -21,3 +21,14 @@ if(SPDLOG_BUILD_EXAMPLE_HO)
add_executable(example_header_only example.cpp)
target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
endif()
# ---------------------------------------------------------------------------------------
# Example of using new features (desensitization, structured logging, aggregation, etc.)
# ---------------------------------------------------------------------------------------
add_executable(new_features_example new_features_example.cpp)
target_link_libraries(new_features_example PRIVATE spdlog::spdlog $<$<BOOL:${MINGW}>:ws2_32>)
if(SPDLOG_BUILD_EXAMPLE_HO)
add_executable(new_features_example_header_only new_features_example.cpp)
target_link_libraries(new_features_example_header_only PRIVATE spdlog::spdlog_header_only)
endif()

@ -0,0 +1,58 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/desensitizer.h>
#endif
#include <algorithm>
#include <stdexcept>
namespace spdlog {
desensitizer::desensitizer() : rules_() {
// 添加默认的脱敏规则
add_rule("phone", R"(1[3-9]\d{9})", "1****5678");
add_rule("email", R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", "***@***");
add_rule("id_card", R"(\d{17}[\dXx])", "***********5678");
}
desensitizer::desensitizer(details::desensitize_rules rules) : rules_(std::move(rules)) {
}
void desensitizer::add_rule(const std::string &name, const std::string &pattern, const std::string &replacement) {
try {
rules_[name] = {std::regex(pattern), replacement};
} catch (const std::regex_error &e) {
throw std::invalid_argument(fmt::format("Invalid regex pattern '{}': {}", pattern, e.what()));
}
}
void desensitizer::remove_rule(const std::string &name) {
rules_.erase(name);
}
void desensitizer::clear_rules() {
rules_.clear();
}
std::string desensitizer::desensitize(const std::string &msg) const {
std::string result = msg;
for (const auto &[name, rule] : rules_) {
result = std::regex_replace(result, rule.pattern, rule.replacement);
}
return result;
}
void desensitizer::desensitize(details::log_msg &msg) const {
if (msg.payload.empty()) {
return;
}
std::string desensitized = desensitize(std::string(msg.payload.data(), msg.payload.size()));
msg.payload = spdlog::string_view_t(desensitized.data(), desensitized.size());
}
} // namespace spdlog

@ -0,0 +1,52 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/details/log_msg.h>
#include <spdlog/fmt/fmt.h>
#include <regex>
#include <string>
#include <unordered_map>
namespace spdlog {
namespace details {
struct desensitize_rule {
std::regex pattern;
std::string replacement;
};
using desensitize_rules = std::unordered_map<std::string, desensitize_rule>;
} // namespace details
class SPDLOG_API desensitizer {
public:
desensitizer();
explicit desensitizer(details::desensitize_rules rules);
// 添加脱敏规则
void add_rule(const std::string &name, const std::string &pattern, const std::string &replacement);
// 移除脱敏规则
void remove_rule(const std::string &name);
// 清空所有脱敏规则
void clear_rules();
// 脱敏字符串
std::string desensitize(const std::string &msg) const;
// 脱敏log_msg
void desensitize(details::log_msg &msg) const;
private:
details::desensitize_rules rules_;
};
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "desensitizer-inl.h"
#endif

@ -10,6 +10,7 @@
#include <spdlog/common.h>
#include <spdlog/details/periodic_worker.h>
#include <spdlog/aggregator.h>
#include <chrono>
#include <functional>
@ -121,6 +122,8 @@ private:
std::shared_ptr<logger> default_logger_;
bool automatic_registration_ = true;
size_t backtrace_n_messages_ = 0;
std::shared_ptr<aggregator> aggregator_;
size_t max_aggregated_logs_ = 1000;
};
} // namespace details

@ -0,0 +1,89 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/formatters/json_formatter.h>
#endif
#include <spdlog/details/os.h>
#include <spdlog/details/format_helper.h>
namespace spdlog {
namespace formatters {
json_formatter::json_formatter(std::string eol)
: eol_(std::move(eol))
{}
std::unique_ptr<formatter> json_formatter::clone() const {
return details::make_unique<json_formatter>(*this);
}
void json_formatter::format(const details::log_msg &msg, memory_buf_t &dest) {
fmt::format_to(std::back_inserter(dest), "{{");
// 添加时间字段
std::tm tm_time;
if (msg.time.time_since_epoch().count() == 0) {
// 如果时间未设置,使用当前时间
auto now = std::chrono::system_clock::now();
tm_time = details::os::localtime(std::chrono::system_clock::to_time_t(now));
} else {
tm_time = details::os::localtime(std::chrono::system_clock::to_time_t(msg.time));
}
char time_buf[128];
std::strftime(time_buf, sizeof(time_buf), time_format_.c_str(), &tm_time);
fmt::format_to(std::back_inserter(dest), "\"time\":\"{}\",", time_buf);
// 添加日志级别
fmt::format_to(std::back_inserter(dest), "\"level\":{},", static_cast<int>(msg.level));
// 添加日志级别名称
if (include_level_name_) {
fmt::format_to(std::back_inserter(dest), "\"level_name\":\"{}\",", level::to_string_view(msg.level));
}
// 添加日志名称
fmt::format_to(std::back_inserter(dest), "\"logger_name\":\"{}\",", msg.logger_name);
// 添加线程ID
if (include_thread_id_) {
fmt::format_to(std::back_inserter(dest), "\"thread_id\":{},", msg.thread_id);
}
// 添加日志消息
fmt::format_to(std::back_inserter(dest), "\"message\":{}", fmt::format("{}", msg.payload));
// 添加源位置信息
if (include_source_location_ && msg.source.filename != nullptr) {
fmt::format_to(std::back_inserter(dest), ",\"source_location\":{{\"file\":\"{}\",\"line\":{},\"function\":\"{}\"}}",
msg.source.filename,
msg.source.line,
msg.source.funcname);
}
// 关闭JSON对象
fmt::format_to(std::back_inserter(dest), "{}", eol_);
}
void json_formatter::set_include_source_location(bool include) {
include_source_location_ = include;
}
void json_formatter::set_include_thread_id(bool include) {
include_thread_id_ = include;
}
void json_formatter::set_include_level_name(bool include) {
include_level_name_ = include;
}
void json_formatter::set_time_format(std::string format) {
time_format_ = std::move(format);
}
} // namespace formatters
} // namespace spdlog

@ -0,0 +1,48 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/formatter.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/fmt/fmt.h>
#include <string>
#include <vector>
namespace spdlog {
namespace formatters {
class SPDLOG_API json_formatter : public formatter {
public:
json_formatter(std::string eol = spdlog::details::os::default_eol);
std::unique_ptr<formatter> clone() const override;
void format(const details::log_msg &msg, memory_buf_t &dest) override;
// 设置是否包含源位置信息
void set_include_source_location(bool include);
// 设置是否包含线程ID
void set_include_thread_id(bool include);
// 设置是否包含日志级别名称
void set_include_level_name(bool include);
// 设置时间格式
void set_time_format(std::string format);
private:
std::string eol_;
bool include_source_location_ = true;
bool include_thread_id_ = true;
bool include_level_name_ = true;
std::string time_format_ = "%Y-%m-%dT%H:%M:%S.%eZ";
};
} // namespace formatters
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "json_formatter-inl.h"
#endif

@ -0,0 +1,240 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/log_aggregator.h>
#endif
#include <spdlog/details/os.h>
#include <spdlog/fmt/fmt.h>
#include <regex>
#include <chrono>
#include <algorithm>
#include <iterator>
namespace spdlog {
log_aggregator::log_aggregator(size_t max_buffer_size)
: max_buffer_size_(max_buffer_size)
, log_callback_([this](const details::log_msg &msg) {
std::lock_guard<std::mutex> lock(buffer_mutex_);
if (log_buffer_.size() >= max_buffer_size_) {
log_buffer_.erase(log_buffer_.begin());
}
log_buffer_.push_back(msg);
update_statistics(msg);
update_latency_distribution(msg);
})
{
// 初始化统计信息
auto now = std::chrono::system_clock::now();
statistics_.first_log_time = now;
statistics_.last_log_time = now;
}
log_aggregator::~log_aggregator() {
// 注销所有日志记录器
std::lock_guard<std::mutex> lock(loggers_mutex_);
for (auto &[name, logger] : registered_loggers_) {
logger->remove_callback(log_callback_);
}
}
void log_aggregator::register_logger(std::shared_ptr<logger> logger) {
std::lock_guard<std::mutex> lock(loggers_mutex_);
auto name = logger->name();
if (registered_loggers_.find(name) == registered_loggers_.end()) {
registered_loggers_[name] = logger;
logger->add_callback(log_callback_);
}
}
void log_aggregator::unregister_logger(std::shared_ptr<logger> logger) {
std::lock_guard<std::mutex> lock(loggers_mutex_);
auto name = logger->name();
auto it = registered_loggers_.find(name);
if (it != registered_loggers_.end()) {
logger->remove_callback(log_callback_);
registered_loggers_.erase(it);
}
}
query_result log_aggregator::query_logs(const query_filter &filter) const {
auto start_time = std::chrono::high_resolution_clock::now();
query_result result;
std::vector<details::log_msg> filtered_logs;
std::lock_guard<std::mutex> lock(buffer_mutex_);
result.total_logs = log_buffer_.size();
// 过滤日志
for (const auto &msg : log_buffer_) {
if (msg.time < filter.start_time || msg.time > filter.end_time) {
continue;
}
if (msg.level < filter.min_level || msg.level > filter.max_level) {
continue;
}
if (!filter.logger_name.empty() && msg.logger_name != filter.logger_name) {
continue;
}
if (!filter.keyword.empty()) {
std::string payload(msg.payload.data(), msg.payload.size());
if (payload.find(filter.keyword) == std::string::npos) {
continue;
}
}
filtered_logs.push_back(msg);
}
result.total_matched = filtered_logs.size();
// 应用分页
size_t start_idx = filter.offset;
size_t end_idx = std::min(start_idx + filter.limit, filtered_logs.size());
if (start_idx < filtered_logs.size()) {
result.logs.insert(result.logs.end(), filtered_logs.begin() + start_idx, filtered_logs.begin() + end_idx);
}
auto end_time = std::chrono::high_resolution_clock::now();
result.query_duration = std::chrono::duration<double, std::milli>(end_time - start_time).count();
return result;
}
log_statistics log_aggregator::get_statistics() const {
std::lock_guard<std::mutex> lock(statistics_mutex_);
return statistics_;
}
latency_distribution log_aggregator::get_latency_distribution(const std::string &prefix) const {
std::lock_guard<std::mutex> lock(latency_mutex_);
auto it = latency_distributions_.find(prefix);
if (it != latency_distributions_.end()) {
return it->second;
}
return latency_distribution{};
}
void log_aggregator::clean_old_logs(std::chrono::system_clock::time_point before_time) {
std::lock_guard<std::mutex> lock(buffer_mutex_);
auto it = std::remove_if(log_buffer_.begin(), log_buffer_.end(),
[&before_time](const details::log_msg &msg) {
return msg.time < before_time;
});
log_buffer_.erase(it, log_buffer_.end());
}
void log_aggregator::set_max_buffer_size(size_t max_size) {
std::lock_guard<std::mutex> lock(buffer_mutex_);
max_buffer_size_ = max_size;
// 如果当前缓存大小超过新的最大大小,删除旧的日志
if (log_buffer_.size() > max_size) {
size_t to_remove = log_buffer_.size() - max_size;
log_buffer_.erase(log_buffer_.begin(), log_buffer_.begin() + to_remove);
}
}
size_t log_aggregator::get_current_buffer_size() const {
std::lock_guard<std::mutex> lock(buffer_mutex_);
return log_buffer_.size();
}
void log_aggregator::add_custom_statistic(const std::string &name,
std::function<void(const details::log_msg &, log_statistics &)> statistic_fn) {
std::lock_guard<std::mutex> lock(custom_stats_mutex_);
custom_statistics_[name] = std::move(statistic_fn);
}
std::unordered_map<std::string, double> log_aggregator::get_custom_statistics() const {
std::lock_guard<std::mutex> lock(custom_stats_mutex_);
std::unordered_map<std::string, double> result;
for (const auto &[name, fn] : custom_statistics_) {
// 这里可以根据需要实现自定义统计结果的获取
// 目前只是返回0.0作为示例
result[name] = 0.0;
}
return result;
}
void log_aggregator::update_statistics(const details::log_msg &msg) {
std::lock_guard<std::mutex> lock(statistics_mutex_);
statistics_.total_logs++;
statistics_.last_log_time = msg.time;
// 更新级别计数
statistics_.level_counts[msg.level]++;
// 调用自定义统计函数
std::lock_guard<std::mutex> custom_lock(custom_stats_mutex_);
for (auto &[name, fn] : custom_statistics_) {
try {
fn(msg, statistics_);
} catch (const std::exception &ex) {
// 忽略自定义统计函数的异常
}
}
}
void log_aggregator::update_latency_distribution(const details::log_msg &msg) {
std::string payload(msg.payload.data(), msg.payload.size());
std::optional<double> latency = parse_latency(payload);
if (!latency) {
return;
}
// 提取延迟前缀(如果有的话)
std::string prefix;
size_t colon_pos = payload.find(":");
if (colon_pos != std::string::npos) {
prefix = payload.substr(0, colon_pos);
}
std::lock_guard<std::mutex> lock(latency_mutex_);
auto &dist = latency_distributions_[prefix];
dist.count++;
dist.min_latency = std::min(dist.min_latency, *latency);
dist.max_latency = std::max(dist.max_latency, *latency);
dist.average_latency = (dist.average_latency * (dist.count - 1) + *latency) / dist.count;
// 将延迟分到不同的桶中
std::string bucket;
if (*latency < 1.0) {
bucket = "<1ms";
} else if (*latency < 10.0) {
bucket = "1-10ms";
} else if (*latency < 100.0) {
bucket = "10-100ms";
} else if (*latency < 1000.0) {
bucket = "100-1000ms";
} else {
bucket = ">1000ms";
}
dist.latency_buckets[bucket]++;
}
std::optional<double> log_aggregator::parse_latency(const std::string &msg) const {
// 尝试从日志消息中解析延迟信息
// 支持的格式:
// - latency: 123.45ms
// - time: 123.45ms
// - took: 123.45ms
std::regex latency_regex(R"((latency|time|took):\s*(\d+(?:\.\d+)?)ms)"s);
std::smatch match;
if (std::regex_search(msg, match, latency_regex)) {
try {
double latency = std::stod(match[2]);
return latency;
} catch (const std::exception &ex) {
// 解析失败
}
}
return std::nullopt;
}
} // namespace spdlog

@ -0,0 +1,135 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/details/log_msg.h>
#include <spdlog/common.h>
#include <spdlog/logger.h>
#include <chrono>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <functional>
namespace spdlog {
// 日志统计信息
struct log_statistics {
std::unordered_map<level::level_enum, size_t> level_counts;
size_t total_logs = 0;
std::chrono::system_clock::time_point first_log_time;
std::chrono::system_clock::time_point last_log_time;
};
// 延迟分布
struct latency_distribution {
size_t count = 0;
double min_latency = 0.0;
double max_latency = 0.0;
double average_latency = 0.0;
std::unordered_map<std::string, size_t> latency_buckets;
};
// 查询结果
struct query_result {
std::vector<details::log_msg> logs;
size_t total_matched = 0;
size_t total_logs = 0;
double query_duration = 0.0;
};
// 查询过滤条件
struct query_filter {
std::chrono::system_clock::time_point start_time;
std::chrono::system_clock::time_point end_time;
level::level_enum min_level = level::trace;
level::level_enum max_level = level::critical;
std::string keyword;
std::string logger_name;
size_t limit = 100;
size_t offset = 0;
};
class SPDLOG_API log_aggregator {
public:
explicit log_aggregator(size_t max_buffer_size = 10000);
~log_aggregator();
log_aggregator(const log_aggregator &) = delete;
log_aggregator &operator=(const log_aggregator &) = delete;
// 注册日志记录器
void register_logger(std::shared_ptr<logger> logger);
// 注销日志记录器
void unregister_logger(std::shared_ptr<logger> logger);
// 查询日志
query_result query_logs(const query_filter &filter) const;
// 获取日志统计信息
log_statistics get_statistics() const;
// 获取延迟分布
latency_distribution get_latency_distribution(const std::string &prefix = "") const;
// 清理指定时间之前的日志
void clean_old_logs(std::chrono::system_clock::time_point before_time);
// 设置最大缓存大小
void set_max_buffer_size(size_t max_size);
// 获取当前缓存大小
size_t get_current_buffer_size() const;
// 添加自定义统计函数
void add_custom_statistic(const std::string &name,
std::function<void(const details::log_msg &, log_statistics &)> statistic_fn);
// 获取自定义统计结果
std::unordered_map<std::string, double> get_custom_statistics() const;
private:
// 日志缓存
std::vector<details::log_msg> log_buffer_;
size_t max_buffer_size_;
mutable std::mutex buffer_mutex_;
// 注册的日志记录器
std::unordered_map<std::string, std::shared_ptr<logger>> registered_loggers_;
mutable std::mutex loggers_mutex_;
// 日志统计信息
log_statistics statistics_;
mutable std::mutex statistics_mutex_;
// 延迟分布
std::unordered_map<std::string, latency_distribution> latency_distributions_;
mutable std::mutex latency_mutex_;
// 自定义统计函数
std::unordered_map<std::string, std::function<void(const details::log_msg &, log_statistics &)>> custom_statistics_;
mutable std::mutex custom_stats_mutex_;
// 日志收集回调
std::function<void(const details::log_msg &)> log_callback_;
// 更新统计信息
void update_statistics(const details::log_msg &msg);
// 更新延迟分布
void update_latency_distribution(const details::log_msg &msg);
// 解析延迟信息
std::optional<double> parse_latency(const std::string &msg) const;
};
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "log_aggregator-inl.h"
#endif

@ -22,7 +22,8 @@ SPDLOG_INLINE logger::logger(const logger &other)
level_(other.level_.load(std::memory_order_relaxed)),
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
custom_err_handler_(other.custom_err_handler_),
tracer_(other.tracer_) {}
tracer_(other.tracer_),
desensitizer_(other.desensitizer_) {}
SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT
: name_(std::move(other.name_)),
@ -30,7 +31,8 @@ SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT
level_(other.level_.load(std::memory_order_relaxed)),
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
custom_err_handler_(std::move(other.custom_err_handler_)),
tracer_(std::move(other.tracer_))
tracer_(std::move(other.tracer_)),
desensitizer_(std::move(other.desensitizer_))
{}
@ -55,6 +57,7 @@ SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT {
custom_err_handler_.swap(other.custom_err_handler_);
std::swap(tracer_, other.tracer_);
std::swap(desensitizer_, other.desensitizer_);
}
SPDLOG_INLINE void swap(logger &a, logger &b) noexcept { a.swap(b); }
@ -113,6 +116,31 @@ SPDLOG_INLINE void logger::set_error_handler(err_handler handler) {
custom_err_handler_ = std::move(handler);
}
SPDLOG_INLINE void logger::set_desensitizer(std::shared_ptr<desensitizer> desensitizer) {
desensitizer_ = std::move(desensitizer);
}
SPDLOG_INLINE std::shared_ptr<desensitizer> logger::get_desensitizer() const {
return desensitizer_;
}
SPDLOG_INLINE void logger::add_callback(std::function<void(const details::log_msg &)> callback) {
callbacks_.push_back(std::move(callback));
}
SPDLOG_INLINE void logger::remove_callback(std::function<void(const details::log_msg &)> callback) {
callbacks_.erase(std::remove_if(callbacks_.begin(), callbacks_.end(),
[&callback](const std::function<void(const details::log_msg &)> &c) {
return c.target_type() == callback.target_type() &&
c.target<void(const details::log_msg &)>() == callback.target<void(const details::log_msg &)>();
}),
callbacks_.end());
}
SPDLOG_INLINE void logger::clear_callbacks() {
callbacks_.clear();
}
// create new logger with same sinks and configuration.
SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name) {
auto cloned = std::make_shared<logger>(*this);
@ -124,11 +152,28 @@ SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name) {
SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg,
bool log_enabled,
bool traceback_enabled) {
// 应用脱敏
details::log_msg desensitized_msg = log_msg;
if (desensitizer_) {
desensitizer_->desensitize(desensitized_msg);
}
if (log_enabled) {
sink_it_(log_msg);
sink_it_(desensitized_msg);
}
if (traceback_enabled) {
tracer_.push_back(log_msg);
tracer_.push_back(desensitized_msg);
}
// 调用所有注册的回调函数
for (const auto &callback : callbacks_) {
try {
callback(desensitized_msg);
} catch (const std::exception &ex) {
err_handler_(fmt::format("Callback failed: {}", ex.what()));
} catch (...) {
err_handler_("Callback failed with unknown exception");
}
}
}

@ -17,6 +17,7 @@
#include <spdlog/common.h>
#include <spdlog/details/backtracer.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/desensitizer.h>
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
@ -25,6 +26,8 @@
#include <spdlog/details/os.h>
#endif
#include <functional>
#include <memory>
#include <vector>
#ifndef SPDLOG_NO_EXCEPTIONS
@ -52,7 +55,8 @@ public:
// Empty logger
explicit logger(std::string name)
: name_(std::move(name)),
sinks_() {}
sinks_(),
desensitizer_(std::make_shared<desensitizer>()) {}
// Logger with range on sinks
template <typename It>
@ -74,6 +78,21 @@ public:
logger(logger &&other) SPDLOG_NOEXCEPT;
logger &operator=(logger other) SPDLOG_NOEXCEPT;
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
// 设置脱敏器
void set_desensitizer(std::shared_ptr<desensitizer> desensitizer);
// 获取脱敏器
std::shared_ptr<desensitizer> get_desensitizer() const;
// 添加日志消费回调
void add_callback(std::function<void(const details::log_msg &)> callback);
// 移除日志消费回调
void remove_callback(std::function<void(const details::log_msg &)> callback);
// 清空所有日志消费回调
void clear_callbacks();
template <typename... Args>
void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args) {
@ -311,6 +330,8 @@ protected:
spdlog::level_t flush_level_{level::off};
err_handler custom_err_handler_{nullptr};
details::backtracer tracer_;
std::shared_ptr<desensitizer> desensitizer_;
std::vector<std::function<void(const details::log_msg &)>> callbacks_;
// common implementation for after templated public api has been resolved
template <typename... Args>

@ -0,0 +1,134 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/level_based_sink.h>
#endif
#include <algorithm>
#include <mutex>
namespace spdlog {
namespace sinks {
void level_based_sink::add_sink_for_level(level::level_enum level, sink_ptr sink) {
std::lock_guard<std::mutex> lock(mutex_);
sinks_[level].push_back(std::move(sink));
}
void level_based_sink::add_sink_for_levels(std::initializer_list<level::level_enum> levels, sink_ptr sink) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto level : levels) {
sinks_[level].push_back(sink);
}
}
void level_based_sink::add_sink_for_all_levels(sink_ptr sink) {
std::lock_guard<std::mutex> lock(mutex_);
all_level_sinks_.push_back(std::move(sink));
}
void level_based_sink::remove_sink_for_level(level::level_enum level, sink_ptr sink) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = sinks_.find(level);
if (it != sinks_.end()) {
auto &level_sinks = it->second;
level_sinks.erase(std::remove(level_sinks.begin(), level_sinks.end(), sink), level_sinks.end());
}
}
void level_based_sink::remove_all_sinks_for_level(level::level_enum level) {
std::lock_guard<std::mutex> lock(mutex_);
sinks_.erase(level);
}
void level_based_sink::remove_all_sinks() {
std::lock_guard<std::mutex> lock(mutex_);
sinks_.clear();
all_level_sinks_.clear();
}
void level_based_sink::set_formatter(std::unique_ptr<formatter> formatter) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto &[level, level_sinks] : sinks_) {
for (auto &sink : level_sinks) {
sink->set_formatter(formatter->clone());
}
}
for (auto &sink : all_level_sinks_) {
sink->set_formatter(std::move(formatter));
}
}
void level_based_sink::set_level(level::level_enum level) {
std::lock_guard<std::mutex> lock(mutex_);
level_ = level;
}
level::level_enum level_based_sink::level() const {
std::lock_guard<std::mutex> lock(mutex_);
return level_;
}
bool level_based_sink::should_log(level::level_enum level) const {
std::lock_guard<std::mutex> lock(mutex_);
return level >= level_;
}
void level_based_sink::log(const details::log_msg &msg) {
std::lock_guard<std::mutex> lock(mutex_);
// 首先发送到所有级别的sink
for (auto &sink : all_level_sinks_) {
if (sink->should_log(msg.level)) {
try {
sink->log(msg);
} catch (const std::exception &ex) {
// 忽略sink的异常继续处理其他sink
}
}
}
// 然后发送到对应级别的sink
auto it = sinks_.find(msg.level);
if (it != sinks_.end()) {
for (auto &sink : it->second) {
if (sink->should_log(msg.level)) {
try {
sink->log(msg);
} catch (const std::exception &ex) {
// 忽略sink的异常继续处理其他sink
}
}
}
}
}
void level_based_sink::flush() {
std::lock_guard<std::mutex> lock(mutex_);
// 刷新所有级别的sink
for (auto &sink : all_level_sinks_) {
try {
sink->flush();
} catch (const std::exception &ex) {
// 忽略sink的异常继续处理其他sink
}
}
// 刷新对应级别的sink
for (auto &[level, level_sinks] : sinks_) {
for (auto &sink : level_sinks) {
try {
sink->flush();
} catch (const std::exception &ex) {
// 忽略sink的异常继续处理其他sink
}
}
}
}
} // namespace sinks
} // namespace spdlog

@ -0,0 +1,73 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/common.h>
#include <memory>
#include <vector>
#include <unordered_map>
namespace spdlog {
namespace sinks {
// 基于日志级别的sink根据日志级别将日志分发到不同的sink
class SPDLOG_API level_based_sink : public sink {
public:
explicit level_based_sink() = default;
~level_based_sink() override = default;
level_based_sink(const level_based_sink &) = delete;
level_based_sink &operator=(const level_based_sink &) = delete;
// 添加sink到指定级别
void add_sink_for_level(level::level_enum level, sink_ptr sink);
// 添加sink到多个级别
void add_sink_for_levels(std::initializer_list<level::level_enum> levels, sink_ptr sink);
// 添加sink到所有级别
void add_sink_for_all_levels(sink_ptr sink);
// 移除指定级别上的指定sink
void remove_sink_for_level(level::level_enum level, sink_ptr sink);
// 移除指定级别上的所有sink
void remove_all_sinks_for_level(level::level_enum level);
// 移除所有sink
void remove_all_sinks();
// 设置sink的格式化器
void set_formatter(std::unique_ptr<formatter> formatter) override;
// 设置sink的日志级别
void set_level(level::level_enum level) override;
// 获取sink的日志级别
level::level_enum level() const override;
// 检查sink是否应该记录指定级别的日志
bool should_log(level::level_enum level) const override;
// 记录日志
void log(const details::log_msg &msg) override;
// 刷新sink
void flush() override;
private:
std::unordered_map<level::level_enum, std::vector<sink_ptr>> sinks_;
std::vector<sink_ptr> all_level_sinks_;
level::level_enum level_ = level::trace;
};
} // namespace sinks
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "level_based_sink-inl.h"
#endif

@ -98,6 +98,19 @@ SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
// Will replace any existing logger with the same name.
SPDLOG_API void register_or_replace(std::shared_ptr<logger> logger);
// Log aggregator functions
// Get global log aggregator instance
SPDLOG_API std::shared_ptr<spdlog::aggregator> aggregator();
// Set maximum number of logs to keep in aggregator
SPDLOG_API void set_max_aggregated_logs(size_t max_logs);
// Get all aggregated logs with specified level or higher
SPDLOG_API std::vector<spdlog::details::log_msg> get_aggregated_logs(level::level_enum level = level::trace);
// Clear all aggregated logs
SPDLOG_API void clear_aggregated_logs();
// Apply a user-defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});

Loading…
Cancel
Save