LogIt++
Loading...
Searching...
No Matches
UniqueFileLogger.hpp
Go to the documentation of this file.
1#ifndef _LOGIT_UNIQUE_FILE_LOGGER_HPP_INCLUDED
2#define _LOGIT_UNIQUE_FILE_LOGGER_HPP_INCLUDED
5
6#include "ILogger.hpp"
7#include <iostream>
8#include <fstream>
9#include <mutex>
10#include <atomic>
11#include <regex>
12#include <queue>
13#include <functional>
14#include <sstream>
15#include <iomanip>
16#include <ctime>
17#include <random>
18#include <algorithm>
19#include <unordered_map>
20
21namespace logit {
22
35 class UniqueFileLogger : public ILogger {
36 public:
37
40 struct Config {
41 std::string directory = "unique_logs";
42 bool async = true;
44 size_t hash_length = 8;
45 };
46
51
54 UniqueFileLogger(const Config& config) : m_config(config) {
56 }
57
64 const std::string& directory,
65 bool async = true,
66 int auto_delete_days = 30,
67 size_t hash_length = 8) {
68 m_config.directory = directory;
69 m_config.async = async;
70 m_config.auto_delete_days = auto_delete_days;
71 m_config.hash_length = hash_length;
73 }
74
77 }
78
86 void log(const LogRecord& record, const std::string& message) override {
87 auto thread_id = record.thread_id;
89 if (!m_config.async) {
90 std::lock_guard<std::mutex> lock(m_mutex);
91 std::string file_path;
92 try {
93 file_path = write_log(message, record.timestamp_ms);
94 } catch (const std::exception& e) {
95 file_path.clear();
96 std::cerr << "Log error: " << e.what() << std::endl;
97 }
98
99 std::unique_lock<std::mutex> info_lock(m_thread_log_info_mutex);
100 auto it = m_thread_log_info.find(thread_id);
101 if (it == m_thread_log_info.end()) {
102 if (!file_path.empty()) {
103 m_thread_log_info[thread_id] = {0, file_path, get_file_name(file_path)};
104 } else {
105 m_thread_log_info[thread_id] = {0, "Not available", "Not available"};
106 }
107 return;
108 }
109
110 if (!file_path.empty()) {
111 it->second.last_file_path = file_path;
112 it->second.last_file_name = get_file_name(file_path);
113 } else {
114 it->second.last_file_path = "Not available";
115 it->second.last_file_name = "Not available";
116 }
117
118 m_pending_logs_cv.notify_all();
119 info_lock.unlock();
120
121 try {
123 } catch (const std::exception& e) {
124 std::cerr << "Log error: " << e.what() << std::endl;
125 }
126 return;
127 }
128
129 std::unique_lock<std::mutex> info_lock(m_thread_log_info_mutex);
130 m_thread_log_info[thread_id].pending_logs++;
131 info_lock.unlock();
132
133 auto timestamp_ms = record.timestamp_ms;
134 TaskExecutor::get_instance().add_task([this, message, timestamp_ms, thread_id]() {
135 std::lock_guard<std::mutex> lock(m_mutex);
136 std::string file_path;
137 try {
138 file_path = write_log(message, timestamp_ms);
139 } catch (const std::exception& e) {
140 file_path.clear();
141 std::cerr << "Async log error: " << e.what() << std::endl;
142 }
143
144 std::unique_lock<std::mutex> info_lock(m_thread_log_info_mutex);
145 auto it = m_thread_log_info.find(thread_id);
146 if (it == m_thread_log_info.end()) return;
147
148 if (!file_path.empty()) {
149 it->second.last_file_path = file_path;
150 it->second.last_file_name = get_file_name(file_path);
151 } else {
152 it->second.last_file_path = "Not available";
153 it->second.last_file_name = "Not available";
154 }
155 it->second.pending_logs--;
156
157 if (it->second.pending_logs == 0) {
158 m_pending_logs_cv.notify_all();
159 }
160 info_lock.unlock();
161
162 try {
164 } catch (const std::exception& e) {
165 std::cerr << "Async log error: " << e.what() << std::endl;
166 }
167 });
168 }
169
173 std::string get_string_param(const LoggerParam& param) const override {
174 switch (param) {
177 case LoggerParam::LastLogTimestamp: return std::to_string(get_last_log_ts());
178 case LoggerParam::TimeSinceLastLog: return std::to_string(get_time_since_last_log());
179 default:
180 break;
181 };
182 return std::string();
183 }
184
188 int64_t get_int_param(const LoggerParam& param) const override {
189 switch (param) {
192 default:
193 break;
194 };
195 return 0;
196 }
197
201 double get_float_param(const LoggerParam& param) const override {
202 switch (param) {
203 case LoggerParam::LastLogTimestamp: return (double)get_last_log_ts() / 1000.0;
204 case LoggerParam::TimeSinceLastLog: return (double)get_time_since_last_log() / 1000.0;
205 default:
206 break;
207 };
208 return 0.0;
209 }
210
212 void wait() override {
213 if (!m_config.async) return;
215 }
216
217 private:
218 mutable std::mutex m_mutex;
220
223 std::string last_file_path;
224 std::string last_file_name;
225
227
229 const int pending_logs,
230 const std::string last_file_path,
231 const std::string last_file_name) :
235 }
236 };
237
238 mutable std::mutex m_thread_log_info_mutex;
239 mutable std::condition_variable m_pending_logs_cv;
240 std::unordered_map<std::thread::id, ThreadLogInfo> m_thread_log_info;
241
242 std::atomic<int64_t> m_last_log_ts = ATOMIC_VAR_INIT(0);
243
244
247 std::lock_guard<std::mutex> lock(m_mutex);
248 try {
251 } catch (const std::exception& e) {
252 std::cerr << "Initialization error: " << e.what() << std::endl;
253 }
254 }
255
258 wait();
259 }
260
265
268 std::string get_directory_path() const {
269 return get_exec_dir() + "/" + m_config.directory;
270 }
271
276 std::string write_log(const std::string& message, const int64_t& timestamp_ms) {
277 std::string file_path = create_unique_file_path(timestamp_ms);
278# if defined(_WIN32)
279 std::ofstream file(utf8_to_ansi(file_path), std::ios_base::binary);
280# else
281 std::ofstream file(file_path, std::ios_base::binary);
282# endif
283 if (!file.is_open()) {
284 throw std::runtime_error("Failed to open log file: " + file_path);
285 }
286 file.write(message.data(), message.size());
287 file.close();
288 return file_path;
289 }
290
294 std::string create_unique_file_path(const int64_t& timestamp_ms) const {
295 const std::string timestamp_str = format_timestamp(timestamp_ms);
296 const std::string hash_str = generate_fixed_length_hash(m_config.hash_length);
297 return get_directory_path() + "/" + timestamp_str + "-" + hash_str + ".log";
298 }
299
303 std::string format_timestamp(const int64_t& timestamp_ms) const {
304 const auto dt = time_shield::to_date_time_ms<time_shield::DateTimeStruct>(timestamp_ms);
305 char buffer[32] = {0};
306 snprintf(buffer, sizeof(buffer), "%lld-%.2d-%.2d_%.2d-%.2d-%.2d-%.3d", dt.year, dt.mon, dt.day, dt.hour, dt.min, dt.sec, dt.ms);
307 return std::string(buffer);
308 }
309
313 std::string generate_fixed_length_hash(size_t length) const {
314 static const char charset[] =
315 "0123456789"
316 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
317 "abcdefghijklmnopqrstuvwxyz";
318 static thread_local std::mt19937 generator(std::random_device{}());
319 static thread_local std::uniform_int_distribution<size_t> distribution(0, sizeof(charset) - 2);
320
321 std::string hash;
322 hash.reserve(length);
323 for (size_t i = 0; i < length; ++i) {
324 hash += charset[distribution(generator)];
325 }
326 return hash;
327 }
328
331 const int64_t threshold_ts =
332 time_shield::ms_to_sec(current_timestamp_ms()) -
333 (m_config.auto_delete_days * time_shield::SEC_PER_DAY);
334# if __cplusplus >= 201703L
335
336# if defined(_WIN32)
337 fs::path dir_path = fs::u8path(get_directory_path());
338# else
339 fs::path dir_path(get_directory_path());
340# endif
341
342 if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) {
343 return;
344 }
345
346 for (const auto& entry : fs::directory_iterator(dir_path)) {
347 if (!fs::is_regular_file(entry.status())) continue;
348 std::string filename = entry.path().filename().string();
349 if (is_valid_log_filename(filename)) {
350 const int64_t file_ts = get_timestamp_from_filename(filename);
351 if (file_ts < threshold_ts) {
352 fs::remove(entry.path());
353 }
354 }
355 }
356# else
357 const std::vector<std::string> file_list = get_list_files(get_directory_path());
358 for (const auto& file_path : file_list) {
359 std::string filename = get_file_name(file_path);
360 if (is_valid_log_filename(filename)) {
361 const int64_t file_ts = get_timestamp_from_filename(filename);
362 if (file_ts < threshold_ts) {
363# if defined(_WIN32)
364 remove(utf8_to_ansi(file_path).c_str());
365# else
366 remove(file_path.c_str());
367# endif
368 }
369 }
370 }
371# endif
372 }
373
377 bool is_valid_log_filename(const std::string& filename) const {
378 static const std::regex pattern(R"((\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3})-[a-zA-Z0-9]{1,}\.log)");
379 return std::regex_match(filename, pattern);
380 }
381
385 int64_t get_timestamp_from_filename(const std::string& filename) const {
386 std::string datetime_str = filename.substr(0, 10); // "YYYY-MM-DD"
387 return time_shield::ts(datetime_str);
388 }
389
392 int64_t current_timestamp_ms() const {
393 return LOGIT_CURRENT_TIMESTAMP_MS();
394 }
395
402 std::string get_last_log_file_name() const {
403 auto thread_id = std::this_thread::get_id();
404 std::unique_lock<std::mutex> lock(m_thread_log_info_mutex);
405 m_pending_logs_cv.wait(lock, [this, thread_id]() {
406 auto it = m_thread_log_info.find(thread_id);
407 return it == m_thread_log_info.end() || it->second.pending_logs == 0;
408 });
409 auto it = m_thread_log_info.find(thread_id);
410 if (it != m_thread_log_info.end()) {
411 return it->second.last_file_name;
412 }
413 return std::string();
414 }
415
422 std::string get_last_log_file_path() const {
423 auto thread_id = std::this_thread::get_id();
424 std::unique_lock<std::mutex> lock(m_thread_log_info_mutex);
425 m_pending_logs_cv.wait(lock, [this, thread_id]() {
426 auto it = m_thread_log_info.find(thread_id);
427 return it == m_thread_log_info.end() || it->second.pending_logs == 0;
428 });
429 auto it = m_thread_log_info.find(thread_id);
430 if (it != m_thread_log_info.end()) {
431 return it->second.last_file_path;
432 }
433 return std::string();
434 }
435
438 int64_t get_last_log_ts() const {
439 return m_last_log_ts;
440 }
441
444 int64_t get_time_since_last_log() const {
445 return LOGIT_CURRENT_TIMESTAMP_MS() - m_last_log_ts;
446 }
447
448 }; // UniqueFileLogger
449
450}; // namespace logit
451
452#endif // _LOGIT_UNIQUE_FILE_LOGGER_HPP_INCLUDED
Defines the interface for loggers used in the logging system.
Interface for loggers that handle log message output.
Definition ILogger.hpp:25
void add_task(std::function< void()> task)
Adds a task to the queue in a thread-safe manner.
void wait()
Waits for all tasks in the queue to be processed.
static TaskExecutor & get_instance()
Get the singleton instance of the TaskExecutor.
int64_t current_timestamp_ms() const
Gets the current timestamp in milliseconds.
std::string get_directory_path() const
Gets the full path to the logging directory.
std::string generate_fixed_length_hash(size_t length) const
Generates a fixed-length hash string.
void wait() override
Waits for all asynchronous tasks to complete.
int64_t get_last_log_ts() const
Retrieves the timestamp of the last log.
void initialize_directory()
Initializes the logging directory.
double get_float_param(const LoggerParam &param) const override
Retrieves a floating-point parameter from the logger.
std::string get_last_log_file_path() const
Retrieves the last log file path for the calling thread.
std::condition_variable m_pending_logs_cv
Condition variable to wait for pending logs to finish.
std::unordered_map< std::thread::id, ThreadLogInfo > m_thread_log_info
Map to store log information per thread.
UniqueFileLogger(const std::string &directory, bool async=true, int auto_delete_days=30, size_t hash_length=8)
Constructor with directory and asynchronous flag.
Config m_config
Configuration for the unique file logger.
UniqueFileLogger()
Default constructor that uses default configuration.
UniqueFileLogger(const Config &config)
Constructor with custom configuration.
int64_t get_timestamp_from_filename(const std::string &filename) const
Extracts the timestamp from the filename.
void stop_logging()
Stops the logging process by waiting for tasks.
std::atomic< int64_t > m_last_log_ts
Timestamp of the last log.
std::string get_string_param(const LoggerParam &param) const override
Retrieves a string parameter from the logger.
void log(const LogRecord &record, const std::string &message) override
Logs a message to a unique file with thread safety.
std::mutex m_mutex
Mutex to protect file operations.
std::string create_unique_file_path(const int64_t &timestamp_ms) const
Creates a unique file path based on the timestamp and a hash.
std::string format_timestamp(const int64_t &timestamp_ms) const
Formats the timestamp into a string with date and time.
std::string get_last_log_file_name() const
Retrieves the last log file name for the calling thread.
void remove_old_logs()
Removes old log files based on the auto-delete days configuration.
void start_logging()
Starts the logging process by initializing the directory and removing old logs.
int64_t get_int_param(const LoggerParam &param) const override
Retrieves an integer parameter from the logger.
std::mutex m_thread_log_info_mutex
Mutex to protect access to thread log information.
int64_t get_time_since_last_log() const
Retrieves the time elapsed since the last log.
std::string write_log(const std::string &message, const int64_t &timestamp_ms)
Writes a log message to a unique file.
bool is_valid_log_filename(const std::string &filename) const
Checks if the filename matches the log file naming pattern.
The primary namespace for the LogIt++ library.
std::vector< std::string > get_list_files(const std::string &path)
Recursively retrieves a list of all files in a directory.
std::string utf8_to_ansi(const std::string &utf8)
Converts a UTF-8 string to an ANSI string (Windows-specific).
void create_directories(const std::string &path)
Creates directories recursively for the given path using C++17 std::filesystem.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
Definition Enums.hpp:46
@ TimeSinceLastLog
The time elapsed since the last log in seconds.
Definition Enums.hpp:50
@ LastLogTimestamp
The timestamp of the last log.
Definition Enums.hpp:49
@ LastFileName
The name of the last file written to.
Definition Enums.hpp:47
@ LastFilePath
The full path of the last file written to.
Definition Enums.hpp:48
std::string get_file_name(const std::string &file_path)
Extracts the file name from a full file path.
std::string get_exec_dir()
Retrieves the directory of the executable file.
Stores log metadata and content.
Definition LogRecord.hpp:13
std::thread::id thread_id
ID of the logging thread.
Definition LogRecord.hpp:22
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:15
Configuration for the unique file logger.
int auto_delete_days
Number of days after which old log files are deleted.
std::string directory
Directory where log files are stored.
bool async
Flag indicating whether logging should be asynchronous.
size_t hash_length
Length of the hash used in filenames.
ThreadLogInfo(const int pending_logs, const std::string last_file_path, const std::string last_file_name)