19 #include <condition_variable>
30 #include <string_view>
34 namespace diagnostics {
84 Impl() {
parse_format(
"{timestamp} [{level}] [{component}] [{operation}] {message}"); }
92 static const std::string default_format =
"{timestamp} [{level}] [{component}] [{operation}] {message}";
93 static std::shared_ptr<LogFormat> cached_default_format;
94 static std::once_flag flag;
96 std::call_once(flag, []() {
97 auto new_format = std::make_shared<LogFormat>();
98 new_format->format_string = default_format;
99 new_format->parsed_format.reserve(9);
109 cached_default_format = new_format;
112 if (format == default_format) {
113 std::lock_guard<std::mutex> lock(
mutex_);
118 auto new_format = std::make_shared<LogFormat>();
119 new_format->format_string = format;
122 for (
char c : format) {
123 if (c ==
'{') count += 2;
125 new_format->parsed_format.reserve(count);
129 std::string_view format_view = format;
131 while ((pos = format_view.find(
'{', start)) != std::string_view::npos) {
133 new_format->parsed_format.push_back({
FormatPart::LITERAL, std::string(format_view.substr(start, pos - start))});
136 size_t end = format_view.find(
'}', pos);
137 if (end == std::string_view::npos) {
138 new_format->parsed_format.push_back({
FormatPart::LITERAL, std::string(format_view.substr(pos))});
139 start = format_view.length();
143 std::string_view placeholder = format_view.substr(pos + 1, end - pos - 1);
144 if (placeholder ==
"timestamp") {
146 }
else if (placeholder ==
"level") {
148 }
else if (placeholder ==
"component") {
150 }
else if (placeholder ==
"operation") {
152 }
else if (placeholder ==
"message") {
155 new_format->parsed_format.push_back({
FormatPart::LITERAL, std::string(format_view.substr(pos, end - pos + 1))});
161 if (start < format_view.length()) {
162 new_format->parsed_format.push_back({
FormatPart::LITERAL, std::string(format_view.substr(start))});
165 std::lock_guard<std::mutex> lock(
mutex_);
170 std::lock_guard<std::mutex> lock(
mutex_);
179 std::string_view component, std::string_view operation, std::string_view message) {
183 std::shared_ptr<LogFormat> current_format;
185 std::lock_guard<std::mutex> lock(
mutex_);
190 if (current_format) {
191 result.reserve(current_format->format_string.length() + message.length() + 32);
193 for (
const auto& part : current_format->parsed_format) {
196 result.append(part.value);
199 result.append(timestamp.
view());
202 result.append(level_str);
205 result.append(component);
208 result.append(operation);
211 result.append(message);
221 std::lock_guard<std::mutex> lock(
mutex_);
222 int current_outputs =
outputs_.load();
255 auto time_t = std::chrono::system_clock::to_time_t(timestamp);
256 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp.time_since_epoch()) % 1000;
258 static thread_local std::time_t last_t = -1;
259 static thread_local
char date_buf[32] = {0};
261 if (time_t != last_t) {
264 ::localtime_s(&time_info, &time_t);
266 ::localtime_r(&time_t, &time_info);
268 std::strftime(date_buf,
sizeof(date_buf),
"%Y-%m-%d %H:%M:%S", &time_info);
273 int len = std::snprintf(result.
data,
sizeof(result.
data),
"%s.%03d", date_buf,
static_cast<int>(ms.count()));
275 if (
static_cast<size_t>(len) >=
sizeof(result.
data)) {
278 result.
length =
static_cast<size_t>(len);
287 if (message.find(
"[ERROR]") != std::string::npos || message.find(
"[CRITICAL]") != std::string::npos) {
288 std::cerr << message << std::endl;
291 std::cout << message << std::endl;
293 std::cout << message <<
'\n';
308 }
catch (
const std::exception& e) {
309 std::cerr <<
"Error in log callback: " << e.what() << std::endl;
311 std::cerr <<
"Unknown error in log callback" << std::endl;
321 bool should_rotate =
false;
324 if (current_pos !=
static_cast<std::streampos
>(-1) &&
325 static_cast<size_t>(current_pos) >=
log_rotation_->get_config().max_file_size_bytes) {
326 should_rotate =
true;
330 should_rotate =
true;
334 if (!should_rotate) {
349 file_output_ = std::make_unique<std::ofstream>(filename, std::ios::app);
354 std::cerr <<
"Failed to open log file: " << filename << std::endl;
362 std::lock_guard<std::mutex> lock(
mutex_);
374 std::thread worker_to_join;
375 bool notify_worker =
false;
377 std::lock_guard<std::mutex> lock(
mutex_);
382 notify_worker =
true;
394 if (worker_to_join.joinable()) {
395 worker_to_join.join();
403 std::vector<LogEntry> batch;
406 auto last_flush = std::chrono::steady_clock::now();
412 [
this] { return !log_queue_.empty() || shutdown_requested_.load(); });
419 batch.reserve(batch_size);
421 for (
size_t i = 0; i < batch_size && !
log_queue_.empty(); ++i) {
422 batch.push_back(std::move(
log_queue_.front()));
428 if (!batch.empty()) {
436 auto now = std::chrono::steady_clock::now();
453 for (
const auto& entry : batch) {
454 std::string formatted_message =
455 format_message(entry.timestamp, entry.level, entry.component, entry.operation, entry.message);
510 std::lock_guard<std::mutex> lock(impl_->mutex_);
519 std::lock_guard<std::mutex> lock(impl_->mutex_);
521 if (filename.empty()) {
522 impl_->file_output_.reset();
523 impl_->log_rotation_.reset();
524 impl_->current_log_file_.clear();
527 impl_->open_log_file(filename);
532 std::lock_guard<std::mutex> lock(impl_->mutex_);
534 if (filename.empty()) {
535 impl_->file_output_.reset();
536 impl_->log_rotation_.reset();
537 impl_->current_log_file_.clear();
540 impl_->log_rotation_ = std::make_unique<LogRotation>(config);
541 impl_->current_log_file_ = filename;
542 impl_->open_log_file(filename);
548 impl_->setup_async_logging(config);
550 impl_->teardown_async_logging();
557 std::lock_guard<std::mutex> lock(get_impl()->stats_mutex_);
565 std::lock_guard<std::mutex> lock(impl_->mutex_);
566 impl_->callback_ = std::move(callback);
567 if (impl_->callback_) {
584 void Logger::log(
LogLevel level, std::string_view component, std::string_view operation, std::string_view message) {
585 if (!get_impl()->enabled_.load() || level < get_impl()->current_level_.load()) {
589 if (get_impl()->async_enabled_.load()) {
590 LogEntry entry(level, component, operation, message);
591 impl_->update_stats_on_enqueue();
593 if (get_impl()->async_config_.enable_backpressure && impl_->should_drop_log()) {
594 impl_->update_stats_on_drop();
599 std::lock_guard<std::mutex> lock(impl_->queue_mutex_);
600 impl_->log_queue_.push(entry);
603 impl_->queue_cv_.notify_one();
607 std::string formatted_message =
608 impl_->format_message(std::chrono::system_clock::now(), level, component, operation, message);
609 if (get_impl()->outputs_.load(std::memory_order_relaxed) != 0) {
610 impl_->write_to_sinks(level, formatted_message);
614 void Logger::debug(std::string_view component, std::string_view operation, std::string_view message) {
618 void Logger::info(std::string_view component, std::string_view operation, std::string_view message) {
622 void Logger::warning(std::string_view component, std::string_view operation, std::string_view message) {
626 void Logger::error(std::string_view component, std::string_view operation, std::string_view message) {
630 void Logger::critical(std::string_view component, std::string_view operation, std::string_view message) {
Centralized logging system with async support.
void set_format(const std::string &format)
Set log format.
void log(LogLevel level, std::string_view component, std::string_view operation, std::string_view message)
static Logger & default_logger()
void critical(std::string_view component, std::string_view operation, std::string_view message)
AsyncLogStats get_async_stats() const
Get async logging statistics.
static Logger & instance()
Get singleton instance.
void set_console_output(bool enable)
Enable/disable console output.
LogLevel get_level() const
Get current log level.
void set_async_logging(bool enable, const AsyncLogConfig &config=AsyncLogConfig{})
Enable/disable async logging.
bool is_async_logging_enabled() const
Check if async logging is enabled.
void info(std::string_view component, std::string_view operation, std::string_view message)
bool is_enabled() const
Check if logging is enabled.
void set_level(LogLevel level)
Set minimum log level.
void error(std::string_view component, std::string_view operation, std::string_view message)
std::function< void(LogLevel level, const std::string &formatted_message)> LogCallback
void flush()
Flush all outputs.
void debug(std::string_view component, std::string_view operation, std::string_view message)
void warning(std::string_view component, std::string_view operation, std::string_view message)
void set_file_output_with_rotation(const std::string &filename, const LogRotationConfig &config=LogRotationConfig{})
Set file output with rotation.
void set_file_output(const std::string &filename)
Set file output.
void set_outputs(int outputs)
Set output destinations.
void set_callback(LogCallback callback)
Set log callback.
void set_enabled(bool enabled)
Enable/disable logging.
LogLevel
Log severity levels.
Async logging configuration.
bool enable_batch_processing
std::chrono::milliseconds flush_interval
Async logging statistics.
uint64_t max_queue_size_reached
Log entry structure for async processing.
Log rotation configuration.
std::string_view view() const
TimestampBuffer get_timestamp(std::chrono::system_clock::time_point timestamp) const
std::atomic< bool > async_enabled_
std::atomic< bool > enabled_
std::atomic< bool > running_
void write_to_console(const std::string &message) const
void setup_async_logging(const AsyncLogConfig &config)
std::thread worker_thread_
std::condition_variable queue_cv_
void write_to_file(const std::string &message)
size_t get_queue_size() const
std::queue< LogEntry > log_queue_
AsyncLogConfig async_config_
void update_stats_on_drop()
std::string_view level_to_string(LogLevel level) const
void call_callback(LogLevel level, const std::string &message)
std::atomic< int > outputs_
void update_stats_on_batch(size_t)
std::string format_message(std::chrono::system_clock::time_point timestamp_val, LogLevel level, std::string_view component, std::string_view operation, std::string_view message)
std::atomic< bool > shutdown_requested_
std::atomic< LogLevel > current_level_
void teardown_async_logging()
std::shared_ptr< LogFormat > log_format_
void write_to_sinks(LogLevel level, const std::string &formatted_message)
void parse_format(const std::string &format)
std::unique_ptr< std::ofstream > file_output_
void process_batch(const std::vector< LogEntry > &batch)
std::unique_ptr< LogRotation > log_rotation_
void update_stats_on_enqueue()
bool should_drop_log() const
void update_stats_on_flush()
void open_log_file(const std::string &filename)
void check_and_rotate_log()
std::string current_log_file_
AsyncLogStats async_stats_