LogIt++
Loading...
Searching...
No Matches
FileLogger.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _LOGIT_FILE_LOGGER_HPP_INCLUDED
3#define _LOGIT_FILE_LOGGER_HPP_INCLUDED
4
7
8#include "ILogger.hpp"
9#include <iostream>
10#include <fstream>
11#include <mutex>
12#include <atomic>
13#include <regex>
14#include <queue>
15#include <functional>
16#include <time_shield/time_parser.hpp>
17
18namespace logit {
19
20#if defined(__EMSCRIPTEN__)
21
22 class FileLogger : public ILogger {
23 public:
24 struct Config {
25 std::string directory = "logs";
26 bool async = false;
27 int auto_delete_days = 30;
28 };
29
30 FileLogger() { warn(); }
31 FileLogger(const Config&) { warn(); }
32 FileLogger(const std::string&, const bool& = true, const int& = 30) { warn(); }
33
34 void log(const LogRecord&, const std::string&) override { warn(); }
35 std::string get_string_param(const LoggerParam&) const override { return {}; }
36 int64_t get_int_param(const LoggerParam&) const override { return 0; }
37 double get_float_param(const LoggerParam&) const override { return 0.0; }
38 void set_log_level(LogLevel) override {}
39 LogLevel get_log_level() const override { return LogLevel::LOG_LVL_TRACE; }
40 void wait() override {}
41
42 private:
43 void warn() const { std::cerr << "FileLogger is not supported under Emscripten" << std::endl; }
44 std::atomic<int> m_log_level = ATOMIC_VAR_INIT(static_cast<int>(LogLevel::LOG_LVL_TRACE));
45 };
46
47#else
48
60 class FileLogger : public ILogger {
61 public:
62
65 struct Config {
66 std::string directory = "logs";
67 bool async = true;
69 };
70
74 }
75
78 FileLogger(const Config& config) : m_config(config) {
80 }
81
87 const std::string& directory,
88 const bool& async = true,
89 const int& auto_delete_days = 30) {
90 m_config.directory = directory;
91 m_config.async = async;
92 m_config.auto_delete_days = auto_delete_days;
94 }
95
97 virtual ~FileLogger() {
99 }
100
108 void log(const LogRecord& record, const std::string& message) override {
110 if (!m_config.async) {
111 std::lock_guard<std::mutex> lock(m_mutex);
112 try {
113 write_log(message, record.timestamp_ms);
114 } catch (const std::exception& e) {
115 std::cerr << "Log error: " << e.what() << std::endl;
116 }
117 return;
118 }
119 auto timestamp_ms = record.timestamp_ms;
120 TaskExecutor::get_instance().add_task([this, message, timestamp_ms]() {
121 std::lock_guard<std::mutex> lock(m_mutex);
122 try {
123 write_log(message, timestamp_ms);
124 } catch (const std::exception& e) {
125 std::cerr << "Log async log error: " << e.what() << std::endl;
126 }
127 });
128 }
129
133 std::string get_string_param(const LoggerParam& param) const override {
134 switch (param) {
137 case LoggerParam::LastLogTimestamp: return std::to_string(get_last_log_ts());
138 case LoggerParam::TimeSinceLastLog: return std::to_string(get_time_since_last_log());
139 default:
140 break;
141 };
142 return std::string();
143 }
144
148 int64_t get_int_param(const LoggerParam& param) const override {
149 switch (param) {
152 default:
153 break;
154 };
155 return 0;
156 }
157
161 double get_float_param(const LoggerParam& param) const override {
162 switch (param) {
163 case LoggerParam::LastLogTimestamp: return (double)get_last_log_ts() / 1000.0;
164 case LoggerParam::TimeSinceLastLog: return (double)get_time_since_last_log() / 1000.0;
165 default:
166 break;
167 };
168 return 0.0;
169 }
170
172 void set_log_level(LogLevel level) override {
173 m_log_level = static_cast<int>(level);
174 }
175
177 LogLevel get_log_level() const override {
178 return static_cast<LogLevel>(m_log_level.load());
179 }
180
182 void wait() override {
183 if (!m_config.async) return;
185 }
186
187 private:
188 mutable std::mutex m_mutex;
190 std::ofstream m_file;
191 mutable std::mutex m_file_path_mutex;
192 std::string m_file_path;
193 std::string m_file_name;
194 int64_t m_current_date_ts = 0;
195 std::atomic<int64_t> m_last_log_ts = ATOMIC_VAR_INIT(0);
196 std::atomic<int> m_log_level = ATOMIC_VAR_INIT(static_cast<int>(LogLevel::LOG_LVL_TRACE));
197
200 // I/O streams (e.g., std::cin, std::cout, std::cerr) may be closed before the program exits.
201 // In this case, calls to functions that use I/O streams (for example, the std::regex constructor)
202 // can lead to undesirable behavior such as hangs or segmentation faults.
203 is_valid_log_filename("2024-01-01.log");
204 std::lock_guard<std::mutex> lock(m_mutex);
205 try {
209 } catch (const std::exception& e) {
210 std::cerr << "Initialization error: " << e.what() << std::endl;
211 }
212 }
213
216 wait();
217 std::lock_guard<std::mutex> lock(m_mutex);
218 if (m_file.is_open()) {
219 m_file.close();
220 }
221 }
222
227
230 std::string get_directory_path() const {
231# if defined(_WIN32)
232 return get_exec_dir() + "\\" + m_config.directory;
233# else
234 return get_exec_dir() + "/" + m_config.directory;
235# endif
236 }
237
240 void open_log_file(const int64_t& date_ts) {
241 if (m_file.is_open()) {
242 m_file.close();
243 }
244 m_current_date_ts = date_ts;
245 std::unique_lock<std::mutex> lock(m_file_path_mutex);
246 m_file_path = create_file_path(date_ts);
248 lock.unlock();
249# if defined(_WIN32)
250 m_file.open(utf8_to_ansi(m_file_path), std::ios_base::app);
251# else
252 m_file.open(m_file_path, std::ios_base::app);
253# endif
254 if (!m_file.is_open()) {
255 throw std::runtime_error("Failed to open log file: " + m_file_path);
256 }
257 }
258
262 std::string create_file_path(int64_t date_ts) const {
263 std::string date_str = time_shield::to_iso8601_date(date_ts);
264 return get_directory_path() + "/" + date_str + ".log";
265 }
266
270 void write_log(const std::string& message, const int64_t& timestamp_ms) {
271 const int64_t message_date_ts = time_shield::start_of_day(time_shield::ms_to_sec(timestamp_ms));
272 if (message_date_ts != m_current_date_ts) {
273 open_log_file(message_date_ts);
274 }
275 if (m_file.is_open()) {
276 m_file << message << std::endl;
277 }
279 }
280
283 const int64_t threshold_ts = m_current_date_ts - (time_shield::SEC_PER_DAY * m_config.auto_delete_days);
284# if __cplusplus >= 201703L
285# ifdef _WIN32
286 fs::path dir_path = fs::u8path(get_directory_path());
287# else
288 fs::path dir_path(get_directory_path());
289# endif
290
291 if (!fs::exists(dir_path) ||
292 !fs::is_directory(dir_path)) {
293 return;
294 }
295
296 for (const auto& entry : fs::directory_iterator(dir_path)) {
297 if (!fs::is_regular_file(entry.status())) continue;
298 std::string filename = entry.path().filename().string();
299 if (is_valid_log_filename(filename)) {
300 const int64_t file_date_ts = get_date_ts_from_filename(filename);
301 if (file_date_ts < threshold_ts) {
302 fs::remove(entry.path());
303 }
304 }
305 }
306# else
307 std::vector<std::string> file_list = get_list_files(get_directory_path());
308 for (const auto& file_path : file_list) {
309 // Extract the file name
310 std::string filename = file_path.substr(file_path.find_last_of("/\\") + 1);
311 if (is_valid_log_filename(filename)) {
312 const int64_t file_date_ts = get_date_ts_from_filename(filename);
313 if (file_date_ts < threshold_ts) {
314# if defined(_WIN32)
315 remove(utf8_to_ansi(file_path).c_str());
316# else
317 remove(file_path.c_str());
318# endif
319 }
320 }
321 }
322# endif
323 }
324
328 bool is_valid_log_filename(const std::string& filename) const {
329 static const std::regex pattern(R"((\d{4}-\d{2}-\d{2})\.log)");
330 return std::regex_match(filename, pattern);
331 }
332
336 int64_t get_date_ts_from_filename(const std::string& filename) const {
337 constexpr size_t EXTENSION_LENGTH = sizeof(".log") - 1;
338 return time_shield::ts(filename.substr(0, filename.size() - EXTENSION_LENGTH));
339 }
340
343 int64_t get_current_utc_date_ts() const {
344 return time_shield::start_of_day(time_shield::ms_to_sec(current_timestamp_ms()));
345 }
346
349 int64_t current_timestamp_ms() const {
351 }
352
355 std::string get_last_log_file_path() const {
356 std::lock_guard<std::mutex> lock(m_file_path_mutex);
357 return m_file_path;
358 }
359
362 std::string get_last_log_file_name() const {
363 std::lock_guard<std::mutex> lock(m_file_path_mutex);
364 return m_file_name;
365 }
366
369 int64_t get_last_log_ts() const {
370 return m_last_log_ts;
371 }
372
375 int64_t get_time_since_last_log() const {
377 }
378 }; // FileLogger
379#endif // defined(__EMSCRIPTEN__)
380
381}; // namespace logit
382
383#endif // _LOGIT_FILE_LOGGER_HPP_INCLUDED
Defines the interface for loggers used in the logging system.
#define LOGIT_CURRENT_TIMESTAMP_MS()
Macro to get the current timestamp in milliseconds.
std::mutex m_mutex
Mutex to protect file operations.
int64_t m_current_date_ts
Timestamp of the current log file's date.
void stop_logging()
Stops the logging process by closing the file and waiting for tasks.
void initialize_directory()
Initializes the logging directory.
std::string get_last_log_file_name() const
Retrieves the last log file name.
void remove_old_logs()
Removes old log files based on the auto-delete days configuration.
int64_t get_date_ts_from_filename(const std::string &filename) const
Extracts the date timestamp from the log filename.
FileLogger(const Config &config)
Constructor with custom configuration.
int64_t current_timestamp_ms() const
Gets the current timestamp in milliseconds.
Config m_config
Configuration for the file logger.
int64_t get_time_since_last_log() const
Retrieves the time since the last log.
std::string get_last_log_file_path() const
Retrieves the last log file path.
FileLogger(const std::string &directory, const bool &async=true, const int &auto_delete_days=30)
Constructor with directory and asynchronous flag.
std::mutex m_file_path_mutex
Mutex to protect file path operations.
void set_log_level(LogLevel level) override
Sets the minimal log level for this logger.
int64_t get_int_param(const LoggerParam &param) const override
Retrieves an integer parameter from the logger.
std::ofstream m_file
Output file stream for logging.
std::atomic< int64_t > m_last_log_ts
Timestamp of the last log.
virtual ~FileLogger()
Destructor to stop logging and close file.
void start_logging()
Starts the logging process by initializing the file and directory.
bool is_valid_log_filename(const std::string &filename) const
Checks if the filename matches the log file naming pattern.
std::string get_string_param(const LoggerParam &param) const override
Retrieves a string parameter from the logger.
int64_t get_last_log_ts() const
Retrieves the timestamp of the last log.
std::string m_file_path
Path of the currently open log file.
void log(const LogRecord &record, const std::string &message) override
Logs a message to a file with thread safety.
FileLogger()
Default constructor that uses default configuration.
int64_t get_current_utc_date_ts() const
Gets the current UTC date timestamp in seconds.
void open_log_file(const int64_t &date_ts)
Opens a new log file based on the provided date timestamp.
std::string get_directory_path() const
Gets the full path to the logging directory.
void write_log(const std::string &message, const int64_t &timestamp_ms)
Writes a log message to the file.
std::string create_file_path(int64_t date_ts) const
Creates a file path for the log file based on the date timestamp.
LogLevel get_log_level() const override
Gets the minimal log level for this logger.
std::atomic< int > m_log_level
void wait() override
Waits for all asynchronous tasks to complete.
std::string m_file_name
Name of the currently open log file.
double get_float_param(const LoggerParam &param) const override
Retrieves a floating-point parameter from the logger.
Interface for loggers that handle log message output.
Definition ILogger.hpp:15
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.
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.
LogLevel
Logging levels.
Definition enums.hpp:15
@ LOG_LVL_TRACE
Trace level logging.
Definition enums.hpp:16
void create_directories(const std::string &path)
Creates directories recursively for the given path.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
Definition enums.hpp:47
@ TimeSinceLastLog
The time elapsed since the last log in seconds.
Definition enums.hpp:51
@ LastLogTimestamp
The timestamp of the last log.
Definition enums.hpp:50
@ LastFileName
The name of the last file written to.
Definition enums.hpp:48
@ LastFilePath
The full path of the last file written to.
Definition enums.hpp:49
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.
Configuration for the file logger.
int auto_delete_days
Number of days after which old log files are deleted.
bool async
Flag indicating whether logging should be asynchronous.
std::string directory
Directory where log files are stored.
Stores log metadata and content.
Definition LogRecord.hpp:17
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:19