19 changed files with 1124 additions and 142 deletions
			
			
		- 
					10CMakeLists.txt
- 
					13resources/3rdparty/CMakeLists.txt
- 
					21resources/3rdparty/l3pp/LICENSE
- 
					107resources/3rdparty/l3pp/Readme.md
- 
					158resources/3rdparty/l3pp/formatter.h
- 
					106resources/3rdparty/l3pp/impl/formatter.h
- 
					123resources/3rdparty/l3pp/impl/logger.h
- 
					32resources/3rdparty/l3pp/impl/logging.h
- 
					18resources/3rdparty/l3pp/impl/sink.h
- 
					165resources/3rdparty/l3pp/l3pp.h
- 
					179resources/3rdparty/l3pp/logger.h
- 
					108resources/3rdparty/l3pp/sink.h
- 
					2src/builder/ExplicitDFTModelBuilder.cpp
- 
					7src/cli/cli.cpp
- 
					2src/settings/modules/DebugSettings.cpp
- 
					33src/utility/initialize.cpp
- 
					7src/utility/initialize.h
- 
					23src/utility/logging.h
- 
					130src/utility/macros.h
| @ -0,0 +1,21 @@ | |||
| The MIT License (MIT) | |||
| 
 | |||
| Copyright (c) 2016 nafur | |||
| 
 | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| 
 | |||
| The above copyright notice and this permission notice shall be included in all | |||
| copies or substantial portions of the Software. | |||
| 
 | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| SOFTWARE. | |||
| @ -0,0 +1,107 @@ | |||
| L3++: Lightweight Logging Library for C++ | |||
| ===== | |||
| 
 | |||
| L3++ is a self-contained, single-header, cross-platform logging library for C++. | |||
| 
 | |||
| The main goals for this library are simplicity, modularity and ease of use. | |||
| This library is released under the MIT License. | |||
| 
 | |||
| Copyright (C) 2015 Gereon Kremer | |||
| 
 | |||
| 
 | |||
| Concepts | |||
| ===== | |||
| 
 | |||
| L3++ is based on the following conceptual components: | |||
| 
 | |||
| * RecordInfo: A record info stores auxiliary information of a log message like the filename, line number and function name where the log message was emitted. | |||
| * Logger: A logger categorizes log messages, usually according to logical components or modules in the source code. | |||
| * Sink: A sink represents a logging output, for example the terminal or a log file. | |||
| * Formatter: A formatter is associated with a sink and converts a log message into an actual string. | |||
| 
 | |||
| 
 | |||
| Log levels | |||
| ----- | |||
| The following log levels exist (from `l3pp::LogLevel`): | |||
| * ALL | |||
| * TRACE | |||
| * DEBUG | |||
| * INFO | |||
| * WARN | |||
| * ERR | |||
| * FATAL | |||
| * OFF | |||
| 
 | |||
| Hierarchical Loggers | |||
| ----- | |||
| Loggers are hierarchically structured strings like `"app.module.submodule"`. In this example, `submodule` is considered a sublogger of `module` and `app` the parent of `module`. A sublogger implicitly inherits all sinks and the log level of the parent, unless explicitly configured otherwise. | |||
| 
 | |||
| The hierarchical tree of loggers always contains the root logger at the top. The root logger itself does not have a parent, nor a name. It can be accessed via `l3pp::Logging::getRootLogger()`. | |||
| 
 | |||
| Each logger is assigned a log level, or is configured to inherit the log level of the parent (with the special log level `l3pp::LogLevel::INHERIT`). Note that the root logger cannot inherit a log level. Any log entry with a lower level is filtered out and will not be logged. | |||
| 
 | |||
| A logger can also be assigned one or more sinks. By default, a logger will log to both the sinks of its parent, as well as its own sinks. Should a logger only use it own sinks, it should be set to non-additive (using `Logger::setAdditive(false)`). | |||
| 
 | |||
| Sinks | |||
| ----- | |||
| A sink provides an output for loggers. Loggers may define multiple sinks, and sinks may be shared between loggers. Sinks are associated with a formatter and a log level. The log level specifies the minimum level of a message for it to be output (independent of the log level of a logger), and by default permits all log messages. A formatter formats the log messages before being output. By default, a simple formatter is used which prints the log level, message and a newline, but other formatters can be specified. | |||
| 
 | |||
| Formatters | |||
| ----- | |||
| A formatter shapes a log message before being sent to its final destination. A non-configurable simple formatter exists, as well as a template-based formatter. The latter specifies the format of a message by means of its template arguments, see `l3pp::makeTemplateFormatter`. | |||
| 
 | |||
| 
 | |||
| Basic Usage | |||
| ===== | |||
| 
 | |||
| A logger object can be accessed via `l3pp::Logger::getLogger()` or `l3pp::Logger::getRootLogger()`. By default, the root logger does not output anywhere. Therefore, a sink should be added. An initial configuration may look like this: | |||
| 
 | |||
|     l3pp::Logger::initialize(); | |||
|     l3pp::SinkPtr sink = log4carl::StreamSink::create(std::clog); | |||
|     l3pp::Logger::getRootLogger()->addSink(sink); | |||
|     l3pp::Logger::getRootLogger()->setLevel(log4carl::LogLevel::INFO); | |||
| 
 | |||
