unilink  0.4.3
A simple C++ library for unified async communication
logger.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2025 Jinwoo Sung
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <atomic>
20 #include <chrono>
21 #include <functional>
22 #include <memory>
23 #include <string>
24 #include <string_view>
25 #include <vector>
26 
27 #include "log_rotation.hpp"
29 
30 #ifdef DEBUG
31 #undef DEBUG
32 #endif
33 #ifdef INFO
34 #undef INFO
35 #endif
36 #ifdef WARNING
37 #undef WARNING
38 #endif
39 #ifdef ERROR
40 #undef ERROR
41 #endif
42 #ifdef CRITICAL
43 #undef CRITICAL
44 #endif
45 #ifdef CALLBACK
46 #undef CALLBACK
47 #endif
48 
49 namespace unilink {
50 namespace diagnostics {
51 
55 enum class LogLevel { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3, CRITICAL = 4 };
56 
60 enum class LogOutput { CONSOLE = 0x01, FILE = 0x02, CALLBACK = 0x04 };
61 
65 struct LogEntry {
66  std::chrono::system_clock::time_point timestamp;
68  std::string component;
69  std::string operation;
70  std::string message;
71  std::string formatted_message;
72 
74 
75  LogEntry(LogLevel lvl, std::string_view comp, std::string_view op, std::string_view msg)
76  : timestamp(std::chrono::system_clock::now()), level(lvl), component(comp), operation(op), message(msg) {}
77 };
78 
83  size_t max_queue_size = 10000; // Maximum queue size
84  size_t batch_size = 100; // Batch processing size
85  std::chrono::milliseconds flush_interval{100}; // Flush interval
86  std::chrono::milliseconds shutdown_timeout{5000}; // Shutdown timeout
87  bool enable_backpressure = true; // Enable backpressure handling
88  bool enable_batch_processing = true; // Enable batch processing
89 
90  AsyncLogConfig() = default;
91 
92  AsyncLogConfig(size_t max_q, size_t batch, std::chrono::milliseconds interval)
93  : max_queue_size(max_q), batch_size(batch), flush_interval(interval) {}
94 };
95 
99 struct AsyncLogStats {
100  uint64_t total_logs{0};
101  uint64_t dropped_logs{0};
102  uint64_t queue_size{0};
104  uint64_t batch_count{0};
105  uint64_t flush_count{0};
106  std::chrono::system_clock::time_point start_time;
107 
108  AsyncLogStats() : start_time(std::chrono::system_clock::now()) {}
109 
110  void reset() {
111  total_logs = 0;
112  dropped_logs = 0;
113  queue_size = 0;
115  batch_count = 0;
116  flush_count = 0;
117  start_time = std::chrono::system_clock::now();
118  }
119 
120  double get_drop_rate() const {
121  if (total_logs == 0) return 0.0;
122  return static_cast<double>(dropped_logs) / static_cast<double>(total_logs);
123  }
124 
125  std::chrono::milliseconds get_uptime() const {
126  return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start_time);
127  }
128 };
129 
137  public:
138  using LogCallback = std::function<void(LogLevel level, const std::string& formatted_message)>;
139 
143  static Logger& instance();
144  static Logger& default_logger();
145 
146  Logger();
148 
149  // Move semantics
150  Logger(Logger&&) noexcept;
151  Logger& operator=(Logger&&) noexcept;
152 
153  // Non-copyable
154  Logger(const Logger&) = delete;
155  Logger& operator=(const Logger&) = delete;
156 
161  void set_level(LogLevel level);
162 
166  LogLevel get_level() const;
167 
172  void set_console_output(bool enable);
173 
178  void set_file_output(const std::string& filename);
179 
185  void set_file_output_with_rotation(const std::string& filename,
186  const LogRotationConfig& config = LogRotationConfig{});
187 
193  void set_async_logging(bool enable, const AsyncLogConfig& config = AsyncLogConfig{});
194 
198  bool is_async_logging_enabled() const;
199 
203  AsyncLogStats get_async_stats() const;
204 
209  void set_callback(LogCallback callback);
210 
215  void set_outputs(int outputs);
216 
221  void set_enabled(bool enabled);
222 
226  bool is_enabled() const;
227 
232  void set_format(const std::string& format);
233 
237  void flush();
238 
239  // Main logging functions
240  void log(LogLevel level, std::string_view component, std::string_view operation, std::string_view message);
241 
242  void debug(std::string_view component, std::string_view operation, std::string_view message);
243  void info(std::string_view component, std::string_view operation, std::string_view message);
244  void warning(std::string_view component, std::string_view operation, std::string_view message);
245  void error(std::string_view component, std::string_view operation, std::string_view message);
246  void critical(std::string_view component, std::string_view operation, std::string_view message);
247 
248  private:
249  struct Impl;
250  const Impl* get_impl() const { return impl_.get(); }
251  Impl* get_impl() { return impl_.get(); }
252  std::unique_ptr<Impl> impl_;
253 };
254 
258 #define UNILINK_LOG_DEBUG(component, operation, message) \
259  do { \
260  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::DEBUG) { \
261  unilink::diagnostics::Logger::instance().debug(component, operation, message); \
262  } \
263  } while (0)
264 
265 #define UNILINK_LOG_INFO(component, operation, message) \
266  do { \
267  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::INFO) { \
268  unilink::diagnostics::Logger::instance().info(component, operation, message); \
269  } \
270  } while (0)
271 
272 #define UNILINK_LOG_WARNING(component, operation, message) \
273  do { \
274  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::WARNING) { \
275  unilink::diagnostics::Logger::instance().warning(component, operation, message); \
276  } \
277  } while (0)
278 
279 #define UNILINK_LOG_ERROR(component, operation, message) \
280  do { \
281  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::ERROR) { \
282  unilink::diagnostics::Logger::instance().error(component, operation, message); \
283  } \
284  } while (0)
285 
286 #define UNILINK_LOG_CRITICAL(component, operation, message) \
287  do { \
288  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::CRITICAL) { \
289  unilink::diagnostics::Logger::instance().critical(component, operation, message); \
290  } \
291  } while (0)
292 
296 #define UNILINK_LOG_DEBUG_IF(component, operation, message) \
297  do { \
298  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::DEBUG) { \
299  UNILINK_LOG_DEBUG(component, operation, message); \
300  } \
301  } while (0)
302 
303 #define UNILINK_LOG_INFO_IF(component, operation, message) \
304  do { \
305  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::INFO) { \
306  UNILINK_LOG_INFO(component, operation, message); \
307  } \
308  } while (0)
309 
313 #define UNILINK_LOG_PERF_START(component, operation) \
314  auto _perf_start_##operation = \
315  (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::DEBUG) \
316  ? std::chrono::high_resolution_clock::now() \
317  : std::chrono::high_resolution_clock::time_point()
318 
319 #define UNILINK_LOG_PERF_END(component, operation) \
320  do { \
321  if (unilink::diagnostics::Logger::instance().get_level() <= unilink::diagnostics::LogLevel::DEBUG) { \
322  auto _perf_end_##operation = std::chrono::high_resolution_clock::now(); \
323  using _us_t = std::chrono::microseconds; \
324  auto _diff_##operation = _perf_end_##operation - _perf_start_##operation; \
325  auto _perf_duration_##operation = std::chrono::duration_cast<_us_t>(_diff_##operation).count(); \
326  UNILINK_LOG_DEBUG(component, operation, "Duration: " + std::to_string(_perf_duration_##operation) + " μs"); \
327  } \
328  } while (0)
329 
330 } // namespace diagnostics
331 } // namespace unilink
#define UNILINK_API
Definition: visibility.hpp:37