diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-11-23 01:39:42 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-11-23 01:39:42 +0100 |
commit | aaaf493e3cddcc2cb0797ca3fe7eca4f12a04453 (patch) | |
tree | a260341b3c9d0839358876a2d053fb20d0f3f05e /src | |
parent | ec0039e4656d579d423dac3f5d626cb2deed05af (diff) |
imlemented Logger, TerminalLogger, OusiaException, LoggableException
Diffstat (limited to 'src')
-rw-r--r-- | src/core/Exceptions.cpp | 45 | ||||
-rw-r--r-- | src/core/Exceptions.hpp | 170 | ||||
-rw-r--r-- | src/core/Logger.cpp | 160 | ||||
-rw-r--r-- | src/core/Logger.hpp | 384 |
4 files changed, 686 insertions, 73 deletions
diff --git a/src/core/Exceptions.cpp b/src/core/Exceptions.cpp new file mode 100644 index 0000000..92d9293 --- /dev/null +++ b/src/core/Exceptions.cpp @@ -0,0 +1,45 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include <sstream> + +#include "Exceptions.hpp" + +namespace ousia { + +/* Class LoggableException */ + +std::string LoggableException::formatMessage(const std::string &msg, + const std::string &file, int line, + int column, bool fatal) +{ + std::stringstream ss; + if (!file.empty()) { + ss << "while processing \"" << file << "\" "; + } + if (line >= 0) { + ss << "at line: " << line << " "; + if (column >= 0) { + ss << "col: " << column << " "; + } + } + ss << "message: " << msg; + return ss.str(); +} +} + diff --git a/src/core/Exceptions.hpp b/src/core/Exceptions.hpp new file mode 100644 index 0000000..f1bb95a --- /dev/null +++ b/src/core/Exceptions.hpp @@ -0,0 +1,170 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file Exceptions.hpp + * + * Describes basic exception classes which are used throughout Ousía. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_EXCEPTIONS_HPP_ +#define _OUSIA_EXCEPTIONS_HPP_ + +namespace ousia { + +/** + * Base exception class all other Ousía exceptions should derive from. + */ +class OusiaException : public std::exception { +private: + /** + * Error message which will be printed by the runtime environment if the + * exception is not caught and handled in the code. + */ + const std::string formatedMessage; + +public: + /** + * Constructor of the OusiaException class. + * + * @param formatedMessage is a formated message that should be printed by + * the runtime environment if the exception is not caught. + */ + OusiaException(std::string formatedMessage) + : formatedMessage(std::move(formatedMessage)) + { + } + + /** + * Virtual destructor. + */ + virtual ~OusiaException() {} + + /** + * Implementation of the std::exception what function and used to retrieve + * the error message that should be printed by the runtime environment. + * + * @return a reference to the formated message string given in the + * constructor. + */ + const char *what() const noexcept override + { + return formatedMessage.c_str(); + } +}; + +/** + * Exception class which can be directly passed to a Logger instance and thus + * makes it simple to handle non-recoverable errors in the code. + */ +class LoggableException : public OusiaException { +private: + /** + * Function used internally to build the formated message that should be + * reported to the runtime environment. + */ + static std::string formatMessage(const std::string &msg, + const std::string &file, int line, + int column, bool fatal); + +public: + /** + * Message describing the error that occured. + */ + const std::string msg; + + /** + * Name of the file in which the error occured. May be empty. + */ + const std::string file; + + /** + * Line at which the exception occured. Negative values are ignored. + */ + const int line; + + /** + * Column at which the exception occured. Negative values are ignored. + */ + const int column; + + /** + * If set to true, the exception should not be handled as recoverable error + * but as "fatal" error. + */ + const bool fatal; + + /** + * Constructor of the LoggableException class. + * + * @param msg contains the error message. + * @param file provides the context the message refers to. May be empty. + * @param line is the line in the above file the message refers to. + * @param column is the column in the above file the message refers to. + * @param fatal shoudl be set to true if the error is non-recoverable. + */ + LoggableException(std::string msg, std::string file, int line = -1, + int column = -1, bool fatal = true) + : OusiaException(formatMessage(msg, file, line, column, fatal)), + msg(std::move(msg)), + file(std::move(file)), + line(line), + column(column), + fatal(fatal) + { + } + + /** + * Constructor of the LoggableException class with empty file. + * + * @param msg contains the error message. + * @param line is the line in the above file the message refers to. + * @param column is the column in the above file the message refers to. + * @param fatal shoudl be set to true if the error is non-recoverable. + */ + LoggableException(std::string msg, int line = -1, int column = -1, + bool fatal = true) + : OusiaException(formatMessage(msg, "", line, column, fatal)), + msg(std::move(msg)), + line(line), + column(column), + fatal(fatal) + { + } + + /** + * Constructor of the LoggableException class with empty file. + * + * @param msg contains the error message. + * @param fatal should be set to true if the error is non-recoverable. + */ + LoggableException(std::string msg, bool fatal) + : OusiaException(formatMessage(msg, "", -1, -1, fatal)), + msg(std::move(msg)), + line(-1), + column(-1), + fatal(fatal) + { + } +}; +} + +#endif /* _OUSIA_EXCEPTIONS_HPP_ */ + diff --git a/src/core/Logger.cpp b/src/core/Logger.cpp index 2317e70..1a3b6c6 100644 --- a/src/core/Logger.cpp +++ b/src/core/Logger.cpp @@ -16,54 +16,146 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <iostream> +#include <sstream> + +#include "Logger.hpp" + namespace ousia { -static const int BLACK = 30; -static const int RED = 31; -static const int GREEN = 32; -static const int YELLOW = 33; -static const int BLUE = 34; -static const int MAGENTA = 35; -static const int CYAN = 36; -static const int WHITE = 37; - -void StreamLogger::logMessage(const LogMessage &msg) { - os << '['; +/* Class Logger */ + +void Logger::log(Severity severity, const std::string &msg, + const std::string &file, int line, int column) +{ + // Copy the current severity level + if (static_cast<int>(severity) > static_cast<int>(maxEncounteredSeverity)) { + maxEncounteredSeverity = severity; + } + + // Call the actual log message function if the severity is larger or equal + // to the minimum severity + if (static_cast<int>(severity) >= static_cast<int>(minSeverity)) { + process(Message{severity, msg, file, line, column}); + } +} + +unsigned int Logger::pushFilename(const std::string &name) +{ + filenameStack.push(name); + return filenameStack.size(); +} + +unsigned int Logger::popFilename() +{ + filenameStack.pop(); + return filenameStack.size(); +} + +void Logger::unwindFilenameStack(unsigned int pos) +{ + while (filenameStack.size() > pos && !filenameStack.empty()) { + filenameStack.pop(); + } +} + +/* Class TerminalLogger */ + +/** + * Small class used internally for formated terminal output using ANSI/VT100 + * escape codes on supported terminals. + * + * TODO: Deactivate if using windows or use the corresponding API function. + */ +class Terminal { +private: + /** + * If set to false, no control codes are generated. + */ + bool active; + +public: + static const int BLACK = 30; + static const int RED = 31; + static const int GREEN = 32; + static const int YELLOW = 33; + static const int BLUE = 34; + static const int MAGENTA = 35; + static const int CYAN = 36; + static const int WHITE = 37; + + Terminal(bool active) : active(active) {} + + std::string color(int color, bool bright = true) const + { + if (!active) { + return std::string{}; + } + std::stringstream ss; + ss << "\x1b["; + if (bright) { + ss << "1;"; + } + ss << color << "m"; + return ss.str(); + } + + std::string reset() const + { + if (!active) { + return std::string{}; + } + return "\x1b[0m"; + } +}; + +void TerminalLogger::process(const Message &msg) +{ + Terminal t(useColor); + + // Print the file name + if (msg.hasFile()) { + os << t.color(Terminal::WHITE, true) << msg.file << t.reset(); + } + + // Print line and column number + if (msg.hasLine()) { + if (msg.hasFile()) { + os << ':'; + } + os << t.color(Terminal::WHITE, true) << msg.line + << t.reset(); + if (msg.hasColumn()) { + os << ':' << msg.column; + } + } + + // Print the optional seperator + if (msg.hasFile() || msg.hasLine()) { + os << ": "; + } + + // Print the severity switch (msg.severity) { case Severity::DEBUG: - os << "debug" << os; break; - case Severity::INFO: - os << "info" << os; + case Severity::NOTE: + os << t.color(Terminal::CYAN, true) << "note: "; break; case Severity::WARNING: - os << "warning" << os; + os << t.color(Terminal::MAGENTA, true) << "warning: "; break; case Severity::ERROR: - os << "error" << os; + os << t.color(Terminal::RED, true) << "error: "; break; case Severity::FATAL_ERROR: - is << "fatal error" << os; + os << t.color(Terminal::RED, true) << "error: "; break; } - os << ']'; - - // Print the file name - if (!msg.file.empty()) { - os << msg.file; - - // Print the line and column - if (msg.line >= 0) { - os << ':' << msg.line; - if (msg.column >= 0) { - os << ':' << msg.column; - } - } - } + os << t.reset(); // Print the actual message - os << ' ' << msg.msg; + os << msg.msg << std::endl; +} } - -}; diff --git a/src/core/Logger.hpp b/src/core/Logger.hpp index 8f0abfb..260d010 100644 --- a/src/core/Logger.hpp +++ b/src/core/Logger.hpp @@ -16,111 +16,417 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/** + * @file Logger.hpp + * + * Contains classes for logging messages in Ousía. Provides a generic Logger + * class, and TerminalLogger, an extension of Logger which logs do an output + * stream. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + #ifndef _OUSIA_LOGGER_HPP_ #define _OUSIA_LOGGER_HPP_ #include <ostream> +#include <stack> #include <string> #include <vector> +#include "Exceptions.hpp" + namespace ousia { +/** + * Enum containing the severities used for logging errors and debug messages. + */ enum class Severity : int { + /** + * Indicates that this message was only printed for debugging. Note that + * in release builds messages with this severity are discarded. + */ DEBUG = 0, - INFO = 1, + + /** + * A message which might provide additional information to the user. + */ + NOTE = 1, + + /** + * A message which warns of possible mistakes by the user which might not be + * actual errors but may lead to unintended behaviour. + */ WARNING = 2, + + /** + * An error occurred while processing, however program execution continues, + * trying to deal with the error situation (graceful degradation). However, + * messages with this severity may be followed up by fatal errors. + */ ERROR = 3, + + /** + * A fatal error occurred. Program execution cannot continue. + */ FATAL_ERROR = 4 }; -struct LogMessage { - Severity severity; - std::string msg; - std::string file; - int line; - int column; - - LogMessage(Severity severity, std::string msg, std::string file, int line, - int column) - : severity(severity), - msg(std::move(msg)), - file(std::move(file)), - line(line), - column(column){}; -}; +#ifdef NDEBUG +static constexpr Severity DEFAULT_MIN_SEVERITY = Severity::NOTE; +#else +static constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; +#endif +/** + * The Logger class is the base class the individual logging systems should + * derive from. It provides a simple interface for logging errors, warnings and + * notes and filters these according to the set minimum severity. Additionally + * a stack of file names is maintained in order to allow simple descent into + * included files. Note however, that this base Logger class simply discards the + * incomming log messages. Use one of the derived classes to actually handle the + * log messages. + */ class Logger { +public: + /** + * The message struct represents a single log message and all information + * attached to it. + */ + struct Message { + /** + * Severity of the log message. + */ + Severity severity; + + /** + * Actual log message. + */ + std::string msg; + + /** + * Refers to the file which provides the context for this error message. + * May be empty. + */ + std::string file; + + /** + * Line in the above file the error message refers to. Ignored if + * smaller than zero. + */ + int line; + + /** + * Column in the above file the error message refers to. Ignored if + * smaller than zero. + */ + int column; + + /** + * Constructor of the Message struct. + * + * @param severity describes the message severity. + * @param msg contains the actual message. + * @param file provides the context the message refers to. May be empty. + * @param line is the line in the above file the message refers to. + * @param column is the column in the above file the message refers to. + */ + Message(Severity severity, std::string msg, std::string file, int line, + int column) + : severity(severity), + msg(std::move(msg)), + file(std::move(file)), + line(line), + column(column){}; + + /** + * Returns true if the file string is set. + * + * @return true if the file string is set. + */ + bool hasFile() const { return !file.empty(); } + + /** + * Returns true if the line is set. + * + * @return true if the line number is a non-negative integer. + */ + bool hasLine() const { return line >= 0; } + + /** + * Returns true if column and line are set (since a column has no + * significance without a line number). + * + * @return true if line number and column number are non-negative + * integers. + */ + bool hasColumn() const { return hasLine() && column >= 0; } + }; + private: - Severity maxLogSeverity = Severity::DEBUG; - std::string curFile; + /** + * Minimum severity a log message should have before it is discarded. + */ + Severity minSeverity; + + /** + * Maximum encountered log message severity. + */ + Severity maxEncounteredSeverity; + + /** + * Stack containing the current file names that have been processed. + */ + std::stack<std::string> filenameStack; protected: - virtual void logMessage(const LogMessage &msg){}; + /** + * Function to be overriden by child classes to actually display or store + * the messages. The default implementation just discards all incomming + * messages. + * + * @param msg is an instance of the Message struct containing the data that + * should be logged. + */ + virtual void process(const Message &msg){}; public: - Logger(){}; + /** + * Constructor of the Logger class. + * + * @param minSeverity is the minimum severity a log message should have. + * Messages below this severity are discarded. + */ + Logger(Severity minSeverity = DEFAULT_MIN_SEVERITY) + : minSeverity(minSeverity), maxEncounteredSeverity(Severity::DEBUG) + { + } Logger(const Logger &) = delete; - virtual ~Logger(); + /** + * Virtual destructor. + */ + virtual ~Logger(){}; + /** + * Logs the given message. Most generic log function. + * + * @param severity is the severity of the log message. + * @param msg is the actual log message. + * @param file is the name of the file the message refers to. May be empty. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void log(Severity severity, const std::string &msg, const std::string &file, - int line = -1, int column = -1) - { - // Copy the current severity level - if (static_cast<int>(severity) > static_cast<int>(maxSeverity)) { - maxSeverity = severity; - } - - // Call the actual log message function - logMessage(LogMessage{severity, msg, file, line, column}); - } + int line = -1, int column = -1); + /** + * Logs the given message. The file name is set to the topmost file name on + * the file name stack. + * + * @param severity is the severity of the log message. + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void log(Severity severity, const std::string &msg, int line = -1, int column = -1) { - log(severity, msg, curFile, line, column); + log(severity, msg, currentFilename(), line, column); + } + + /** + * Logs the given loggable exception. + * + * @param ex is the exception that should be logged. + */ + void log(const LoggableException &ex) + { + log(ex.fatal ? Severity::FATAL_ERROR : Severity::ERROR, ex.msg, + ex.file.empty() ? currentFilename() : ex.file, ex.line, ex.column); } + /** + * Logs a debug message. The file name is set to the topmost file name on + * the file name stack. + * + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void debug(const std::string &msg, int line = -1, int column = -1) { log(Severity::DEBUG, msg, line, column); } - void info(const std::string &msg, int line = -1, int column = -1) + /** + * Logs a note. The file name is set to the topmost file name on + * the file name stack. + * + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ + void note(const std::string &msg, int line = -1, int column = -1) { - log(Severity::INFO, msg, line, column); + log(Severity::NOTE, msg, line, column); } + /** + * Logs a warning. The file name is set to the topmost file name on + * the file name stack. + * + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void warning(const std::string &msg, int line = -1, int column = -1) { log(Severity::WARNING, msg, line, column); } + /** + * Logs an error message. The file name is set to the topmost file name on + * the file name stack. + * + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void error(const std::string &msg, int line = -1, int column = -1) { log(Severity::ERROR, msg, line, column); } + /** + * Logs a fatal error. The file name is set to the topmost file name on + * the file name stack. + * + * @param msg is the actual log message. + * @param line is the line in the above file at which the error occured. + * Ignored if negative. + * @param column is the column in the above file at which the error occured. + * Ignored if negative. + */ void fatalError(const std::string &msg, int line = -1, int column = -1) { log(Severity::FATAL_ERROR, msg, line, column); } - Severity getMaxSeverity() { return maxSeverity; } + /** + * Pushes a new file name onto the internal filename stack. + * + * @param name is the name of the file that should be added to the filename + * stack. + * @return the size of the filename stack. This number can be passed to the + * "unwindFilenameStack" method in order to return the stack to state it was + * in after this function has been called. + */ + unsigned int pushFilename(const std::string &name); + + /** + * Pops the filename from the internal filename stack. + * + * @return the current size of the filename stack. + */ + unsigned int popFilename(); + + /** + * Pops elements from the filename stack while it has more elements than + * the given number and the stack is non-empty. + * + * @param pos is the position the filename stack should be unwound to. Use + * a number returned by pushFilename. + */ + void unwindFilenameStack(unsigned int pos); + + /** + * Returns the topmost filename from the internal filename stack. + * + * @return the topmost filename from the filename stack or an empty string + * if the filename stack is empty. + */ + std::string currentFilename() + { + return filenameStack.empty() ? std::string{} : filenameStack.top(); + } + + /** + * Returns the maximum severity that was encountered by the Logger but at + * least Severity::DEBUG. + * + * @return the severity of the most severe log message but at least + * Severity::DEBUG. + */ + Severity getMaxEncounteredSeverity() { return maxEncounteredSeverity; } + + /** + * Returns the minimum severity. Messages with a smaller severity are + * discarded. + * + * @return the minimum severity. + */ + Severity getMinSeverity() { return minSeverity; } + + /** + * Sets the minimum severity. Messages with a smaller severity will be + * discarded. Only new messages will be filtered according to the new value. + * + * @param severity is the minimum severity for new log messages. + */ + void setMinSeverity(Severity severity) { minSeverity = severity; } }; -class StreamLogger { +/** + * Class extending the Logger class and printing the log messages to the given + * stream. + */ +class TerminalLogger : public Logger { private: + /** + * Reference to the target output stream. + */ std::ostream &os; + + /** + * If true, the TerminalLogger will use colors to make the log messages + * prettier. + */ bool useColor; protected: - void logMessage(const LogMessage &msg) override; + /** + * Implements the process function and logs the messages to the output. + */ + void process(const Message &msg) override; public: - StreamLogger(std::ostream &os, bool useColor = false) - : os(os), useColor(useColor) + /** + * Constructor of the TerminalLogger class. + * + * @param os is the output stream the log messages should be logged to. + * Should be set to std::cerr in most cases. + * @param useColor if true, the TerminalLogger class will do its best to + * use ANSI/VT100 control sequences for colored log messages. + * @param minSeverity is the minimum severity below which log messages are + * discarded. + */ + TerminalLogger(std::ostream &os, bool useColor = false, + Severity minSeverity = DEFAULT_MIN_SEVERITY) + : Logger(minSeverity), os(os), useColor(useColor) { } }; |