| 37 | |
| 38 | |
| 39 | static void WriteToLogFile(const char* message) { |
| 40 | InitializeFileLogging(); |
| 41 | |
| 42 | if (g_logFile == INVALID_HANDLE_VALUE) { |
| 43 | // Fallback to debug output if file can't be opened |
| 44 | OutputDebugStringA(message); |
| 45 | return; |
| 46 | } |
| 47 | |
| 48 | std::lock_guard<std::mutex> lock(g_logMutex); |
| 49 | |
| 50 | // Get current timestamp |
| 51 | using namespace std::chrono; |
| 52 | auto now = system_clock::now(); |
| 53 | auto in_time_t = system_clock::to_time_t(now); |
| 54 | auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000; |
| 55 | |
| 56 | std::tm local_tm; |
| 57 | if (localtime_s(&local_tm, &in_time_t) == 0) { |
| 58 | std::ostringstream oss; |
| 59 | oss << std::put_time(&local_tm, "%Y-%m-%d %H:%M:%S"); |
| 60 | oss << '.' << std::setfill('0') << std::setw(3) << ms.count(); |
| 61 | oss << " - " << message << "\r\n"; |
| 62 | |
| 63 | std::string timestampedMessage = oss.str(); |
| 64 | DWORD bytesWritten; |
| 65 | if (!WriteFile(g_logFile, timestampedMessage.c_str(), (DWORD)timestampedMessage.length(), &bytesWritten, NULL)) { |
| 66 | // If file write fails, fallback to debug output |
| 67 | OutputDebugStringA(message); |
| 68 | } |
| 69 | else { |
| 70 | FlushFileBuffers(g_logFile); |
| 71 | } |
| 72 | } |
| 73 | else { |
| 74 | // If timestamp formatting fails, still try to write the message |
| 75 | std::string simpleMessage = std::string(message) + "\r\n"; |
| 76 | DWORD bytesWritten; |
| 77 | if (!WriteFile(g_logFile, simpleMessage.c_str(), (DWORD)simpleMessage.length(), &bytesWritten, NULL)) { |
| 78 | OutputDebugStringA(message); |
| 79 | } |
| 80 | else { |
| 81 | FlushFileBuffers(g_logFile); |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | |
| 87 | void LOG_A(int verbosity, const char* format, ...) |