| In this demo, a single sink is created that passes log messages to the standard logging stream `std::clog`. All messages must have at least level `LVL_INFO` before being printed. | |||
| 
 | |||
| The actual logging is performed using a handful of macros. | |||
| These macros | |||
| 
 | |||
| 
 | |||
| Considerations | |||
| ===== | |||
| 
 | |||
| Performance | |||
| ----- | |||
| While the use of hierarchical loggers and multiple sinks with associated formatters gives a lot of flexibility, it comes at a certain price. As the configuration is done at runtime (and may even change at runtime), the question whether a certain message is printed can only be answered at runtime. Therefore, every message, whether you will ever see it or not, has to pass through the logger and cost runtime. | |||
| 
 | |||
| To mitigate this, we suggest the following: | |||
| 
 | |||
| Create a preprocessor flag (like `ENABLE_LOGGING`) and define your own set of logging macros. | |||
| If this flag is defined, make your macros forward to the `L3PP_LOG_*` macros. | |||
| If this flag is not defined, make your macros do nothing. | |||
| 
 | |||
| 
 | |||
| Multiple usages in the same project | |||
| ----- | |||
| Assume you have an application that uses L3++ for logging as well as some other library that also uses L3++. | |||
| L3++ will play nicely in this scenario (partly, it was designed for this case). | |||
| 
 | |||
| However, you should take care of a few things: | |||
| * Colliding loggers: Prefix your loggers with some unique prefix. | |||
| * Colliding macros: If you implement the aforementioned `ENABLE_LOGGING` macro, prefix your macros with your project name. Otherwise, these macros will collide. | |||
| 
 | |||
| 
 | |||
| Implementation Details | |||
| ===== | |||
| 
 | |||
| Sinks | |||
| ----- | |||
| A sink is a class that provides some `log` method. Any class that inherits from `l3pp::Sink` can be used. | |||
| 
 | |||
| As of now, two implementations are available:  | |||
| * FileSink: Writes to a output file. | |||
| * StreamSink: Writes to any given `std::ostream`, for example to `std::cout`. | |||
| 
 | |||
| Formatters | |||
| ----- | |||
| A formatter is a functor that given a log entry, provides a formatted string. The base class `Formatter` provides some very simple formatting, whereas `TemplateFormatter` provides more control over the shape. Internally, a `TemplateFormatter` streams its arguments to a stream before constructing the string. The special types `FieldStr` and `TimeStr` can be used to format particular attributes of a log entry. | |||
| @ -0,0 +1,158 @@ | |||
| /** | |||
|  * @file formatter.h | |||
|  * @author Harold Bruintjes <h.bruintjes@cs.rwth-aachen.de> | |||
|  * | |||
|  * Define the Formatter class, used to format output of a sink | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <string> | |||
| #include <tuple> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| /** | |||
|  * Formats a log messages. This is a base class that simply print the message | |||
|  * with the log level prefix, see derived classes such as TemplatedFormatter | |||
|  * for more interesting data. | |||
|  */ | |||
| class Formatter { | |||
| 	friend class Logger; | |||
| 
 | |||
| 	static void initialize(); | |||
| 
 | |||
| 	virtual std::string format(EntryContext const& context, std::string const& msg) const; | |||
| public: | |||
| 	virtual ~Formatter() {} | |||
| 
 | |||
| 	std::string operator()(EntryContext const& context, std::string const& msg) { | |||
| 		return format(context, msg); | |||
| 	} | |||
| }; | |||
| typedef std::shared_ptr<Formatter> FormatterPtr; | |||
| 
 | |||
