├── Logger.cpp ├── Logger.hpp └── README.md /Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.hpp" 2 | 3 | #include // start, createDir 4 | #include // dateTimeString 5 | #ifdef _WIN32 6 | #include // createDir 7 | #endif // _WIN32 8 | 9 | #ifndef S_ISDIR 10 | #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 11 | #endif // S_ISDIR 12 | 13 | Logger::LogLevel Logger::PRINT = { Color::White, Color::Black, true, true, "\n", "", 0 }; 14 | Logger::LogLevel Logger::INFO = { Color::BrightWhite, Color::Black, true, true, "\n", "| [INFO] ", 1 }; 15 | Logger::LogLevel Logger::WARN = { Color::LightYellow, Color::Black, true, true, "\n", "| [WARN] ", 2 }; 16 | Logger::LogLevel Logger::ERR = { Color::Red, Color::Black, true, true, "\n", "| [ERROR] ", 3 }; 17 | Logger::LogLevel Logger::FATAL = { Color::LightRed, Color::Black, true, true, "\n", "| [FATAL] ", 4 }; 18 | Logger::LogLevel Logger::DEBUG = { Color::LightGreen, Color::Black, false, true, "\n", "| [DEBUG] ", 5 }; 19 | 20 | Logger::Logger() { 21 | start(); 22 | } 23 | 24 | Logger::Logger(const std::string &logName) { 25 | LOG_NAME = logName; 26 | start(); 27 | } 28 | 29 | bool Logger::createDir(const char *dir) { 30 | struct stat info; 31 | 32 | if (stat(dir, &info) != 0 || !S_ISDIR(info.st_mode)) { 33 | #ifdef _WIN32 34 | return _mkdir(dir) > -1; 35 | #else 36 | return mkdir(dir, 0733) > -1; 37 | #endif // _WIN32 38 | } 39 | return false; 40 | } 41 | 42 | std::string Logger::dateTimeString(bool returnTimeOnly) { 43 | char buf[80]; 44 | time_t now = time(0); 45 | #pragma warning(push) 46 | #pragma warning(disable: 4996) 47 | struct tm tstruct = *localtime(&now); 48 | #pragma warning(pop) 49 | 50 | if (returnTimeOnly) 51 | strftime(buf, sizeof(buf), "%H;%M;%S %p", &tstruct); 52 | else 53 | strftime(buf, sizeof(buf), "%Y-%m-%d %H;%M;%S %p", &tstruct); 54 | return buf; 55 | } 56 | 57 | void Logger::resetColors() noexcept { 58 | #ifdef _WIN32 59 | setConsoleInfo(); 60 | SetConsoleTextAttribute(hOut, defaultColorAttribs); 61 | #else 62 | std::cout << "\e[0m\n"; 63 | lastFgColor = "39"; 64 | lastConsoleColor = "49"; 65 | #endif // _WIN32 66 | } 67 | 68 | void Logger::clearConsole() noexcept { 69 | #ifdef _WIN32 70 | setConsoleInfo(); 71 | GetConsoleScreenBufferInfo(hOut, &csbi); 72 | DWORD cellCount = csbi.dwSize.X * csbi.dwSize.Y; 73 | DWORD count; 74 | COORD home = { 0, 0 }; 75 | 76 | FillConsoleOutputCharacter(hOut, ' ', cellCount, home, &count); 77 | FillConsoleOutputAttribute(hOut, csbi.wAttributes, cellCount, home, &count); 78 | SetConsoleCursorPosition(hOut, home); 79 | #else 80 | if (lastConsoleColor != "49") 81 | std::cout << "\e[0;" << lastFgColor << ";" << lastConsoleColor << "m"; 82 | std::cout << "\e[2J\e[1;1H\n"; 83 | #endif // _WIN32 84 | } 85 | 86 | void Logger::setTextColor(const Color &foreground, const Color &background) noexcept { 87 | if (background < 0 || foreground < 0 || background > 15 || foreground > 15) { 88 | std::cerr << "Colors only range from 0 to 15.\n"; 89 | return; 90 | } 91 | #ifdef _WIN32 92 | setConsoleInfo(); 93 | SetConsoleTextAttribute(hOut, (WORD)(foreground + (background * 16))); 94 | #else 95 | lastFgColor = fc[foreground]; 96 | std::cout << "\e[0;" << lastFgColor << ";" << bc[background] << "m"; 97 | #endif // _WIN32 98 | } 99 | 100 | void Logger::setConsoleColor(const Color &color) noexcept { 101 | if (color < 0 || color > 15) { 102 | std::cerr << "Colors only range from 0 to 15.\n"; 103 | return; 104 | } 105 | #ifdef _WIN32 106 | setConsoleInfo(); 107 | SetConsoleTextAttribute(hOut, (WORD)(color << 4)); 108 | #else 109 | lastConsoleColor = bc[color]; 110 | std::cout << "\e[0;" << lastFgColor << ";" << lastConsoleColor << "m"; 111 | #endif // _WIN32 112 | clearConsole(); 113 | } 114 | 115 | void Logger::setSeverity(int level) noexcept { 116 | maxSeverity = level; 117 | } 118 | 119 | void Logger::setFileSeverity(int level) noexcept { 120 | maxFileSeverity = level; 121 | } 122 | 123 | int &Logger::getSeverity() noexcept { 124 | return maxSeverity; 125 | } 126 | 127 | int &Logger::getFileSeverity() noexcept { 128 | return maxFileSeverity; 129 | } 130 | 131 | void Logger::start() { 132 | if (file.is_open()) return; 133 | 134 | try { 135 | struct stat info; 136 | const std::string timeStr = dateTimeString(); 137 | const std::string fileName = LOG_FLDR + "/" + LOG_NAME + ".log"; 138 | const std::string backupFileName = LOG_BACKUP_FLDR + "/" + LOG_NAME + "-" + timeStr + ".log"; 139 | 140 | createDir(LOG_FLDR.c_str()); 141 | 142 | if (stat(fileName.c_str(), &info) == 0) { 143 | // Backup previous log 144 | createDir(LOG_BACKUP_FLDR.c_str()); 145 | std::rename(fileName.c_str(), backupFileName.c_str()); 146 | } 147 | file.open(fileName, std::ofstream::out); 148 | if (file.fail()) throw; 149 | 150 | file << "=== Started " + timeStr + " ===\n"; 151 | } catch (...) { 152 | file.close(); 153 | std::cerr << "Error starting logger\n"; 154 | } 155 | } 156 | 157 | void Logger::end() { 158 | if (!file.is_open()) return; 159 | file << "=== Shutdown " + dateTimeString() + " ===\n"; 160 | file.close(); 161 | } 162 | 163 | Logger::~Logger() { 164 | end(); 165 | } 166 | 167 | #ifdef _WIN32 168 | const HANDLE Logger::hOut = GetStdHandle(STD_OUTPUT_HANDLE); 169 | WORD Logger::defaultColorAttribs; 170 | CONSOLE_SCREEN_BUFFER_INFO Logger::csbi; 171 | 172 | void Logger::setConsoleInfo() noexcept { 173 | GetConsoleScreenBufferInfo(hOut, &csbi); 174 | if (!csbi.dwMaximumWindowSize.X && !csbi.dwMaximumWindowSize.Y) 175 | defaultColorAttribs = csbi.wAttributes; 176 | } 177 | #else 178 | const char *Logger::lastFgColor = "39"; 179 | const char *Logger::lastConsoleColor = "49"; 180 | const char *Logger::fc[16] = { "30","34","32","36","31","35","33","37", "90", "94", "92", "96", "91", "95", "93", "97" }; 181 | const char *Logger::bc[16] = { "40","44","42","46","41","45","43","47","100","104","102","106","101","105","103","107" }; 182 | #endif 183 | 184 | std::string Logger::LOG_NAME = "MainLog"; 185 | std::string Logger::LOG_FLDR = "./logs"; 186 | std::string Logger::LOG_BACKUP_FLDR = "./logs/LogBackups"; 187 | 188 | std::ofstream Logger::file; 189 | int Logger::maxSeverity = 5; 190 | int Logger::maxFileSeverity = 5; 191 | -------------------------------------------------------------------------------- /Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // writeTo, setTextColor, setConsoleColor, start 4 | #include // Folder and file names 5 | #include // Log recording 6 | #ifdef _WIN32 7 | #include // Colors for Windows 8 | #endif // _WIN32 9 | 10 | class Logger { 11 | public: 12 | ///////////////////////////////////////// 13 | /// \enum Logger::Color 14 | /// \brief Standard console colors 15 | //////////////////////////////////////// 16 | enum Color { 17 | Black, Blue, Green, Aqua, Red, Purple, Yellow, White, Gray, LightBlue, 18 | LightGreen, LightAqua, LightRed, LightPurple, LightYellow, BrightWhite 19 | }; 20 | 21 | ///////////////////////////////////////// 22 | /// \struct Logger::LogLevel 23 | /// \brief Properties for each log level 24 | //////////////////////////////////////// 25 | static struct LogLevel { 26 | Color fgColor; // Text foreground color 27 | Color bgColor; // Text background color 28 | bool enumerable; // If level is affected by 'max severity' checks before being written 29 | bool writable; // If level is able to be written to log file 30 | const char *suffix; // Suffix of log-level added after string 31 | const char *prefix; // Prefix of log-level added before string 32 | int severity; // Severity of log-level 33 | } PRINT, INFO, WARN, ERR, FATAL, DEBUG; 34 | 35 | //////////////////////////////// 36 | /// \brief Constructs a logger 37 | /// \note Calls Logger::start() 38 | /// \see Logger::start() 39 | /////////////////////////////// 40 | Logger(); 41 | 42 | ////////////////////////////////////////////////////// 43 | /// \brief Constructs a logger and sets it's log name 44 | /// \note Calls Logger::start() 45 | /// \param logName Name of log to be set 46 | /// \see Logger::start(), Logger::LOG_NAME 47 | ///////////////////////////////////////////////////// 48 | Logger(const std::string &logName); 49 | 50 | /////////////////////////////////////////////////////////////// 51 | /// \brief Creates a directory 52 | /// \param dir Name and/or path of the directory to be created 53 | /// \returns True if the directory was created successfully 54 | ////////////////////////////////////////////////////////////// 55 | static bool createDir(const char *dir); 56 | 57 | //////////////////////////////////////////////////////////////////// 58 | /// \brief Creates a string of the current date and/or time 59 | /// \warning Function's use of localtime() is not thread safe 60 | /// \param returnTimeOnly Return a string of the current time only? 61 | /// \returns String containing the current date and/or time 62 | /////////////////////////////////////////////////////////////////// 63 | static std::string dateTimeString(bool returnTimeOnly = false); 64 | 65 | ///////////////////////////////////// 66 | /// \brief Resets all console colors 67 | //////////////////////////////////// 68 | static void resetColors() noexcept; 69 | 70 | ///////////////////////////////// 71 | /// \brief Clears console output 72 | //////////////////////////////// 73 | static void clearConsole() noexcept; 74 | 75 | //////////////////////////////////////////////////////////////// 76 | /// \brief Sets the foreground and background color of text. 77 | /// \note This does not affect log level colors 78 | /// \param foreground Foreground color of text 79 | /// \param background Background color of text (default: black) 80 | /// \see Logger::Color 81 | /////////////////////////////////////////////////////////////// 82 | static void setTextColor(const Color &foreground, const Color &background = Color::Black) noexcept; 83 | 84 | /////////////////////////////////////////////////////////// 85 | /// \brief Sets background color of the entire console. 86 | /// \note This does not affect log level background colors 87 | /// \param color Color of console to be set 88 | /// \see Logger::Color 89 | ////////////////////////////////////////////////////////// 90 | static void setConsoleColor(const Color &color) noexcept; 91 | 92 | ////////////////////////////////////////////////////////////////////////////////////////////// 93 | /// \brief Writes message with log-level properties to file and/or console 94 | /// \note Messages will not be written to log if start() was not called previously 95 | /// \param lvl LogLevel to have its properties added onto message 96 | /// \param msg Message to be written 97 | /// \param toLog Write message to log file? 98 | /// \param toConsole Write message to console? 99 | /// \see Logger::PRINT, Logger::INFO, Logger::WARN, Logger::ERR, Logger::FATAL, Logger::DEBUG; 100 | ////////////////////////////////////////////////////////////////////////////////////////////// 101 | template 102 | static void write(const LogLevel *lvl, const T &msg, bool toLog, bool toConsole) { 103 | std::ios_base::sync_with_stdio(false); 104 | // Write message to file 105 | if (toLog && file.is_open() && lvl->writable && (!lvl->enumerable || lvl->severity < maxFileSeverity)) { 106 | try { 107 | file << "[" + dateTimeString(true) + "] " << lvl->prefix << msg << lvl->suffix; 108 | } catch (...) { 109 | file.close(); 110 | std::cerr << "Error writing to log.\n"; 111 | } 112 | } 113 | // Write message to console 114 | if (toConsole && (!lvl->enumerable || lvl->severity < maxSeverity)) { 115 | // Print colored string 116 | setTextColor(lvl->fgColor, lvl->bgColor); 117 | std::cout << lvl->prefix << msg << lvl->suffix; 118 | // Reset colors 119 | #ifdef _WIN32 120 | SetConsoleTextAttribute(hOut, csbi.wAttributes); 121 | #else 122 | std::cout << "\e[0m\n"; 123 | #endif // _WIN32 124 | } 125 | } 126 | 127 | ///////////////////////////////////////////////////////////////////////// 128 | /// \brief Writes message + PRINT LogLevel properties to log and console 129 | /// \param msg Message to write 130 | /// \see Logger::PRINT 131 | //////////////////////////////////////////////////////////////////////// 132 | template static void print(const T &msg) { write(&PRINT, msg, true, true); } 133 | 134 | //////////////////////////////////////////////////////////////////////// 135 | /// \brief Writes message + INFO LogLevel properties to log and console 136 | /// \param msg Message to write 137 | /// \see Logger::INFO 138 | /////////////////////////////////////////////////////////////////////// 139 | template static void info(const T &msg) { write(&INFO, msg, true, true); } 140 | 141 | //////////////////////////////////////////////////////////////////////// 142 | /// \brief Writes message + WARN LogLevel properties to log and console 143 | /// \param msg Message to write 144 | /// \see Logger::WARN 145 | /////////////////////////////////////////////////////////////////////// 146 | template static void warn(const T &msg) { write(&WARN, msg, true, true); } 147 | 148 | /////////////////////////////////////////////////////////////////////// 149 | /// \brief Writes message + ERR LogLevel properties to log and console 150 | /// \param msg Message to write 151 | /// \see Logger::ERR 152 | ////////////////////////////////////////////////////////////////////// 153 | template static void error(const T &msg) { write(&ERR, msg, true, true); } 154 | 155 | ///////////////////////////////////////////////////////////////////////// 156 | /// \brief Writes message + FATAL LogLevel properties to log and console 157 | /// \param msg Message to write 158 | /// \see Logger::FATAL 159 | //////////////////////////////////////////////////////////////////////// 160 | template static void fatal(const T &msg) { write(&FATAL, msg, true, true); } 161 | 162 | ///////////////////////////////////////////////////////////////////////// 163 | /// \brief Writes message + DEBUG LogLevel properties to log and console 164 | /// \param msg Message to write 165 | /// \see Logger::DEBUG 166 | //////////////////////////////////////////////////////////////////////// 167 | template static void debug(const T &msg) { write(&DEBUG, msg, true, true); } 168 | 169 | ///////////////////////////////////////////////////////////// 170 | /// \brief Writes message + PRINT LogLevel properties to log 171 | /// \param msg Message to write 172 | /// \see Logger::PRINT 173 | //////////////////////////////////////////////////////////// 174 | template static void logMessage(const T &msg) { write(&PRINT, msg, true, false); } 175 | 176 | /////////////////////////////////////////////////////////// 177 | /// \brief Writes message + ERR LogLevel properties to log 178 | /// \param msg Message to write 179 | /// \see Logger::ERR 180 | ////////////////////////////////////////////////////////// 181 | template static void logError(const T &msg) { write(&ERR, msg, true, false); } 182 | 183 | ///////////////////////////////////////////////////////////// 184 | /// \brief Writes message + DEBUG LogLevel properties to log 185 | /// \param msg Message to write 186 | /// \see Logger::DEBUG 187 | //////////////////////////////////////////////////////////// 188 | template static void logDebug(const T &msg) { write(&DEBUG, msg, true, false); } 189 | 190 | //////////////////////////////////////////////////////////// 191 | /// \brief Sets maximum LogLevel severity for console write 192 | /// \param level New maximum severity 193 | /// \see Logger::maxSeverity 194 | /////////////////////////////////////////////////////////// 195 | static void setSeverity(int level) noexcept; 196 | 197 | ///////////////////////////////////////////////////////// 198 | /// \brief Sets maximum LogLevel severity for log output 199 | /// \param level New maximum severity 200 | /// \see Logger::maxFileSeverity 201 | //////////////////////////////////////////////////////// 202 | static void setFileSeverity(int level) noexcept; 203 | 204 | //////////////////////////////////////////////////////////// 205 | /// \brief Gets maximum LogLevel severity for console write 206 | /// \returns Maximum LogLevel severity for console write 207 | /// \see Logger::maxSeverity 208 | /////////////////////////////////////////////////////////// 209 | static int &getSeverity() noexcept; 210 | 211 | ///////////////////////////////////////////////////////// 212 | /// \brief Gets maximum LogLevel severity for log output 213 | /// \returns Maximum LogLevel severity for log output 214 | /// \see Logger::maxFileSeverity 215 | //////////////////////////////////////////////////////// 216 | static int &getFileSeverity() noexcept; 217 | 218 | /////////////////////////////////////////////////////////////////////////////////////////// 219 | /// \brief Opens log file stream for recording logs 220 | /// \note Logging is still possible without calling start(), but logs will not be recorded 221 | /// \see Logger::file 222 | ////////////////////////////////////////////////////////////////////////////////////////// 223 | static void start(); 224 | 225 | ////////////////////////////////////////////////////////////////// 226 | /// \brief Closes log file stream, saves and stops recording logs 227 | /// \see Logger::file 228 | ///////////////////////////////////////////////////////////////// 229 | static void end(); 230 | 231 | /////////////////////////////// 232 | /// \brief Destroys the logger 233 | /// \note Calls Logger::end() 234 | /// \see Logger::end() 235 | ////////////////////////////// 236 | ~Logger(); 237 | 238 | private: 239 | // OS specific variables 240 | #ifdef _WIN32 241 | const static HANDLE hOut; 242 | static WORD defaultColorAttribs; 243 | static CONSOLE_SCREEN_BUFFER_INFO csbi; 244 | 245 | // Remember original attributes 246 | static void setConsoleInfo() noexcept; 247 | #else 248 | static const char *lastFgColor; 249 | static const char *lastConsoleColor; 250 | static const char *fc[16]; 251 | static const char *bc[16]; 252 | #endif // _WIN32 253 | 254 | static std::string LOG_NAME; // Name of log file 255 | static std::string LOG_FLDR; // Name of log folder 256 | static std::string LOG_BACKUP_FLDR; // Name of log backups folder 257 | 258 | static std::ofstream file; // Log file 259 | static int maxSeverity; // Maximum log-level severity for console write 260 | static int maxFileSeverity; // Maximum log-level severity for file write 261 | }; 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Logger-Cpp 2 | Simple logger written in C++ 3 | --------------------------------------------------------------------------------