#include // std::tm, std::time_t #include // std::ifstream, std::ofstream #include // std::cout, std::endl #include // std::runtime_error #include namespace op { // Private auxiliar functions bool checkIfErrorHas(const ErrorMode errorMode) { for (const auto& sErrorMode : ConfigureError::getErrorModes()) if (sErrorMode == errorMode || sErrorMode == ErrorMode::All) return true; return false; } bool checkIfLoggingHas(const LogMode loggingMode) { for (const auto& sLoggingMode : ConfigureLog::getLogModes()) if (sLoggingMode == loggingMode || sLoggingMode == LogMode::All) return true; return false; } std::string createFullMessage(const std::string& message, const int line = -1, const std::string& function = "", const std::string& file = "") { const auto hasMessage = (!message.empty()); const auto hasLocation = (line != -1 || !function.empty() || !file.empty()); std::string fullMessage; if (hasMessage) { fullMessage += message; // // Add dot at the end if the sentence does not finish in a programming file path (this happens when the // error is propagated over several error) // if (*message.crbegin() != '.' // && (message.size() < 4 // || (message.substr(message.size() - 4, 4) != ".cpp" // && message.substr(message.size() - 4, 4) != ".hpp" // && message.substr(message.size() - 4, 4) != ".h" // && message.substr(message.size() - 4, 4) != ".c"))) // fullMessage += "."; if (hasLocation) { if (*message.crbegin() != '.') fullMessage += " in "; else fullMessage += " In "; } } if (hasLocation) fullMessage += file + ":" + function + "():" + std::to_string(line); else if (!hasMessage) // else assumed !hasLocation fullMessage += "[Undefined]"; return fullMessage; } std::string getTime() { // Ubuntu version std::time_t rawtime; struct std::tm timeStruct; std::time(&rawtime); timeStruct = *localtime(&rawtime); // // Windows version // struct std::tm timeStruct; // std::time_t time_create{std::time(NULL)}; // localtime_s(&timeStruct, &time_create); // Common timeStruct.tm_mon++; timeStruct.tm_year += 1900; return std::to_string(timeStruct.tm_year) + '_' + std::to_string(timeStruct.tm_mon) + '_' + std::to_string(timeStruct.tm_mday) + "___" + std::to_string(timeStruct.tm_hour) + '_' + std::to_string(timeStruct.tm_min) + '_' + std::to_string(timeStruct.tm_sec); } void fileLogging(const std::string& message) { std::string fileToOpen{"errorLogging.txt"}; // Get current file size std::ifstream in{fileToOpen, std::ios::binary | std::ios::ate}; const auto currentSizeBytes = in.tellg(); in.close(); // Continue at the end of the file or delete it and re-write it (according to current file size) const auto maxLogSize = 15 * 1024 * 1024; // 15 MB std::ofstream loggingFile{fileToOpen, (currentSizeBytes < maxLogSize ? std::ios_base::app : std::ios_base::trunc)}; // Message to write loggingFile << getTime(); loggingFile << "\n"; loggingFile << message; loggingFile << "\n\n\n\n\n"; loggingFile.close(); } // Public functions void error(const std::string& message, const int line, const std::string& function, const std::string& file) { const std::string errorInit = "\nError:\n"; const std::string errorEnum = "- "; // Compose error message std::string errorMessageToPropagate; std::string errorMessageToPrint; // If first error if (message.size() < errorInit.size() || message.substr(0, errorInit.size()) != errorInit) { errorMessageToPrint = errorInit + createFullMessage(message) + "\n\nComing from:\n" + errorEnum + createFullMessage("", line, function, file); errorMessageToPropagate = errorMessageToPrint + "\n"; } // If error propagated among different errors else { errorMessageToPrint = errorEnum + createFullMessage("", line, function, file); errorMessageToPropagate = createFullMessage(message.substr(0, message.size()-1)) + "\n" + errorMessageToPrint + "\n"; } // std::cerr if (checkIfErrorHas(ErrorMode::StdCerr)) std::cerr << errorMessageToPrint << std::endl; // File logging if (checkIfErrorHas(ErrorMode::FileLogging)) fileLogging(errorMessageToPrint); // std::runtime_error if (checkIfErrorHas(ErrorMode::StdRuntimeError)) throw std::runtime_error{errorMessageToPropagate}; } void log(const std::string& message, const Priority priority, const int line, const std::string& function, const std::string& file) { if (priority >= ConfigureLog::getPriorityThreshold()) { const auto infoMessage = createFullMessage(message, line, function, file); // std::cout if (checkIfLoggingHas(LogMode::StdCout)) std::cout << infoMessage << std::endl; // File logging if (checkIfLoggingHas(LogMode::FileLogging)) fileLogging(infoMessage); } } // ConfigureError - Private variables // std::vector sErrorModes {ErrorMode::StdRuntimeError}; std::vector sErrorModes {ErrorMode::StdCerr, ErrorMode::StdRuntimeError}; std::mutex sErrorModesMutex {}; std::vector ConfigureError::getErrorModes() { const std::lock_guard lock{sErrorModesMutex}; return sErrorModes; } void ConfigureError::setErrorModes(const std::vector& errorModes) { const std::lock_guard lock{sErrorModesMutex}; sErrorModes = errorModes; } // ConfigureLog - Private variables std::atomic sPriorityThreshold {Priority::High}; // std::atomic sPriorityThreshold {Priority::None}; std::vector sLoggingModes {LogMode::StdCout}; std::mutex sConfigureLogMutex {}; Priority ConfigureLog::getPriorityThreshold() { return sPriorityThreshold; } const std::vector& ConfigureLog::getLogModes() { const std::lock_guard lock{sConfigureLogMutex}; return sLoggingModes; } void ConfigureLog::setPriorityThreshold(const Priority priorityThreshold) { sPriorityThreshold = priorityThreshold; } void ConfigureLog::setLogModes(const std::vector& loggingModes) { const std::lock_guard lock{sConfigureLogMutex}; sLoggingModes = loggingModes; } }