File: CLogger.h

package info (click to toggle)
vcmi 0.99%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: stretch
  • size: 10,264 kB
  • ctags: 16,826
  • sloc: cpp: 121,945; objc: 248; sh: 193; makefile: 28; python: 13; ansic: 9
file content (294 lines) | stat: -rw-r--r-- 9,258 bytes parent folder | download
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;
};