42std::string CurrentTimestamp() {
43 const auto now = std::chrono::system_clock::now();
44 const auto time = std::chrono::system_clock::to_time_t(now);
45 const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
49 localtime_s(&tm_buf, &time);
51 localtime_r(&time, &tm_buf);
54 std::ostringstream oss;
55 oss << std::put_time(&tm_buf,
"%Y-%m-%d %H:%M:%S");
56 oss <<
'.' << std::setfill(
'0') << std::setw(3) << ms.count();
60std::string_view LevelToString(
LogLevel level) {
76const char* LevelToColor(
LogLevel level) {
94Logger::Logger() : m_impl(std::make_unique<Impl>()) {}
106 const std::lock_guard<std::mutex> lock(m_impl->mutex);
108 m_minLevel.store(minLevel, std::memory_order_release);
109 m_impl->logFilePath = logFilePath;
111 if (!logFilePath.empty()) {
112 const auto parentPath = logFilePath.parent_path();
113 if (!parentPath.empty() && !std::filesystem::exists(parentPath)) {
115 std::filesystem::create_directories(parentPath, ec);
117 std::cerr <<
"Warning: Could not create log directory: " << ec.message() <<
'\n';
121 m_impl->logFile.open(logFilePath, std::ios::out | std::ios::app);
122 if (m_impl->logFile.is_open()) {
123 m_impl->fileOutput =
true;
124 m_impl->logFile <<
"\n========== Log Session Started: " << CurrentTimestamp() <<
" ==========\n";
125 m_impl->logFile.flush();
127 std::cerr <<
"Failed to open log file: " << logFilePath <<
'\n';
128 m_impl->fileOutput =
false;
132 if (m_impl->consoleOutput) {
133 std::cout <<
"Logger initialized (Level: " << LevelToString(minLevel) <<
")" <<
'\n';
138 const std::lock_guard<std::mutex> lock(m_impl->mutex);
140 if (m_impl->logFile.is_open()) {
141 m_impl->logFile <<
"========== Log Session Ended: " << CurrentTimestamp() <<
" ==========\n\n";
142 m_impl->logFile.close();
147 const std::lock_guard<std::mutex> lock(m_impl->mutex);
148 m_impl->consoleOutput = enable;
152 const std::lock_guard<std::mutex> lock(m_impl->mutex);
153 m_impl->fileOutput = enable && m_impl->logFile.is_open();
157 const std::lock_guard<std::mutex> lock(m_impl->mutex);
158 if (m_impl->logFile.is_open()) {
159 m_impl->logFile.flush();
166 const std::lock_guard<std::mutex> lock(m_impl->mutex);
167 return m_impl->logFilePath;
170void Logger::LogVFormat(
LogLevel level, std::string_view fmt, std::format_args args) {
173 message = std::vformat(fmt, args);
174 }
catch (
const std::format_error& e) {
177 message = std::string(fmt) +
" [format_error: " + e.what() +
"]";
179 LogPlain(level, message);
182void Logger::LogPlain(
LogLevel level, std::string_view message) {
185 const std::string timestamp = CurrentTimestamp();
186 const std::string_view levelStr = LevelToString(level);
188 std::string formatted;
189 formatted.reserve(timestamp.size() + levelStr.size() + message.size() + 8);
190 formatted.append(
"[").append(timestamp).append(
"] [").append(levelStr).append(
"] ").append(message);
192 bool consoleOutput =
false;
193 bool fileOutput =
false;
195 const std::lock_guard<std::mutex> lock(m_impl->mutex);
196 consoleOutput = m_impl->consoleOutput;
197 fileOutput = m_impl->fileOutput && m_impl->logFile.is_open();
200 m_impl->logFile << formatted <<
'\n';
202 m_impl->logFile.flush();
209 const char* color = LevelToColor(level);
210 const char* reset =
"\033[0m";
211 std::ostream& out = (level >=
LogLevel::Error) ? std::cerr : std::cout;
212 out << color << formatted << reset <<
'\n';