diff options
Diffstat (limited to 'src/core/common/Logger.hpp')
-rw-r--r-- | src/core/common/Logger.hpp | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp new file mode 100644 index 0000000..be82ea0 --- /dev/null +++ b/src/core/common/Logger.hpp @@ -0,0 +1,602 @@ +/* + 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 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" +#include "TextCursor.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, + + /** + * 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 +}; + +#ifdef NDEBUG +static constexpr Severity DEFAULT_MIN_SEVERITY = Severity::NOTE; +#else +static constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; +#endif + +// Forward declaration +class LoggerFork; + +/** + * 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: + /** + * Describes an included file. + */ + struct File { + /** + * Is the name of the file. + */ + std::string file; + + /** + * Position at which the file was included. + */ + TextCursor::Position pos; + + /** + * Context in which the file was included. + */ + TextCursor::Context ctx; + + /** + * Constructor of the File struct. + * + * @param file is the name of the included file. + * @param pos is the position in the parent file, at which this file + * was included. + * @param ctx is the context in which the feil was included. + */ + File(std::string file, TextCursor::Position pos, + TextCursor::Context ctx) + : file(file), pos(pos), ctx(ctx) + { + } + }; + + /** + * 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; + + /** + * Position in the text the message refers to. + */ + TextCursor::Position pos; + + /** + * Context the message refers to. + */ + TextCursor::Context ctx; + + /** + * Constructor of the Message struct. + * + * @param severity describes the message severity. + * @param msg contains the actual 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. + */ + Message(Severity severity, std::string msg, TextCursor::Position pos, + TextCursor::Context ctx) + : severity(severity), + msg(std::move(msg)), + pos(std::move(pos)), + ctx(std::move(ctx)){}; + }; + +protected: + /** + * Minimum severity a log message should have before it is discarded. + */ + const Severity minSeverity; + + /** + * Maximum encountered log message severity. + */ + Severity maxEncounteredSeverity; + + /** + * 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 processMessage(Message msg) {} + + /** + * Called whenever a new file is pushed onto the stack. + * + * @param file is the file that should be pushed onto the stack. + */ + virtual void processPushFile(File file) {} + + /** + * Called whenever a file is popped from the stack. + */ + virtual void processPopFile() {} + +public: + /** + * 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) + { + } + + /** + * Virtual destructor. + */ + virtual ~Logger(){}; + + // No copy + Logger(const Logger &) = delete; + + // No assign + Logger &operator=(const Logger &) = delete; + + /** + * 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 pos is the position the log message refers to. + * @param ctx describes the context of the log message. + */ + void log(Severity severity, std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}); + + /** + * Logs the given loggable exception. + * + * @param ex is the exception that should be logged. + */ + void log(const LoggableException &ex) + { + log(Severity::ERROR, ex.msg, ex.getPosition(), ex.getContext()); + } + + /** + * 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 pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void logAt(Severity severity, std::string msg, PosType &pos) + { + log(severity, std::move(msg), pos.getPosition(), pos.getContext()); + } + + /** + * Logs a debug message. Debug messages will be discarded if the software + * is compiled in the release mode (with the NDEBUG flag). + * + * @param msg is the actual log message. + * @param pos describes the position of the debug message. + * @param ctx describes the context of the debug message. + */ + void debug(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { +#ifndef NDEBUG + log(Severity::DEBUG, std::move(msg), std::move(pos), std::move(ctx)); +#endif + } + + /** + * Logs a debug message. Debug messages will be discarded if the software + * is compiled in the release mode. + * + * @param msg is the actual log message. + * @param pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void debug(std::string msg, PosType &pos) + { +#ifndef NDEBUG + logAt(Severity::DEBUG, std::move(msg), pos); +#endif + } + + /** + * Logs a note. + * + * @param msg is the actual log message. + * @param pos describes the position of the note. + * @param ctx describes the context of the note. + */ + void note(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { + log(Severity::NOTE, std::move(msg), std::move(pos), std::move(ctx)); + } + + /** + * Logs a note. + * + * @param msg is the actual log message. + * @param pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void note(std::string msg, PosType &pos) + { + logAt(Severity::NOTE, std::move(msg), pos); + } + + /** + * Logs a warning. + * + * @param msg is the actual log message. + * @param pos describes the position of the warning. + * @param ctx describes the context of the warning. + */ + void warning(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { + log(Severity::WARNING, std::move(msg), std::move(pos), std::move(ctx)); + } + + /** + * Logs a warning. + * + * @param msg is the actual log message. + * @param pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void warning(std::string msg, PosType &pos) + { + logAt(Severity::WARNING, std::move(msg), pos); + } + + /** + * Logs an error message. + * + * @param msg is the actual log message. + * @param pos is the position at which the error occured. + * @param ctx describes the context in which the error occured. + */ + void error(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { + log(Severity::ERROR, std::move(msg), std::move(pos), std::move(ctx)); + } + + /** + * Logs an error message. + * + * @param msg is the actual log message. + * @param pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void error(std::string msg, PosType &pos) + { + logAt(Severity::ERROR, std::move(msg), pos); + } + + /** + * Logs a fatal error message. + * + * @param msg is the actual log message. + * @param pos is the position at which the error occured. + * @param ctx describes the context in which the error occured. + */ + void fatalError(std::string msg, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { + log(Severity::FATAL_ERROR, std::move(msg), std::move(pos), + std::move(ctx)); + } + + /** + * Logs a fatal error message. + * + * @param msg is the actual log message. + * @param pos is a reference to a variable which provides position and + * context information. + */ + template <class PosType> + void fatalError(std::string msg, PosType &pos) + { + logAt(Severity::FATAL_ERROR, std::move(msg), pos); + } + + /** + * Pushes a new file name onto the internal filename stack. + * + * @param name is the name of the file to be added to the stack. + * @param pos is the position from which the new file is included. + * @param ctx is the context in which the new file is included. + */ + void pushFile(std::string name, + TextCursor::Position pos = TextCursor::Position{}, + TextCursor::Context ctx = TextCursor::Context{}) + { + processPushFile(File(std::move(name), std::move(pos), std::move(ctx))); + } + + /** + * Pops the filename from the internal filename stack. + * + * @return the current size of the filename stack. + */ + void popFile() { processPopFile(); } + + /** + * 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; } + + /** + * Returns a forked logger instance which can be used to collect log + * messages for which it is not sure whether they will be used. + */ + LoggerFork fork(); +}; + +/** + * Fork of the Logger -- stores all logged messages without actually pushing + * them to the underlying logger instance. + */ +class LoggerFork : public Logger { +private: + friend Logger; + + /** + * Intanally used to store the incomming function calls. + */ + enum class CallType { MESSAGE, PUSH_FILE, POP_FILE }; + + /** + * Datastructure used to represent a logger function call. + */ + struct Call { + /** + * Type of the function call. + */ + CallType type; + + /** + * Index of the associated data in the type-specific vector. + */ + size_t dataIdx; + + /** + * Constructor of the Call structure. + * + * @param type is the type of the call. + * @param dataIdx is the index of the associated data in the type + * specific data vector. + */ + Call(CallType type, size_t dataIdx) : type(type), dataIdx(dataIdx) {} + }; + + /** + * Vector storing all incomming calls. + */ + std::vector<Call> calls; + + /** + * Vector storing all incomming messages. + */ + std::vector<Message> messages; + + /** + * Vector storing all incomming pushed files. + */ + std::vector<File> files; + + /** + * Parent logger instance. + */ + Logger *parent; + + /** + * Constructor of the LoggerFork class. + * + * @param minSeverity is the minimum severity a message should have to be + * stored. + * @param parent is the parent logger instance. + */ + LoggerFork(Logger *parent, Severity minSeverity) + : Logger(minSeverity), parent(parent) + { + } + +protected: + void processMessage(Message msg) override; + void processPushFile(File file) override; + void processPopFile() override; + +public: + /** + * Commits all collected messages to the parent Logger instance. + */ + void commit(); + + /** + * Explicitly declared move constructor. + */ + LoggerFork(LoggerFork &&l) + : Logger(l.getMinSeverity()), + calls(std::move(l.calls)), + messages(std::move(l.messages)), + files(std::move(l.files)), + parent(std::move(l.parent)) + { + } +}; + +/** + * 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; + + /** + * Stack used to keep the file references. + */ + std::stack<File> files; + + /** + * The size of the stack the last time a file backtrace was printed. + */ + size_t lastFilePrinted = 0; + +protected: + void processMessage(Message msg) override; + void processPushFile(File file) override; + void processPopFile() override; + +public: + /** + * 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) + { + } + + /** + * Returns the name of the topmost file. + */ + std::string currentFilename(); +}; +} + +#endif /* _OUSIA_LOGGER_HPP_ */ + |