| /** | |||
|  * Possible fields for FieldStr instance | |||
|  */ | |||
| enum class Field { | |||
| 	/// Name of the file (everything following the last path separator) | |||
| 	FileName, | |||
| 	/// Full path of the file | |||
| 	FilePath, | |||
| 	/// Line number | |||
| 	Line, | |||
| 	/// Name of function currently executed | |||
| 	Function, | |||
| 	/// Name of the logger | |||
| 	LoggerName, | |||
| 	/// Message to be logged | |||
| 	Message, | |||
| 	/// Level of the log entry | |||
| 	LogLevel, | |||
| 	/// Number of milliseconds since the logger was initialized | |||
| 	WallTime, | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Controls justification of formatted log fields. | |||
|  */ | |||
| enum class Justification { | |||
| 	/// Left align field | |||
| 	LEFT, | |||
| 	/// Right align field | |||
| 	RIGHT | |||
| }; | |||
| 
 | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * Formatter for log entry fields, with the exception of time stamp formatting | |||
|  * (see TimeStr for that). The Field template argument determines which field | |||
|  * is printed, see logging::Field. | |||
|  * The other template arguments control the alignment of the output string. | |||
|  */ | |||
| template<Field field, int Width = 0, Justification j = Justification::RIGHT, char Fill = ' '> | |||
| class FieldStr { | |||
| public: | |||
| 	void stream(std::ostream& os, EntryContext const& context, std::string const& msg) const; | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Formatter for log time stamps. The constructor expects a single string | |||
|  * argument which is a formatter for the time stamp. For the specification of | |||
|  * this format string see the documentation for std::put_time . You can use for | |||
|  * example "%c" or "%T". | |||
|  * The template arguments control the alignment of the output string. | |||
|  */ | |||
| class TimeStr { | |||
| 	std::string formatStr; | |||
| 
 | |||
| public: | |||
| 	TimeStr(char const* format) : formatStr(format) { | |||
| 	} | |||
| 	TimeStr(std::string const& format) : formatStr(format) { | |||
| 	} | |||
| 
 | |||
| 	void stream(std::ostream& os, EntryContext const& context, std::string const&) const; | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Formatter which formats the output based on the (templated) arguments given. | |||
|  * The arguments can be anything that implements the stream operator <<, but | |||
|  * more interestingly also the various FormatField subclasses. These classes | |||
|  * can output the various fields associated with a log entry. | |||
|  */ | |||
| template<typename ... Formatters> | |||
| class TemplateFormatter : public Formatter { | |||
| 	std::tuple<Formatters...> formatters; | |||
| 
 | |||
| 	template <int N> | |||
| 	typename std::enable_if<N < (sizeof...(Formatters))>::type | |||
| 	formatTuple(EntryContext const& context, std::string const& msg, std::ostream& os) const { | |||
| 		formatElement(std::get<N>(formatters), os, context, msg); | |||
| 		formatTuple<N+1>(context, msg, os); | |||
| 	} | |||
| 
 | |||
| 	template <int N> | |||
| 	typename std::enable_if<(N >= sizeof...(Formatters))>::type | |||
| 	formatTuple(EntryContext const&, std::string const&, std::ostream&) const { | |||
| 	} | |||
| 
 | |||
| 	template<Field field, int Width, Justification j, char Fill> | |||
| 	void formatElement(FieldStr<field, Width, j, Fill> const& t, std::ostream& stream, EntryContext const& context, std::string const& msg) const { | |||
| 		t.stream(stream, context, msg); | |||
| 	} | |||
| 
 | |||
| 	void formatElement(TimeStr const& t, std::ostream& stream, EntryContext const& context, std::string const& msg) const { | |||
| 		t.stream(stream, context, msg); | |||
| 	} | |||
| 
 | |||
| 	template<typename T> | |||
| 	void formatElement(T const& t, std::ostream& stream, EntryContext const&, std::string const&) const { | |||
| 		stream << t; | |||
| 	} | |||
| public: | |||
| 	TemplateFormatter(Formatters ... formatters) : | |||
| 		formatters(std::forward<Formatters>(formatters)...) | |||
| 	{ | |||
| 	} | |||
| 
 | |||
| 	std::string format(EntryContext const& context, std::string const& msg) const override; | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Helper function to create a TemplateFormatter. Simply call with some | |||
|  * formatable arguments, e.g., | |||
|  * @code{.cpp} | |||
|  * logging::makeTemplateFormatter( | |||
|  *     logging::FieldStr<logging::Field::LogLevel>(), " - ", | |||
|  *     logging::FieldStr<logging::Field::Message>(), "\n"); | |||
|  * @endcode | |||
|  */ | |||
| template<typename ... Formatters> | |||
| FormatterPtr makeTemplateFormatter(Formatters&& ... formatters) { | |||
| 	return std::make_shared<TemplateFormatter<Formatters...>>(std::forward<Formatters>(formatters)...); | |||
| } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,106 @@ | |||
| /** | |||
|  * @file formatter.h | |||
|  * @author Harold Bruintjes <h.bruintjes@cs.rwth-aachen.de> | |||
|  * | |||
|  * Implementation of Formatter classes | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <chrono> | |||
| #include <ctime> | |||
| #include <iomanip> | |||
| #include <sstream> | |||
| #include <cstring> //strrchr | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| namespace detail { | |||
| 	/** | |||
| 	 * Internal function to get wall-time | |||
| 	 */ | |||
| 	inline static std::chrono::system_clock::time_point GetStartTime() { | |||
| 		static std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now(); | |||
| 		return startTime; | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void Formatter::initialize() { | |||
| 	// Init wall-time | |||
| 	detail::GetStartTime(); | |||
| } | |||
| 
 | |||
| inline std::string Formatter::format(EntryContext const& context, std::string const& msg) const { | |||
| 	std::stringstream stream; | |||
| 	stream << context.level << " - " << msg << '\n'; | |||
| 	return stream.str(); | |||
| } | |||
| 
 | |||
| template<Field field, int Width, Justification j, char Fill> | |||
| inline void FieldStr<field, Width, j, Fill>::stream(std::ostream& os, EntryContext const& context, std::string const& msg) const { | |||
| 	os << std::setw(Width); | |||
| 	os << std::setfill(Fill); | |||
| 	switch(j) { | |||
| 		case Justification::LEFT: | |||
| 			os << std::left; | |||
| 		case Justification::RIGHT: | |||
| 			os << std::right; | |||
| 	} | |||
| 
 | |||
| 	switch(field) { | |||
| 		case Field::FileName: | |||
| #ifdef _WIN32 | |||
| 			os << strrchr(context.filename, '\\')+1; | |||
| #else | |||
| 			os << strrchr(context.filename, '/')+1; | |||
| #endif | |||
| 			break; | |||
| 		case Field::FilePath: | |||
| 			os << context.filename; | |||
| 			break; | |||
| 		case Field::Line: | |||
| 			os << context.line; | |||
| 			break; | |||
| 		case Field::Function: | |||
| 			os << context.funcname; | |||
| 			break; | |||
| 		case Field::LoggerName: | |||
| 			os << context.logger->getName(); | |||
| 			break; | |||
| 		case Field::Message: | |||
| 			os << msg; | |||
| 			break; | |||
| 		case Field::LogLevel: | |||
| 			os << context.level; | |||
| 			break; | |||
| 		case Field::WallTime: | |||
| 			auto runtime = context.timestamp - detail::GetStartTime(); | |||
| 			os << std::chrono::duration_cast<std::chrono::milliseconds>(runtime).count(); | |||
| 			break; | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void TimeStr::stream(std::ostream& os, EntryContext const& context, std::string const&) const { | |||
| 	auto time = std::chrono::system_clock::to_time_t(context.timestamp); | |||
| 	auto timeinfo = localtime (&time); | |||
| #if __GNUC__ >= 5 || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 7) || _MSC_VER >= 1700 | |||
| //TODO: Need better way to detect thing | |||
| 	os << std::put_time(timeinfo, formatStr.c_str()); | |||
| #else | |||
| 	char buffer[1024]; | |||
| 	if (strftime(buffer, 1024, formatStr.c_str(), timeinfo)) { | |||
| 		os << buffer; | |||
| 	} | |||
| #endif | |||
| } | |||
| 
 | |||
| template<typename ... Formatters> | |||
| inline std::string TemplateFormatter<Formatters...>::format(EntryContext const& context, std::string const& msg) const { | |||
| 	std::stringstream stream; | |||
| 
 | |||
| 	formatTuple<0>(context, msg, stream); | |||
| 
 | |||
| 	return stream.str(); | |||
| } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,123 @@ | |||
| /** | |||
|  * @file logger.h | |||
|  * | |||
|  * Defines the base Logger class | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <vector> | |||
| #include <algorithm> | |||
| #include <map> | |||
| #include <sstream> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| namespace detail { | |||
| 	/** | |||
| 	 * Internal function to get all configured loggers. Should not be used | |||
| 	 * directly, see Logger::getLogger() | |||
| 	 */ | |||
| 	static inline std::map<std::string, LogPtr>& GetLoggers() { | |||
| 		static std::map<std::string, LogPtr> loggers; | |||
| 		return loggers; | |||
| 	} | |||
| } | |||
| 
 | |||
| inline LogStream::~LogStream() { | |||
| 	if (level != LogLevel::OFF) { | |||
| 		logger.log(level, stream.str(), context); | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void Logger::logEntry(EntryContext const& context, std::string const& msg) { | |||
| 	for(auto& sink: sinks) { | |||
| 		sink->log(context, msg); | |||
| 	} | |||
| 	if (additive && parent) { | |||
| 		parent->logEntry(context, msg); | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void Logger::removeSink(SinkPtr sink) { | |||
| 	std::vector<SinkPtr>::iterator pos = std::find(sinks.begin(), sinks.end(), sink); | |||
| 	if (pos != sinks.end()) { | |||
| 		sinks.erase(pos); | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void Logger::log(LogLevel level, std::string const& msg, EntryContext context) { | |||
| 	if (level < getLevel()) { | |||
| 		return; | |||
| 	} | |||
| 
 | |||
| 	context.level = level; | |||
| 	context.logger = this; | |||
| 	logEntry(context, msg); | |||
| } | |||
| 
 | |||
| inline LogStream Logger::log(LogLevel level, EntryContext context) { | |||
| 	if (level < getLevel()) { | |||
| 		// Effectively disables the stream | |||
| 		return LogStream(*this, LogLevel::OFF, context); | |||
| 	} else { | |||
| 		return LogStream(*this, level, context); | |||
| 	} | |||
| } | |||
| 
 | |||
| inline void Logger::initialize() { | |||
| 	// Setup root logger | |||
| 	getRootLogger(); | |||
| 	// Set wall time | |||
| 	Formatter::initialize(); | |||
| } | |||
| 
 | |||
| inline void Logger::deinitialize() { | |||
| 	detail::GetLoggers().clear(); | |||
| 	getRootLogger()->sinks.clear(); | |||
| } | |||
| 
 | |||
| inline LogPtr Logger::getRootLogger() { | |||
| 	static LogPtr rootLogger = LogPtr(new Logger()); | |||
| 	return rootLogger; | |||
| } | |||
| 
 | |||
| inline LogPtr Logger::getLogger(std::string name) { | |||
| 	if (name.size() == 0) { | |||
| 		// Root logger | |||
| 		return getRootLogger(); | |||
| 	} | |||
| 	auto& loggers = detail::GetLoggers(); | |||
| 	auto it = loggers.find(name); | |||
| 	if (it != loggers.end()) { | |||
| 		return it->second; | |||
| 	} else { | |||
| 		auto n = name.rfind('.'); | |||
| 		LogPtr parent; | |||
| 		if (n == std::string::npos) { | |||
| 			parent = getRootLogger(); | |||
| 		} else{ | |||
| 			parent = getLogger(name.substr(0, n)); | |||
| 		} | |||
| 		LogPtr newLogger = LogPtr(new Logger(name, parent)); | |||
| 		loggers.emplace(name, newLogger); | |||
| 		return newLogger; | |||
| 	} | |||
| } | |||
| 
 | |||
| template<typename T> | |||
| inline LogStream const& operator<<(LogStream const& stream, T const& val) { | |||
| 	if (stream.level != LogLevel::OFF) { | |||
| 		stream.stream << val; | |||
| 	} | |||
| 	return stream; | |||
| } | |||
| 
 | |||
| inline LogStream const& operator<<(LogStream const& stream, std::ostream& (*F)(std::ostream&)) { | |||
| 	if (stream.level != LogLevel::OFF) { | |||
| 		stream.stream << F; | |||
| 	} | |||
| 	return stream; | |||
| } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| /** | |||
|  * @file logging.h | |||
|  * | |||
|  * Implementation of general logging functionality | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <ostream> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| /** | |||
|  * Streaming operator for LogLevel. | |||
|  * @param os Output stream. | |||
|  * @param level LogLevel. | |||
|  * @return os. | |||
|  */ | |||
| inline std::ostream& operator<<(std::ostream& os, LogLevel level) { | |||
| 	switch (level) { | |||
| 		case LogLevel::TRACE:   return os << "TRACE"; | |||
| 		case LogLevel::DEBUG:   return os << "DEBUG"; | |||
| 		case LogLevel::INFO:    return os << "INFO"; | |||
| 		case LogLevel::WARN:    return os << "WARN"; | |||
| 		case LogLevel::ERR:     return os << "ERROR"; | |||
| 		case LogLevel::FATAL:   return os << "FATAL"; | |||
| 		case LogLevel::OFF:     return os << "OFF"; | |||
| 		default:                return os << "???"; | |||
| 	} | |||
| } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| /** | |||
|  * @file sink.h | |||
|  * | |||
|  * Implementation for Sinks | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| inline void Sink::log(EntryContext const& context, std::string const& message) const { | |||
| 	if (context.level >= this->level) { | |||
| 		logEntry((*formatter)(context, message)); | |||
| 	} | |||
| } | |||
| 
 | |||
| } | |||
| 
 | |||
| @ -0,0 +1,165 @@ | |||
| /** | |||
|  * @file l3pp.h | |||
|  * @author Gereon Kremer <gereon.kremer@cs.rwth-aachen.de> | |||
|  * @author Harold Bruintjes <h.bruintjes@cs.rwth-aachen.de> | |||
|  * | |||
|  * The lightweight logging library for C++. | |||
|  * | |||
|  * This logging facility is fairly generic and is used as a simple and | |||
|  * header-only alternative to more advanced solutions like log4cplus or | |||
|  * boost::log. | |||
|  * | |||
|  * The basic components are Sinks, Formatters and Loggers. | |||
|  * | |||
|  * A Sink represents a logging output like a terminal or a log file. | |||
|  * This implementation provides a FileSink and a StreamSink, but the basic | |||
|  * Sink class can be extended as necessary. | |||
|  * | |||
|  * A Formatter is associated with a Sink and produces the actual string that is | |||
|  * sent to the Sink. | |||
|  * Usually, it adds auxiliary information like the current time, LogLevel and | |||
|  * source location to the string logged by the user. | |||
|  * The Formatter implements a reasonable default behavior for simple logging, | |||
|  * but it can be subclassed and modified as necessary. | |||
|  * | |||
|  * The Logger class finally plugs all these components together. | |||
|  * Individual loggers can log to one or more sinks (inheritable) and are | |||
|  * associated with an (inheritable) log level. | |||
|  * | |||
|  * Initial configuration may look like this: | |||
|  * @code{.cpp} | |||
|  * l3pp::Logger::initialize(); | |||
|  * l3pp::SinkPtr sink = l3pp::StreamSink::create(std::clog); | |||
|  * l3pp::Logger::getRootLogger()->addSink(sink); | |||
|  * l3pp::Logger::getRootLogger()->setLevel(l3pp::LogLevel::INFO); | |||
|  * @endcode | |||
|  *  | |||
|  * Macro facilitate the usage: | |||
|  * <ul> | |||
|  * <li>`L3PP_LOG_<LVL>(logger, msg)` produces a normal log message where | |||
|  * logger should be string identifying the logger (or a LogPtr) and msg is the | |||
|  * message to be logged.</li> | |||
|  * </ul> | |||
|  * Any message (`msg`) can be an arbitrary expression that one would | |||
|  * stream to an `std::ostream` like `stream << (msg);`. The default formatter | |||
|  * adds newlines. | |||
|  * Manipulators are generally supported. However, for performance avoid std::endl | |||
|  * and use '\n' directly. | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <chrono> | |||
| #include <memory> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| /** | |||
|  * Indicated which log messages should be forwarded to some sink. | |||
|  *  | |||
|  * All messages which have a level that is equal or greater than the specified | |||
|  * value will be forwarded. | |||
|  */ | |||
| enum class LogLevel { | |||
| 	/// Log messages used for tracing the program flow in detail. | |||
| 	TRACE, | |||
| 	/// Log messages used for debugging. | |||
| 	DEBUG, | |||
| 	/// Log messages used for information. | |||
| 	INFO, | |||
| 	/// Log messages used to warn about an undesired state. | |||
| 	WARN, | |||
| 	/// Log messages used for errors that can be handled. | |||
| 	ERR, | |||
| 	/// Log messages used for errors that lead to program termination. | |||
| 	FATAL, | |||
| 	/// Log no messages. | |||
| 	OFF, | |||
| 	/// Parent level | |||
| 	INHERIT, | |||
| 	/// Default log level. | |||
| 	DEFAULT = WARN, | |||
| 	/// All log messages. | |||
| 	ALL = TRACE | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Streaming operator for LogLevel. | |||
|  * @param os Output stream. | |||
|  * @param level LogLevel. | |||
|  * @return os. | |||
|  */ | |||
| inline std::ostream& operator<<(std::ostream& os, LogLevel level); | |||
| 
 | |||
| class Logger; | |||
| 
 | |||
| /** | |||
|  * Contextual information for a new log entry, contains such this as location, | |||
|  * log info (level, logger) and the time of the event. | |||
|  * A context will be created automatically by using the macros | |||
|  */ | |||
| struct EntryContext { | |||
| 	// Program location | |||
| 	const char* filename; | |||
| 	size_t line; | |||
| 	const char* funcname; | |||
| 
 | |||
| 	// Time of entry | |||
| 	std::chrono::system_clock::time_point timestamp; | |||
| 
 | |||
| 	// Log event info | |||
| 	Logger const* logger; | |||
| 	LogLevel level; | |||
| 
 | |||
| 	EntryContext(const char* filename, size_t line, const char* funcname) : | |||
| 		filename(filename), line(line), funcname(funcname), | |||
| 		timestamp(std::chrono::system_clock::now()), logger(nullptr), | |||
| 		level(LogLevel::OFF) | |||
| 	{ | |||
| 	} | |||
| 
 | |||
| 	EntryContext() : | |||
| 		filename(""), line(0), funcname(""), | |||
| 		timestamp(std::chrono::system_clock::now()), logger(nullptr), | |||
| 		level(LogLevel::OFF) | |||
| 	{ | |||
| 	} | |||
| }; | |||
| 
 | |||
| } | |||
| 
 | |||
| #include "formatter.h" | |||
| #include "sink.h" | |||
| #include "logger.h" | |||
| 
 | |||
| #include "impl/logging.h" | |||
| #include "impl/logger.h" | |||
| #include "impl/formatter.h" | |||
| #include "impl/sink.h" | |||
| 
 | |||
| #ifdef _MSC_VER | |||
| #define __func__ __FUNCTION__ | |||
| #endif | |||
| 
 | |||
| /// Create a record info. | |||
| #define __L3PP_LOG_RECORD l3pp::EntryContext(__FILE__, __LINE__, __func__) | |||
| /// Basic logging macro. | |||
| #define __L3PP_LOG(level, channel, expr) do { \ | |||
|     auto L3PP_channel = ::l3pp::Logger::getLogger(channel); \ | |||
|     if (L3PP_channel->getLevel() <= level) { \ | |||
|         L3PP_channel->log(level, __L3PP_LOG_RECORD) << expr; \ | |||
|     } \ | |||
| } while(false) | |||
| 
 | |||
| /// Log with level TRACE. | |||
| #define L3PP_LOG_TRACE(channel, expr) __L3PP_LOG(::l3pp::LogLevel::TRACE, channel, expr) | |||
| /// Log with level DEBUG. | |||
| #define L3PP_LOG_DEBUG(channel, expr) __L3PP_LOG(::l3pp::LogLevel::DEBUG, channel, expr) | |||
| /// Log with level INFO. | |||
| #define L3PP_LOG_INFO(channel, expr) __L3PP_LOG(::l3pp::LogLevel::INFO, channel, expr) | |||
| /// Log with level WARN. | |||
| #define L3PP_LOG_WARN(channel, expr) __L3PP_LOG(::l3pp::LogLevel::WARN, channel, expr) | |||
| /// Log with level ERROR. | |||
| #define L3PP_LOG_ERROR(channel, expr) __L3PP_LOG(::l3pp::LogLevel::ERR, channel, expr) | |||
| /// Log with level FATAL. | |||
| #define L3PP_LOG_FATAL(channel, expr) __L3PP_LOG(::l3pp::LogLevel::FATAL, channel, expr) | |||
| @ -0,0 +1,179 @@ | |||
| /** | |||
|  * @file logger.h | |||
|  * | |||
|  * Defines the base Logger class | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <vector> | |||
| #include <sstream> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| /** | |||
|  * LogStream is a logger object that can be streamed into, writing an entry | |||
|  * to the logger associated upon destruction. Instances of this classer are | |||
|  * returned by Logger log() functions, so they can be used as such: | |||
|  * logger->debug() << "Message"; | |||
|  */ | |||
| class LogStream { | |||
| 	friend class Logger; | |||
| 
 | |||
| 	Logger& logger; | |||
| 	LogLevel level; | |||
| 	EntryContext context; | |||
| 	mutable std::ostringstream stream; | |||
| 
 | |||
| 	LogStream(Logger& logger, LogLevel level, EntryContext context) : | |||
| 		logger(logger), level(level), context(context) | |||
| 	{ | |||
| 	} | |||
| 
 | |||
| 	LogStream(const LogStream&) = delete; | |||
| 	LogStream& operator=(const LogStream&) = delete; | |||
| public: | |||
| 	LogStream(LogStream&& other) : | |||
| 		logger(other.logger), level(other.level), context(std::move(other.context))/*, | |||
| 		stream(std::move(other.stream))*/ | |||
| 	{ | |||
| 		stream.str(other.stream.str()); | |||
| 	} | |||
| 	~LogStream(); | |||
| 
 | |||
| 	template<typename T> | |||
| 	friend LogStream const& operator<<(LogStream const& stream, T const& val); | |||
| 	friend LogStream const& operator<<(LogStream const& stream, std::ostream& (*F)(std::ostream&)); | |||
| }; | |||
| 
 | |||
| /** | |||
|  * Main logger class. Keeps track of all Logger instances, and can be used to | |||
|  * log various messages. Before the logging library is used, make sure to | |||
|  * call Logger::initialize(). Loggers are hierarchically nested, by means of | |||
|  * names separated by a period. All loggers are a (indirect) child of the root | |||
|  * logger, see Logger::getRootLogger() and Logger::getLogger(). | |||
|  * A logger is associated with a LogLevel. Any entry with a level below this | |||
|  * level will be filtered out. A LogLevel of INHERIT means the parent log | |||
|  * level will be compared against instead. | |||
|  * A logger can be associated with 1 or more Sinks. A log entry is printed to | |||
|  * each associated sink. If the Logger is set additive (see getAdditive(), | |||
|  * setAdditive()) parent sinks are logged to as well (by default true). | |||
|  * Logging can be performed either as a single string message, or by using a | |||
|  * stream. The latter requires the end() method to be called before the entry | |||
|  * is logged. For convenience, various logging macros are defined at the end | |||
|  * of this header. | |||
|  */ | |||
| class Logger { | |||
| 	friend class Formatter; | |||
| 
 | |||
| 	typedef std::shared_ptr<Logger> LogPtr; | |||
| 
 | |||
| 	LogPtr parent; | |||
| 	std::string name; | |||
| 	LogLevel level; | |||
| 	std::vector<SinkPtr> sinks; | |||
| 	bool additive; | |||
| 
 | |||
| 	// Logger constructors are private | |||
| 	Logger() : parent(nullptr), name(""), level(LogLevel::DEFAULT), | |||
| 		additive(true) | |||
| 	{ | |||
| 
 | |||
| 	} | |||
| 
 | |||
| 	Logger(std::string const& name, LogPtr parent) : parent(parent), name(name), | |||
| 		level(LogLevel::INHERIT), additive(true) | |||
| 	{ | |||
| 	} | |||
| 
 | |||
| 	void logEntry(EntryContext const& context, std::string const& msg); | |||
| 
 | |||
| public: | |||
| 	void addSink(SinkPtr sink) { | |||
| 		sinks.push_back(sink); | |||
| 	} | |||
| 
 | |||
| 	void removeSink(SinkPtr sink); | |||
| 
 | |||
| 	void setLevel(LogLevel level) { | |||
| 		if (level == LogLevel::INHERIT && !parent) { | |||
| 			return; | |||
| 		} | |||
| 		this->level = level; | |||
| 	} | |||
| 
 | |||
| 	LogLevel getLevel() const { | |||
| 		if (level == LogLevel::INHERIT) { | |||
| 			return parent->getLevel(); | |||
| 		} | |||
| 		return level; | |||
| 	} | |||
| 
 | |||
| 	std::string const& getName() const { | |||
| 		return name; | |||
| 	} | |||
| 
 | |||
| 	bool getAdditive() const { | |||
| 		return additive; | |||
| 	} | |||
| 
 | |||
| 	void setAdditive(bool additive) { | |||
| 		this->additive = additive; | |||
| 	} | |||
| 
 | |||
| 	void log(LogLevel level, std::string const& msg, EntryContext context = EntryContext()); | |||
| 
 | |||
| 	void trace(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::TRACE, msg, context); | |||
| 	} | |||
| 	void debug(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::DEBUG, msg, context); | |||
| 	} | |||
| 	void info(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::INFO, msg, context); | |||
| 	} | |||
| 	void warn(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::WARN, msg, context); | |||
| 	} | |||
| 	void error(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::ERR, msg, context); | |||
| 	} | |||
| 	void fatal(std::string const& msg, EntryContext context = EntryContext()) { | |||
| 		log(LogLevel::FATAL, msg, context); | |||
| 	} | |||
| 
 | |||
| 	LogStream log(LogLevel level, EntryContext context = EntryContext()); | |||
| 
 | |||
| 	LogStream trace(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::TRACE, context); | |||
| 	} | |||
| 	LogStream debug(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::DEBUG, context); | |||
| 	} | |||
| 	LogStream info(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::INFO, context); | |||
| 	} | |||
| 	LogStream warn(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::WARN, context); | |||
| 	} | |||
| 	LogStream error(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::ERR, context); | |||
| 	} | |||
| 	LogStream fatal(EntryContext context = EntryContext()) { | |||
| 		return log(LogLevel::FATAL, context); | |||
| 	} | |||
| 
 | |||
| 	static void initialize(); | |||
| 	static void deinitialize(); | |||
| 
 | |||
| 	static LogPtr getRootLogger(); | |||
| 
 | |||
| 	static LogPtr getLogger(LogPtr logger) { | |||
| 		return logger; | |||
| 	} | |||
| 
 | |||
| 	static LogPtr getLogger(std::string name); | |||
| }; | |||
| typedef std::shared_ptr<Logger> LogPtr; | |||
| 
 | |||
| } | |||
| @ -0,0 +1,108 @@ | |||
| /** | |||
|  * @file sink.h | |||
|  * | |||
|  * Defines classes for log sinks (i.e. outputs) | |||
|  */ | |||
| 
 | |||
