sjunges
9 years ago
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
-
4src/settings/modules/DebugSettings.cpp
-
43src/utility/initialize.cpp
-
7src/utility/initialize.h
-
23src/utility/logging.h
-
140src/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)); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
@ -1,30 +1,55 @@ |
|||
#include "initialize.h"
|
|||
|
|||
#include "src/utility/macros.h"
|
|||
#include "src/settings/SettingsManager.h"
|
|||
#include "src/settings/modules/DebugSettings.h"
|
|||
|
|||
int storm_runtime_loglevel = STORM_LOGLEVEL_WARN; |
|||
#include <iostream>
|
|||
#include <fstream>
|
|||
|
|||
namespace storm { |
|||
namespace utility { |
|||
|
|||
|
|||
void initializeLogger() { |
|||
// Intentionally left empty.
|
|||
l3pp::Logger::initialize(); |
|||
// By default output to std::cout
|
|||
l3pp::SinkPtr sink = l3pp::StreamSink::create(std::cout); |
|||
l3pp::Logger::getRootLogger()->addSink(sink); |
|||
// Default to warn, set by user to something else
|
|||
l3pp::Logger::getRootLogger()->setLevel(l3pp::LogLevel::WARN); |
|||
|
|||
l3pp::FormatterPtr fptr = l3pp::makeTemplateFormatter( |
|||
l3pp::FieldStr<l3pp::Field::LogLevel, 5, l3pp::Justification::LEFT>(), |
|||
" (", l3pp::FieldStr<l3pp::Field::FileName>(), ':', l3pp::FieldStr<l3pp::Field::Line>(), "): ", |
|||
l3pp::FieldStr<l3pp::Field::Message>(), '\n' |
|||
); |
|||
sink->setFormatter(fptr); |
|||
} |
|||
|
|||
|
|||
void setUp() { |
|||
initializeLogger(); |
|||
std::cout.precision(10); |
|||
} |
|||
|
|||
|
|||
void cleanUp() { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
|
|||
void setLogLevel(l3pp::LogLevel level) { |
|||
l3pp::Logger::getRootLogger()->setLevel(level); |
|||
if (level <= l3pp::LogLevel::DEBUG) { |
|||
#if STORM_LOG_DISABLE_DEBUG
|
|||
std::cout << "***** warning ***** requested loglevel is not compiled\n"; |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
void initializeFileLogging() { |
|||
// FIXME.
|
|||
if (storm::settings::getModule<storm::settings::modules::DebugSettings>().isLogfileSet()) { |
|||
std::string logFileName = storm::settings::getModule<storm::settings::modules::DebugSettings>().getLogfilename(); |
|||
l3pp::SinkPtr sink = l3pp::FileSink::create(logFileName); |
|||
l3pp::Logger::getRootLogger()->addSink(sink); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -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