| 
									
										
										
										
											2022-02-14 22:12:20 -05:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Furnace Tracker - multi-system chiptune tracker | 
					
						
							| 
									
										
										
										
											2023-01-19 19:18:40 -05:00
										 |  |  |  * Copyright (C) 2021-2023 tildearrow and contributors | 
					
						
							| 
									
										
										
										
											2022-02-14 22:12:20 -05:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  * with this program; if not, write to the Free Software Foundation, Inc., | 
					
						
							|  |  |  |  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 16:08:08 -04:00
										 |  |  | #include "ta-log.h"
 | 
					
						
							| 
									
										
										
										
											2023-04-05 20:16:00 -04:00
										 |  |  | #include "fileutils.h"
 | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | #include <thread>
 | 
					
						
							|  |  |  | #include <condition_variable>
 | 
					
						
							| 
									
										
										
										
											2021-05-11 16:08:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 19:10:30 -04:00
										 |  |  | #ifdef _WIN32
 | 
					
						
							|  |  |  | #include <windows.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 20:15:19 -04:00
										 |  |  | #ifdef IS_MOBILE
 | 
					
						
							|  |  |  | int logLevel=LOGLEVEL_TRACE; | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2023-04-04 17:01:45 -04:00
										 |  |  | int logLevel=LOGLEVEL_TRACE; // until done
 | 
					
						
							| 
									
										
										
										
											2022-09-08 20:15:19 -04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-06-09 13:28:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | FILE* logFile; | 
					
						
							|  |  |  | char* logFileBuf; | 
					
						
							| 
									
										
										
										
											2023-01-12 23:55:58 -05:00
										 |  |  | char* logFileWriteBuf; | 
					
						
							| 
									
										
										
										
											2022-12-22 23:41:49 -05:00
										 |  |  | unsigned int logFilePosI; | 
					
						
							|  |  |  | unsigned int logFilePosO; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | std::thread* logFileThread; | 
					
						
							|  |  |  | std::mutex logFileLock; | 
					
						
							|  |  |  | std::mutex logFileLockI; | 
					
						
							|  |  |  | std::condition_variable logFileNotify; | 
					
						
							| 
									
										
										
										
											2023-01-12 23:55:58 -05:00
										 |  |  | std::atomic<bool> logFileAvail(false); | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  | std::atomic<unsigned short> logPosition; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LogEntry logEntries[TA_LOG_SIZE]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static constexpr unsigned int TA_LOG_MASK=TA_LOG_SIZE-1; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | static constexpr unsigned int TA_LOGFILE_BUF_MASK=TA_LOGFILE_BUF_SIZE-1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char* logTypes[5]={ | 
					
						
							|  |  |  |   "ERROR", | 
					
						
							|  |  |  |   "warning", | 
					
						
							|  |  |  |   "info", | 
					
						
							|  |  |  |   "debug", | 
					
						
							|  |  |  |   "trace" | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-22 20:17:02 -05:00
										 |  |  | void appendLogBuf(const LogEntry& entry) { | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |   logFileLockI.lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-22 20:17:02 -05:00
										 |  |  |   std::string toWrite=fmt::sprintf( | 
					
						
							|  |  |  |     "%02d:%02d:%02d [%s] %s\n", | 
					
						
							|  |  |  |     entry.time.tm_hour, | 
					
						
							|  |  |  |     entry.time.tm_min, | 
					
						
							|  |  |  |     entry.time.tm_sec, | 
					
						
							|  |  |  |     logTypes[entry.loglevel], | 
					
						
							|  |  |  |     entry.text | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const char* msg=toWrite.c_str(); | 
					
						
							|  |  |  |   size_t len=toWrite.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-22 23:41:49 -05:00
										 |  |  |   int remaining=(logFilePosO-logFilePosI-1)&TA_LOGFILE_BUF_SIZE; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (len>=(unsigned int)remaining) { | 
					
						
							|  |  |  |     printf("line too long to fit in log buffer!\n"); | 
					
						
							|  |  |  |     logFileLockI.unlock(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((logFilePosI+len)>=TA_LOGFILE_BUF_SIZE) { | 
					
						
							|  |  |  |     size_t firstWrite=TA_LOGFILE_BUF_SIZE-logFilePosI; | 
					
						
							|  |  |  |     memcpy(logFileBuf+logFilePosI,msg,firstWrite); | 
					
						
							|  |  |  |     memcpy(logFileBuf,msg+firstWrite,len-firstWrite); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     memcpy(logFileBuf+logFilePosI,msg,len); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   logFilePosI=(logFilePosI+len)&TA_LOGFILE_BUF_MASK; | 
					
						
							|  |  |  |   logFileLockI.unlock(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-11 14:08:12 -04:00
										 |  |  | int writeLog(int level, const char* msg, fmt::printf_args args) { | 
					
						
							| 
									
										
										
										
											2022-04-11 01:12:24 -04:00
										 |  |  |   time_t thisMakesNoSense=time(NULL); | 
					
						
							| 
									
										
										
										
											2022-12-23 05:14:42 -05:00
										 |  |  |   int pos=(logPosition.fetch_add(1))&TA_LOG_MASK; | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-23 01:15:46 -05:00
										 |  |  |   logEntries[pos].text.assign(fmt::vsprintf(msg,args)); | 
					
						
							| 
									
										
										
										
											2022-04-11 01:12:24 -04:00
										 |  |  |   // why do I have to pass a pointer
 | 
					
						
							|  |  |  |   // can't I just pass the time_t directly?!
 | 
					
						
							| 
									
										
										
										
											2022-04-11 03:01:23 -04:00
										 |  |  | #ifdef _WIN32
 | 
					
						
							|  |  |  |   struct tm* tempTM=localtime(&thisMakesNoSense); | 
					
						
							|  |  |  |   if (tempTM==NULL) { | 
					
						
							|  |  |  |     memset(&logEntries[pos].time,0,sizeof(struct tm)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     memcpy(&logEntries[pos].time,tempTM,sizeof(struct tm)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2022-04-11 01:12:24 -04:00
										 |  |  |   if (localtime_r(&thisMakesNoSense,&logEntries[pos].time)==NULL) { | 
					
						
							|  |  |  |     memset(&logEntries[pos].time,0,sizeof(struct tm)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-11 03:01:23 -04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  |   logEntries[pos].loglevel=level; | 
					
						
							|  |  |  |   logEntries[pos].ready=true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |   // write to log file
 | 
					
						
							|  |  |  |   if (logFileAvail) { | 
					
						
							| 
									
										
										
										
											2022-12-22 20:17:02 -05:00
										 |  |  |     appendLogBuf(logEntries[pos]); | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |     logFileNotify.notify_one(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  |   if (logLevel<level) return 0; | 
					
						
							|  |  |  |   switch (level) { | 
					
						
							|  |  |  |     case LOGLEVEL_ERROR: | 
					
						
							|  |  |  |       return fmt::printf("\x1b[1;31m[ERROR]\x1b[m %s\n",logEntries[pos].text); | 
					
						
							|  |  |  |     case LOGLEVEL_WARN: | 
					
						
							|  |  |  |       return fmt::printf("\x1b[1;33m[warning]\x1b[m %s\n",logEntries[pos].text); | 
					
						
							|  |  |  |     case LOGLEVEL_INFO: | 
					
						
							|  |  |  |       return fmt::printf("\x1b[1;32m[info]\x1b[m %s\n",logEntries[pos].text); | 
					
						
							|  |  |  |     case LOGLEVEL_DEBUG: | 
					
						
							|  |  |  |       return fmt::printf("\x1b[1;34m[debug]\x1b[m %s\n",logEntries[pos].text); | 
					
						
							|  |  |  |     case LOGLEVEL_TRACE: | 
					
						
							|  |  |  |       return fmt::printf("\x1b[1;37m[trace]\x1b[m %s\n",logEntries[pos].text); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void initLog() { | 
					
						
							| 
									
										
										
										
											2023-04-06 18:13:51 -04:00
										 |  |  |   // initialize coloring on Windows
 | 
					
						
							|  |  |  | #ifdef _WIN32
 | 
					
						
							|  |  |  |   HANDLE winout=GetStdHandle(STD_OUTPUT_HANDLE); | 
					
						
							|  |  |  |   int termprop=0; | 
					
						
							|  |  |  |   GetConsoleMode(winout,(LPDWORD)&termprop); | 
					
						
							|  |  |  |   termprop|=ENABLE_VIRTUAL_TERMINAL_PROCESSING; | 
					
						
							|  |  |  |   SetConsoleMode(winout,termprop); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // initialize log buffer
 | 
					
						
							| 
									
										
										
										
											2022-04-10 23:12:02 -04:00
										 |  |  |   logPosition=0; | 
					
						
							|  |  |  |   for (int i=0; i<TA_LOG_SIZE; i++) { | 
					
						
							|  |  |  |     logEntries[i].text.reserve(128); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // initialize log to file thread
 | 
					
						
							|  |  |  |   logFileAvail=false; | 
					
						
							| 
									
										
										
										
											2022-04-11 01:12:24 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | void _logFileThread() { | 
					
						
							|  |  |  |   std::unique_lock<std::mutex> lock(logFileLock); | 
					
						
							|  |  |  |   while (true) { | 
					
						
							|  |  |  |     unsigned int logFilePosICopy=logFilePosI; | 
					
						
							|  |  |  |     if (logFilePosICopy!=logFilePosO) { | 
					
						
							|  |  |  |       // write
 | 
					
						
							|  |  |  |       if (logFilePosO>logFilePosICopy) { | 
					
						
							|  |  |  |         fwrite(logFileBuf+logFilePosO,1,TA_LOGFILE_BUF_SIZE-logFilePosO,logFile); | 
					
						
							|  |  |  |         logFilePosO=0; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         fwrite(logFileBuf+logFilePosO,1,logFilePosICopy-logFilePosO,logFile); | 
					
						
							| 
									
										
										
										
											2022-12-22 20:17:02 -05:00
										 |  |  |         logFilePosO=logFilePosICopy&TA_LOGFILE_BUF_MASK; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // wait
 | 
					
						
							|  |  |  |       fflush(logFile); | 
					
						
							| 
									
										
										
										
											2023-01-12 23:55:58 -05:00
										 |  |  |       if (!logFileAvail) break; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |       logFileNotify.wait(lock); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool startLogFile(const char* path) { | 
					
						
							|  |  |  |   if (logFileAvail) return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // rotate log file if possible
 | 
					
						
							| 
									
										
										
										
											2023-04-05 20:16:00 -04:00
										 |  |  |   char oldPath[4096]; | 
					
						
							|  |  |  |   char newPath[4096]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (fileExists(path)==1) { | 
					
						
							|  |  |  |     for (int i=4; i>=0; i--) { | 
					
						
							|  |  |  |       if (i>0) { | 
					
						
							|  |  |  |         snprintf(oldPath,4095,"%s.%d",path,i); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         strncpy(oldPath,path,4095); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       snprintf(newPath,4095,"%s.%d",path,i+1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (i>=4) { | 
					
						
							|  |  |  |         deleteFile(oldPath); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         moveFiles(oldPath,newPath); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |    | 
					
						
							|  |  |  |   // open log file
 | 
					
						
							| 
									
										
										
										
											2023-04-05 20:16:00 -04:00
										 |  |  |   if ((logFile=ps_fopen(path,"w+"))==NULL) { | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |     logFileAvail=false; | 
					
						
							|  |  |  |     logW("could not open log file! (%s)",strerror(errno)); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   logFileBuf=new char[TA_LOGFILE_BUF_SIZE]; | 
					
						
							| 
									
										
										
										
											2023-01-12 23:55:58 -05:00
										 |  |  |   logFileWriteBuf=new char[TA_LOGFILE_BUF_SIZE]; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:55:21 -05:00
										 |  |  |   logFilePosI=0; | 
					
						
							|  |  |  |   logFilePosO=0; | 
					
						
							|  |  |  |   logFileAvail=true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   logFileThread=new std::thread(_logFileThread); | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool finishLogFile() { | 
					
						
							|  |  |  |   if (!logFileAvail) return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   logFileAvail=false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // flush
 | 
					
						
							|  |  |  |   logFileLockI.lock(); | 
					
						
							|  |  |  |   logFileNotify.notify_one(); | 
					
						
							|  |  |  |   logFileThread->join(); | 
					
						
							|  |  |  |   logFileLockI.unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   fclose(logFile); | 
					
						
							|  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2022-12-22 15:47:05 -05:00
										 |  |  | } |