| #pragma once | |||
| 
 | |||
| #include <ostream> | |||
| #include <fstream> | |||
| 
 | |||
| namespace l3pp { | |||
| 
 | |||
| /** | |||
|  * Base class for a logging sink. It can only log some log entry to which some | |||
|  * formatting is applied (see Formatter). A Sink may be given a log level, | |||
|  * which filters out all entries below that level. By default is logs all | |||
|  * entries. | |||
|  */ | |||
| class Sink { | |||
| 	LogLevel level; | |||
| 	FormatterPtr formatter; | |||
| 
 | |||
| 	virtual void logEntry(std::string const& entry) const = 0; | |||
| public: | |||
| 	Sink() : level(LogLevel::ALL), formatter(std::make_shared<Formatter>()) { | |||
| 
 | |||
| 	} | |||
| 	Sink(FormatterPtr formatter) : level(LogLevel::ALL), formatter(formatter) { | |||
| 
 | |||
| 	} | |||
| 	/** | |||
| 	 * Default destructor. | |||
|      */ | |||
| 	virtual ~Sink() {} | |||
| 
 | |||
| 	LogLevel getLevel() const { | |||
| 		return level; | |||
| 	} | |||
| 
 | |||
| 	void setLevel(LogLevel level) { | |||
| 		this->level = level; | |||
| 	} | |||
| 
 | |||
| 	FormatterPtr getFormatter() const { | |||
| 		return formatter; | |||
| 	} | |||
| 
 | |||
| 	void setFormatter(FormatterPtr formatter) { | |||
| 		this->formatter = formatter; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * Logs the given message with context info | |||
| 	 */ | |||
| 	void log(EntryContext const& context, std::string const& message) const; | |||
| }; | |||
| typedef std::shared_ptr<Sink> SinkPtr; | |||
| 
 | |||
| /** | |||
|  * Logging sink that wraps an arbitrary `std::ostream`. | |||
|  * It is meant to be used for streams like `std::cout` or `std::cerr`. | |||
|  */ | |||
| class StreamSink: public Sink { | |||
| 	/// Output stream. | |||
| 	mutable std::ostream os; | |||
| 
 | |||
| 	void logEntry(std::string const& entry) const override { | |||
| 		os << entry << std::flush; | |||
| 	} | |||
| 
 | |||
| 	explicit StreamSink(std::ostream& _os) : | |||
| 		os(_os.rdbuf()) {} | |||
| public: | |||
| 	/** | |||
| 	 * Create a StreamSink from some output stream. | |||
|      * @param os Output stream. | |||
|      */ | |||
| 	static SinkPtr create(std::ostream& os) { | |||
| 		return SinkPtr(new StreamSink(os)); | |||
| 	} | |||
| }; | |||
| /** | |||
|  * Logging sink for file output. | |||
|  */ | |||
| class FileSink: public Sink { | |||
| 	/// File output stream. | |||
| 	mutable std::ofstream os; | |||
| 
 | |||
| 	void logEntry(std::string const& entry) const override { | |||
| 		os << entry << std::flush; | |||
| 	} | |||
| 
 | |||
| 	explicit FileSink(const std::string& filename) : | |||
| 		os(filename, std::ios::out) {} | |||
| public: | |||
| 	/** | |||
| 	 * Create a FileSink that logs to the specified file. | |||
| 	 * The file is truncated upon construction. | |||
|      * @param filename | |||
|      */ | |||
| 	static SinkPtr create(const std::string& filename) { | |||
| 		return SinkPtr(new FileSink(filename)); | |||
| 	} | |||
| }; | |||
| 
 | |||
| } | |||
| 
 | |||
| @ -0,0 +1,23 @@ | |||
| #ifndef STORM_UTILITY_LOGGING_H_ | |||
| #define STORM_UTILITY_LOGGING_H_ | |||
| 
 | |||
| #include <l3pp.h> | |||
| 
 | |||
| #if !defined(STORM_LOG_DISABLE_DEBUG) && !defined(STORM_LOG_DISABLE_TRACE) | |||
| #define STORM_LOG_TRACE(message) L3PP_LOG_TRACE(l3pp::Logger::getRootLogger(), message) | |||
| #else | |||
| #define STORM_LOG_TRACE(message) (void)(0) | |||
| #endif | |||
| 
 | |||
| #if !defined(STORM_LOG_DISABLE_DEBUG) | |||
| #define STORM_LOG_DEBUG(message) L3PP_LOG_DEBUG(l3pp::Logger::getRootLogger(), message) | |||
| #else | |||
| #define STORM_LOG_DEBUG(message) (void)(0) | |||
| #endif | |||
| 
 | |||
| // Define STORM_LOG_WARN, STORM_LOG_ERROR and STORM_LOG_INFO to log the given message with the corresponding log levels. | |||
| #define STORM_LOG_INFO(message) L3PP_LOG_INFO(l3pp::Logger::getRootLogger(), message) | |||
| #define STORM_LOG_WARN(message) L3PP_LOG_WARN(l3pp::Logger::getRootLogger(), message) | |||
| #define STORM_LOG_ERROR(message) L3PP_LOG_ERROR(l3pp::Logger::getRootLogger(), message) | |||
| 
 | |||
| #endif /* STORM_UTILITY_LOGGING_H_ */ | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue