1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
|
/*
* CLogger.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../CConsoleHandler.h"
#include "../filesystem/FileStream.h"
class CLogger;
struct LogRecord;
class ILogTarget;
namespace ELogLevel
{
#ifdef VCMI_ANDROID
int toAndroid(ELogLevel logLevel);
#endif
}
/// The class CLoggerDomain provides convenient access to super domains from a sub domain.
class DLL_LINKAGE CLoggerDomain
{
public:
/// Constructs a CLoggerDomain with the domain designated by name.
/// Sub-domains can be specified by separating domains by a dot, e.g. "ai.battle". The global domain is named "global".
explicit CLoggerDomain(std::string name);
const std::string& getName() const;
CLoggerDomain getParent() const;
bool isGlobalDomain() const;
static const std::string DOMAIN_GLOBAL;
private:
std::string name;
};
/// The class CLoggerStream provides a stream-like way of logging messages.
class DLL_LINKAGE CLoggerStream
{
public:
CLoggerStream(const CLogger & logger, ELogLevel::ELogLevel level);
~CLoggerStream();
template<typename T>
CLoggerStream & operator<<(const T & data)
{
if(!sbuffer)
sbuffer = new std::stringstream(std::ios_base::out);
(*sbuffer) << data;
return *this;
}
private:
const CLogger & logger;
ELogLevel::ELogLevel level;
std::stringstream * sbuffer;
};
/// The logger is used to log messages to certain targets of a specific domain/name.
/// It is thread-safe and can be used concurrently by several threads.
class DLL_LINKAGE CLogger: public vstd::CLoggerBase
{
public:
ELogLevel::ELogLevel getLevel() const;
void setLevel(ELogLevel::ELogLevel level);
const CLoggerDomain & getDomain() const;
/// Logger access methods
static CLogger * getLogger(const CLoggerDomain & domain);
static CLogger * getGlobalLogger();
/// Log streams for various log levels
CLoggerStream traceStream() const;
CLoggerStream debugStream() const;
CLoggerStream infoStream() const;
CLoggerStream warnStream() const;
CLoggerStream errorStream() const;
void log(ELogLevel::ELogLevel level, const std::string & message) const override;
void addTarget(std::unique_ptr<ILogTarget> && target);
void clearTargets();
/// Returns true if a debug/trace log message will be logged, false if not.
/// Useful if performance is important and concatenating the log message is a expensive task.
bool isDebugEnabled() const;
bool isTraceEnabled() const;
private:
explicit CLogger(const CLoggerDomain & domain);
inline ELogLevel::ELogLevel getEffectiveLevel() const; /// Returns the log level applied on this logger whether directly or indirectly.
inline void callTargets(const LogRecord & record) const;
CLoggerDomain domain;
CLogger * parent;
ELogLevel::ELogLevel level;
std::vector<std::unique_ptr<ILogTarget> > targets;
mutable boost::mutex mx;
static boost::recursive_mutex smx;
};
extern DLL_LINKAGE CLogger * logGlobal;
extern DLL_LINKAGE CLogger * logBonus;
extern DLL_LINKAGE CLogger * logNetwork;
extern DLL_LINKAGE CLogger * logAi;
extern DLL_LINKAGE CLogger * logAnim;
/// RAII class for tracing the program execution.
/// It prints "Leaving function XYZ" automatically when the object gets destructed.
class DLL_LINKAGE CTraceLogger : boost::noncopyable
{
public:
CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage);
~CTraceLogger();
private:
const CLogger * logger;
std::string endMessage;
};
/// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be
/// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled.
///
#define RAII_TRACE(logger, onEntry, onLeave) \
std::unique_ptr<CTraceLogger> ctl00; \
if(logger->isTraceEnabled()) \
ctl00 = make_unique<CTraceLogger>(logger, onEntry, onLeave);
#define LOG_TRACE(logger) RAII_TRACE(logger, \
boost::str(boost::format("Entering %s.") % BOOST_CURRENT_FUNCTION), \
boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
#define LOG_TRACE_PARAMS(logger, formatStr, params) RAII_TRACE(logger, \
boost::str(boost::format("Entering %s: " + std::string(formatStr) + ".") % BOOST_CURRENT_FUNCTION % params), \
boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
/* ---------------------------------------------------------------------------- */
/* Implementation/Detail classes, Private API */
/* ---------------------------------------------------------------------------- */
/// The class CLogManager is a global storage for logger objects.
class DLL_LINKAGE CLogManager : public boost::noncopyable
{
public:
static CLogManager & get();
void addLogger(CLogger * logger);
CLogger * getLogger(const CLoggerDomain & domain); /// Returns a logger or nullptr if no one is registered for the given domain.
std::vector<std::string> getRegisteredDomains() const;
private:
CLogManager();
virtual ~CLogManager();
std::map<std::string, CLogger *> loggers;
mutable boost::mutex mx;
static boost::recursive_mutex smx;
};
/// The struct LogRecord holds the log message and additional logging information.
struct DLL_LINKAGE LogRecord
{
LogRecord(const CLoggerDomain & domain, ELogLevel::ELogLevel level, const std::string & message)
: domain(domain),
level(level),
message(message),
timeStamp(boost::posix_time::microsec_clock::local_time()),
threadId(boost::lexical_cast<std::string>(boost::this_thread::get_id())) { }
CLoggerDomain domain;
ELogLevel::ELogLevel level;
std::string message;
boost::posix_time::ptime timeStamp;
std::string threadId;
};
/// The class CLogFormatter formats log records.
///
/// There are several pattern characters which can be used to format a log record:
/// %d = Date/Time
/// %l = Log level
/// %n = Logger name
/// %t = Thread ID
/// %m = Message
class DLL_LINKAGE CLogFormatter
{
public:
CLogFormatter();
CLogFormatter(const CLogFormatter & copy);
CLogFormatter(CLogFormatter && move);
CLogFormatter(const std::string & pattern);
CLogFormatter & operator=(const CLogFormatter & copy);
CLogFormatter & operator=(CLogFormatter && move);
void setPattern(const std::string & pattern);
void setPattern(std::string && pattern);
const std::string & getPattern() const;
std::string format(const LogRecord & record) const;
private:
std::string pattern;
mutable std::stringstream dateStream;
};
/// The interface ILogTarget is used by all log target implementations. It holds
/// the abstract method write which sub-classes should implement.
class DLL_LINKAGE ILogTarget : public boost::noncopyable
{
public:
virtual ~ILogTarget() { };
virtual void write(const LogRecord & record) = 0;
};
/// The class CColorMapping maps a logger name and a level to a specific color. Supports domain inheritance.
class DLL_LINKAGE CColorMapping
{
public:
CColorMapping();
void setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor::EConsoleTextColor color);
EConsoleTextColor::EConsoleTextColor getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const;
private:
std::map<std::string, std::map<ELogLevel::ELogLevel, EConsoleTextColor::EConsoleTextColor> > map;
};
/// This target is a logging target which writes message to the console.
/// The target may be shared among multiple loggers. All methods except write aren't thread-safe.
/// The console target is intended to be configured once and then added to a logger.
class DLL_LINKAGE CLogConsoleTarget : public ILogTarget
{
public:
explicit CLogConsoleTarget(CConsoleHandler * console);
bool isColoredOutputEnabled() const;
void setColoredOutputEnabled(bool coloredOutputEnabled);
ELogLevel::ELogLevel getThreshold() const;
void setThreshold(ELogLevel::ELogLevel threshold);
const CLogFormatter & getFormatter() const;
void setFormatter(const CLogFormatter & formatter);
const CColorMapping & getColorMapping() const;
void setColorMapping(const CColorMapping & colorMapping);
void write(const LogRecord & record) override;
private:
CConsoleHandler * console;
ELogLevel::ELogLevel threshold;
bool coloredOutputEnabled;
CLogFormatter formatter;
CColorMapping colorMapping;
mutable boost::mutex mx;
};
/// This target is a logging target which writes messages to a log file.
/// The target may be shared among multiple loggers. All methods except write aren't thread-safe.
/// The file target is intended to be configured once and then added to a logger.
class DLL_LINKAGE CLogFileTarget : public ILogTarget
{
public:
/// Constructs a CLogFileTarget and opens the file designated by filePath. If the append parameter is true, the file
/// will be appended to. Otherwise the file designated by filePath will be truncated before being opened.
explicit CLogFileTarget(boost::filesystem::path filePath, bool append = true);
const CLogFormatter & getFormatter() const;
void setFormatter(const CLogFormatter & formatter);
void write(const LogRecord & record) override;
private:
FileStream file;
CLogFormatter formatter;
mutable boost::mutex mx;
};
|