1 /*
2 * The Doomsday Engine Project -- libcore
3 *
4 * Copyright © 2004-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * LGPL: http://www.gnu.org/licenses/lgpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details. You should have received a copy of
16 * the GNU Lesser General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #ifndef LIBDENG2_LOG_H
21 #define LIBDENG2_LOG_H
22
23 #include "../Time"
24 #include "../String"
25 #include "../Lockable"
26 #include "../Guard"
27 #include "../ISerializable"
28
29 #include <QList>
30 #include <vector>
31 #include <string>
32 #include <cstdlib>
33
34 /// Macro for accessing the local log of the current thread.
35 #define LOG() de::Log::threadLog()
36
37 /// Macro for accessing the local log of the current thread and entering
38 /// a new log section.
39 #define LOG_AS(sectionName) \
40 de::Log::Section __logSection = de::Log::Section(sectionName);
41
42 /**
43 * Macro for accessing the local log of the current thread and entering
44 * a new log section with a de::String variable based name.
45 *
46 * @param str Anything out of which a de::String can be constructed.
47 *
48 * @note Log::Section doesn't own the strings passed in; we have to
49 * ensure that the string exists in memory as long as the section (scope)
50 * is valid.
51 */
52 #define LOG_AS_STRING(str) \
53 de::Block __logSectionUtf8 { de::String(str).toUtf8() }; \
54 LOG_AS(__logSectionUtf8.constData());
55
56 /*
57 * Macros that make a new log entry with a particular set of audience/level bits:
58 */
59
60 // High-verbosity log entries. Pre-check if log level is enabled, otherwise message
61 // is ignored.
62 #define LOG_PRECHECK_LEVEL(level, str, args) \
63 { if (de::LogBuffer::appBufferExists() && de::LogBuffer::get().isEnabled(level)) \
64 { LOG_AT_LEVEL(level, str) << args; } }
65
66 // End-user/game audience
67 #define LOG_AT_LEVEL(level, str) de::LogEntryStager(level, str)
68 #define LOG_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Generic | de::LogEntry::XVerbose, str, args)
69 #define LOG_VERBOSE(str) LOG_AT_LEVEL(de::LogEntry::Verbose, str)
70 #define LOG_MSG(str) LOG_AT_LEVEL(de::LogEntry::Message, str)
71 #define LOG_INFO(str) LOG_AT_LEVEL(de::LogEntry::Note, str) // backwards comp
72 #define LOG_NOTE(str) LOG_AT_LEVEL(de::LogEntry::Note, str)
73 #define LOG_WARNING(str) LOG_AT_LEVEL(de::LogEntry::Warning, str)
74 #define LOG_ERROR(str) LOG_AT_LEVEL(de::LogEntry::Error, str)
75 #define LOG_CRITICAL(str) LOG_AT_LEVEL(de::LogEntry::Critical, str)
76
77 // Native code developer audience (general domain)
78 #define LOGDEV_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | (level), str)
79 #define LOGDEV_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::Generic | de::LogEntry::XVerbose, str, args)
80 #define LOG_TRACE(str, args) LOGDEV_XVERBOSE(str, args) // backwards comp
81 #define LOGDEV_VERBOSE(str) LOGDEV_AT_LEVEL(de::LogEntry::Verbose, str)
82 #define LOG_DEBUG(str) LOGDEV_VERBOSE(str) // backwards comp
83 #define LOGDEV_MSG(str) LOGDEV_AT_LEVEL(de::LogEntry::Message, str)
84 #define LOGDEV_NOTE(str) LOGDEV_AT_LEVEL(de::LogEntry::Note, str)
85 #define LOGDEV_WARNING(str) LOGDEV_AT_LEVEL(de::LogEntry::Warning, str)
86 #define LOGDEV_ERROR(str) LOGDEV_AT_LEVEL(de::LogEntry::Error, str)
87
88 #define LOG_WIP(str) LOG_AT_LEVEL(de::LogEntry::Privileged | de::LogEntry::Note, str)
89
90 // Custom combination of audiences
91 #define LOG_XVERBOSE_TO(audflags, str, args) \
92 LOG_PRECHECK_LEVEL((audflags) | de::LogEntry::XVerbose, str, args)
93 #define LOG_VERBOSE_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Verbose, str)
94 #define LOG_MSG_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Message, str)
95 #define LOG_NOTE_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Note, str)
96 #define LOG_WARNING_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Warning, str)
97 #define LOG_ERROR_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Error, str)
98 #define LOG_CRITICAL_TO(audflags, str) LOG_AT_LEVEL((audflags) | de::LogEntry::Critical, str)
99
100 // Resource domain
101 #define LOG_RES_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Resource | (level), str)
102 #define LOG_RES_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Resource | de::LogEntry::XVerbose, str, args)
103 #define LOG_RES_VERBOSE(str) LOG_RES_AT_LEVEL(de::LogEntry::Verbose, str)
104 #define LOG_RES_MSG(str) LOG_RES_AT_LEVEL(de::LogEntry::Message, str)
105 #define LOG_RES_NOTE(str) LOG_RES_AT_LEVEL(de::LogEntry::Note, str)
106 #define LOG_RES_WARNING(str) LOG_RES_AT_LEVEL(de::LogEntry::Warning, str)
107 #define LOG_RES_ERROR(str) LOG_RES_AT_LEVEL(de::LogEntry::Error, str)
108 #define LOG_RES_CRITICAL(str) LOG_RES_AT_LEVEL(de::LogEntry::Critical, str)
109 #define LOGDEV_RES_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Resource | (level), str)
110 #define LOGDEV_RES_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::Resource | de::LogEntry::XVerbose, str, args)
111 #define LOGDEV_RES_VERBOSE(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Verbose, str)
112 #define LOGDEV_RES_MSG(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Message, str)
113 #define LOGDEV_RES_NOTE(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Note, str)
114 #define LOGDEV_RES_WARNING(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Warning, str)
115 #define LOGDEV_RES_ERROR(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Error, str)
116 #define LOGDEV_RES_CRITICAL(str) LOGDEV_RES_AT_LEVEL(de::LogEntry::Critical, str)
117
118 // Map domain
119 #define LOG_MAP_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Map | (level), str)
120 #define LOG_MAP_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Map | de::LogEntry::XVerbose, str, args)
121 #define LOG_MAP_VERBOSE(str) LOG_MAP_AT_LEVEL(de::LogEntry::Verbose, str)
122 #define LOG_MAP_MSG(str) LOG_MAP_AT_LEVEL(de::LogEntry::Message, str)
123 #define LOG_MAP_NOTE(str) LOG_MAP_AT_LEVEL(de::LogEntry::Note, str)
124 #define LOG_MAP_WARNING(str) LOG_MAP_AT_LEVEL(de::LogEntry::Warning, str)
125 #define LOG_MAP_ERROR(str) LOG_MAP_AT_LEVEL(de::LogEntry::Error, str)
126 #define LOG_MAP_CRITICAL(str) LOG_MAP_AT_LEVEL(de::LogEntry::Critical, str)
127 #define LOGDEV_MAP_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Map | (level), str)
128 #define LOGDEV_MAP_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Map | de::LogEntry::Dev | de::LogEntry::XVerbose, str, args)
129 #define LOGDEV_MAP_VERBOSE(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Verbose, str)
130 #define LOGDEV_MAP_MSG(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Message, str)
131 #define LOGDEV_MAP_NOTE(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Note, str)
132 #define LOGDEV_MAP_WARNING(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Warning, str)
133 #define LOGDEV_MAP_ERROR(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Error, str)
134 #define LOGDEV_MAP_CRITICAL(str) LOGDEV_MAP_AT_LEVEL(de::LogEntry::Critical, str)
135
136 // Script domain
137 #define LOG_SCR_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Script | (level), str)
138 #define LOG_SCR_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Script | de::LogEntry::XVerbose, str, args)
139 #define LOG_SCR_VERBOSE(str) LOG_SCR_AT_LEVEL(de::LogEntry::Verbose, str)
140 #define LOG_SCR_MSG(str) LOG_SCR_AT_LEVEL(de::LogEntry::Message, str)
141 #define LOG_SCR_NOTE(str) LOG_SCR_AT_LEVEL(de::LogEntry::Note, str)
142 #define LOG_SCR_WARNING(str) LOG_SCR_AT_LEVEL(de::LogEntry::Warning, str)
143 #define LOG_SCR_ERROR(str) LOG_SCR_AT_LEVEL(de::LogEntry::Error, str)
144 #define LOG_SCR_CRITICAL(str) LOG_SCR_AT_LEVEL(de::LogEntry::Critical, str)
145 #define LOGDEV_SCR_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Script | (level), str)
146 #define LOGDEV_SCR_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::Script | de::LogEntry::XVerbose, str, args)
147 #define LOGDEV_SCR_VERBOSE(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Verbose, str)
148 #define LOGDEV_SCR_MSG(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Message, str)
149 #define LOGDEV_SCR_NOTE(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Note, str)
150 #define LOGDEV_SCR_WARNING(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Warning, str)
151 #define LOGDEV_SCR_ERROR(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Error, str)
152 #define LOGDEV_SCR_CRITICAL(str) LOGDEV_SCR_AT_LEVEL(de::LogEntry::Critical, str)
153
154 // Audio domain
155 #define LOG_AUDIO_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Audio | (level), str)
156 #define LOG_AUDIO_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Audio | de::LogEntry::XVerbose, str, args)
157 #define LOG_AUDIO_VERBOSE(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Verbose, str)
158 #define LOG_AUDIO_MSG(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Message, str)
159 #define LOG_AUDIO_NOTE(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Note, str)
160 #define LOG_AUDIO_WARNING(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Warning, str)
161 #define LOG_AUDIO_ERROR(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Error, str)
162 #define LOG_AUDIO_CRITICAL(str) LOG_AUDIO_AT_LEVEL(de::LogEntry::Critical, str)
163 #define LOGDEV_AUDIO_AT_LEVEL(level, str) \
164 LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Audio | (level), str)
165 #define LOGDEV_AUDIO_XVERBOSE(str, args) \
166 LOG_PRECHECK_LEVEL(de::LogEntry::Audio | de::LogEntry::Dev | de::LogEntry::XVerbose, str, args)
167 #define LOGDEV_AUDIO_VERBOSE(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Verbose, str)
168 #define LOGDEV_AUDIO_MSG(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Message, str)
169 #define LOGDEV_AUDIO_NOTE(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Note, str)
170 #define LOGDEV_AUDIO_WARNING(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Warning, str)
171 #define LOGDEV_AUDIO_ERROR(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Error, str)
172 #define LOGDEV_AUDIO_CRITICAL(str) LOGDEV_AUDIO_AT_LEVEL(de::LogEntry::Critical, str)
173
174 // Graphics domain
175 #define LOG_GL_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::GL | (level), str)
176 #define LOG_GL_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::GL | de::LogEntry::XVerbose, str, args)
177 #define LOG_GL_VERBOSE(str) LOG_GL_AT_LEVEL(de::LogEntry::Verbose, str)
178 #define LOG_GL_MSG(str) LOG_GL_AT_LEVEL(de::LogEntry::Message, str)
179 #define LOG_GL_NOTE(str) LOG_GL_AT_LEVEL(de::LogEntry::Note, str)
180 #define LOG_GL_WARNING(str) LOG_GL_AT_LEVEL(de::LogEntry::Warning, str)
181 #define LOG_GL_ERROR(str) LOG_GL_AT_LEVEL(de::LogEntry::Error, str)
182 #define LOG_GL_CRITICAL(str) LOG_GL_AT_LEVEL(de::LogEntry::Critical, str)
183 #define LOGDEV_GL_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::GL | (level), str)
184 #define LOGDEV_GL_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::GL | de::LogEntry::XVerbose, str, args)
185 #define LOGDEV_GL_VERBOSE(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Verbose, str)
186 #define LOGDEV_GL_MSG(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Message, str)
187 #define LOGDEV_GL_NOTE(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Note, str)
188 #define LOGDEV_GL_WARNING(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Warning, str)
189 #define LOGDEV_GL_ERROR(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Error, str)
190 #define LOGDEV_GL_CRITICAL(str) LOGDEV_GL_AT_LEVEL(de::LogEntry::Critical, str)
191
192 // Input domain
193 #define LOG_INPUT_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Input | (level), str)
194 #define LOG_INPUT_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Input | de::LogEntry::XVerbose, str, args)
195 #define LOG_INPUT_VERBOSE(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Verbose, str)
196 #define LOG_INPUT_MSG(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Message, str)
197 #define LOG_INPUT_NOTE(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Note, str)
198 #define LOG_INPUT_WARNING(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Warning, str)
199 #define LOG_INPUT_ERROR(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Error, str)
200 #define LOG_INPUT_CRITICAL(str) LOG_INPUT_AT_LEVEL(de::LogEntry::Critical, str)
201 #define LOGDEV_INPUT_AT_LEVEL(level, str) \
202 LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Input | (level), str)
203 #define LOGDEV_INPUT_XVERBOSE(str, args) \
204 LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::Input | de::LogEntry::XVerbose, str, args)
205 #define LOGDEV_INPUT_VERBOSE(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Verbose, str)
206 #define LOGDEV_INPUT_MSG(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Message, str)
207 #define LOGDEV_INPUT_NOTE(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Note, str)
208 #define LOGDEV_INPUT_WARNING(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Warning, str)
209 #define LOGDEV_INPUT_ERROR(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Error, str)
210 #define LOGDEV_INPUT_CRITICAL(str) LOGDEV_INPUT_AT_LEVEL(de::LogEntry::Critical, str)
211
212 // Network domain
213 #define LOG_NET_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Network | (level), str)
214 #define LOG_NET_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Network | de::LogEntry::XVerbose, str, args)
215 #define LOG_NET_VERBOSE(str) LOG_NET_AT_LEVEL(de::LogEntry::Verbose, str)
216 #define LOG_NET_MSG(str) LOG_NET_AT_LEVEL(de::LogEntry::Message, str)
217 #define LOG_NET_NOTE(str) LOG_NET_AT_LEVEL(de::LogEntry::Note, str)
218 #define LOG_NET_WARNING(str) LOG_NET_AT_LEVEL(de::LogEntry::Warning, str)
219 #define LOG_NET_ERROR(str) LOG_NET_AT_LEVEL(de::LogEntry::Error, str)
220 #define LOG_NET_CRITICAL(str) LOG_NET_AT_LEVEL(de::LogEntry::Critical, str)
221 #define LOGDEV_NET_AT_LEVEL(level, str) LOG_AT_LEVEL(de::LogEntry::Dev | de::LogEntry::Network | (level), str)
222 #define LOGDEV_NET_XVERBOSE(str, args) LOG_PRECHECK_LEVEL(de::LogEntry::Dev | de::LogEntry::Network | de::LogEntry::XVerbose, str, args)
223 #define LOGDEV_NET_VERBOSE(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Verbose, str)
224 #define LOGDEV_NET_MSG(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Message, str)
225 #define LOGDEV_NET_NOTE(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Note, str)
226 #define LOGDEV_NET_WARNING(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Warning, str)
227 #define LOGDEV_NET_ERROR(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Error, str)
228 #define LOGDEV_NET_CRITICAL(str) LOGDEV_NET_AT_LEVEL(de::LogEntry::Critical, str)
229
230 #ifdef DENG2_DEBUG
231 /**
232 * Makes a developer-only extra verbose level log entry. Only enabled in debug builds; use this
233 * for internal messages that might have a significant processing overhead. (Note that parameters
234 * differ compared to the normal LOG_* macros.)
235 */
236 # define LOG_TRACE_DEBUGONLY(form, args) LOG_TRACE(form, args)
237 # define LOGDEV_MAP_XVERBOSE_DEBUGONLY(form, args) LOGDEV_MAP_XVERBOSE(form, args)
238 # define LOGDEV_RES_XVERBOSE_DEBUGONLY(form, args) LOGDEV_RES_XVERBOSE(form, args)
239 # define LOGDEV_SCR_XVERBOSE_DEBUGONLY(form, args) LOGDEV_SCR_XVERBOSE(form, args)
240 # define LOGDEV_NET_XVERBOSE_DEBUGONLY(form, args) LOGDEV_NET_XVERBOSE(form, args)
241 #else
242 # define LOG_TRACE_DEBUGONLY(form, args)
243 # define LOGDEV_MAP_XVERBOSE_DEBUGONLY(form, args)
244 # define LOGDEV_RES_XVERBOSE_DEBUGONLY(form, args)
245 # define LOGDEV_SCR_XVERBOSE_DEBUGONLY(form, args)
246 # define LOGDEV_NET_XVERBOSE_DEBUGONLY(form, args)
247 #endif
248
249 #ifdef WIN32
250 # undef ERROR
251 #endif
252
253 namespace de {
254
255 class LogBuffer;
256
257
258 /**
259 * An entry to be stored in the log entry buffer. Log entries are created with
260 * Log::enter().
261 *
262 * Log entry arguments must be created before the entry itself is created. The
263 * LogEntryStager class is designed to help with this. Once an entry is
264 * inserted to the log buffer, no modifications may be done to it any more
265 * because another thread may need it immediately for flushing.
266 *
267 * @ingroup core
268 */
269 class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
270 {
271 public:
272 /**
273 * Entry domain (bits) and target audience. If not set, the entry is generic and
274 * intended for the end-user/player.
275 */
276 enum Context
277 {
278 // Domains:
279 FirstDomainBit = 16,
280 GenericBit = FirstDomainBit,
281 ResourceBit,
282 MapBit,
283 ScriptBit,
284 GLBit,
285 AudioBit,
286 InputBit,
287 NetworkBit,
288 LastDomainBit = NetworkBit,
289
290 Generic = (1 << GenericBit), /**< Global domain (bit automatically set if no
291 other domains). */
292 Resource = (1 << ResourceBit), /**< Resource or resource pack domain (files,
293 etc.). "Resource" is here meant in a wider
294 sense of all the external data that Doomsday
295 utilizes. */
296 Map = (1 << MapBit), /**< Map domain: information pertaining to the map
297 and its elements, playsim, etc. */
298 Script = (1 << ScriptBit), ///< Script domain
299 GL = (1 << GLBit), ///< Graphics/renderer domain (shaders, etc.)
300 Audio = (1 << AudioBit), ///< Audio domain
301 Input = (1 << InputBit), ///< Input domain: events, devices, etc.
302 Network = (1 << NetworkBit), ///< Network domain: connections, packets, etc.
303
304 // User groups:
305 Dev = 0x08000000, /**< Native code developer (i.e., the programmer);
306 can be combined with other flags to mark the
307 entry for devs. If bit is not set, the entry
308 is for the end-user. */
309 Privileged = 0x04000000, /**< Work in progress. Entries with this flag are
310 shown regardless of log filtering, in a
311 separate overlay. Use this for whatever you
312 are currently working on (so there is no need
313 to rely on qDebug). */
314 Interactive = 0x02000000, /**< Output from a command entered manually by the
315 user. Typically these should never be
316 filtered. */
317
318 AllDomains = 0x00ff0000,
319 DomainMask = AllDomains,
320 ContextMask = 0x0fff0000
321 };
322
contextToText(duint32 context)323 static String contextToText(duint32 context)
324 {
325 String const suffix = (context & Dev? "Dev" : "");
326 switch (context & DomainMask)
327 {
328 case Resource: return "Resource" + suffix;
329 case Map: return "Map" + suffix;
330 case Script: return "Script" + suffix;
331 case GL: return "GL" + suffix;
332 case Audio: return "Audio" + suffix;
333 case Input: return "Input" + suffix;
334 case Network: return "Network" + suffix;
335 default: return suffix;
336 }
337 }
338
textToContext(String text)339 static Context textToContext(String text)
340 {
341 duint32 val = 0;
342 if (text.endsWith("Dev"))
343 {
344 val |= Dev;
345 text = text.remove(text.size() - 3);
346 }
347 for (int i = FirstDomainBit; i <= LastDomainBit; ++i)
348 {
349 if (!contextToText(LogEntry::Context(1 << i)).compareWithoutCase(text))
350 return LogEntry::Context((1 << i) | val);
351 }
352 throw de::Error("Log::textToContext", "'" + text + "' is not a valid log entry context");
353 }
354
355 /// Importance level of the log entry.
356 enum Level
357 {
358 /**
359 * Verbose messages should be used for logging additional/supplementary
360 * information. All verbose messages can be safely ignored.
361 */
362 XVerbose = 1,
363 Verbose = 2,
364
365 /**
366 * The base level: normal log entries.
367 */
368 Message = 3,
369
370 /**
371 * Important messages that are intended for situations that are particularly
372 * noteworthy. They will not cause an alert to be raised, but the information is
373 * deemed particularly valuable.
374 */
375 Note = 4,
376
377 /**
378 * Warning messages are reserved for error situations that were automatically
379 * recovered from. A warning might be logged for example when the expected
380 * resource could not be found, and a fallback resource was used instead.
381 * Warnings will cause an alert to be raised so that the target audience is aware
382 * of the problem.
383 */
384 Warning = 5,
385
386 /**
387 * Error messages are intended for errors that could not be recovered from: the
388 * attempted operation had to be cancelled entirely. Will cause an alert to be
389 * raised so that the target audience is aware of the problem.
390 */
391 Error = 6,
392
393 /**
394 * Critical messages are intended for fatal errors that force the game to be
395 * unloaded or the entire engine to be shut down.
396 */
397 Critical = 7,
398
399 LowestLogLevel = XVerbose,
400 HighestLogLevel = Critical,
401 LevelMask = 0x7
402 };
403
levelToText(duint32 level)404 static String levelToText(duint32 level)
405 {
406 switch (level & LevelMask)
407 {
408 case XVerbose: return "XVerbose";
409 case Verbose: return "Verbose";
410 case Message: return "Message";
411 case Note: return "Note";
412 case Warning: return "Warning";
413 case Error: return "Error";
414 case Critical: return "Critical";
415 default: return "";
416 }
417 }
418
textToLevel(String text)419 static Level textToLevel(String text)
420 {
421 for (int i = XVerbose; i <= HighestLogLevel; ++i)
422 {
423 if (!levelToText(Level(i)).compareWithoutCase(text))
424 return Level(i);
425 }
426 throw de::Error("Log::textToLevel", "'" + text + "' is not a valid log level");
427 }
428
429 /**
430 * Argument for a log entry. The arguments of an entry are usually created
431 * automatically by LogEntryStager.
432 *
433 * @ingroup core
434 */
435 class DENG2_PUBLIC Arg : public String::IPatternArg, public ISerializable
436 {
437 public:
438 /// The wrong type is used in accessing the value. @ingroup errors
439 DENG2_ERROR(TypeError);
440
441 enum Type {
442 IntegerArgument,
443 FloatingPointArgument,
444 StringArgument
445 };
446
447 /**
448 * Base class for classes that support adding to the arguments. Any
449 * class that is derived from this class may be used as an argument for
450 * log entries. In practice, all arguments are converted to either
451 * numbers (64-bit integer or double) or text strings.
452 */
453 class Base {
454 public:
455 /// Attempted conversion from unsupported type.
456 DENG2_ERROR(TypeError);
457
458 public:
~Base()459 virtual ~Base() {}
460
461 virtual Type logEntryArgType() const = 0;
asInt64()462 virtual dint64 asInt64() const {
463 throw TypeError("LogEntry::Arg::Base", "dint64 not supported");
464 }
asDouble()465 virtual ddouble asDouble() const {
466 throw TypeError("LogEntry::Arg::Base", "ddouble not supported");
467 }
asText()468 virtual String asText() const {
469 throw TypeError("LogEntry::Arg::Base", "String not supported");
470 }
471 };
472
473 public:
474 Arg();
475 ~Arg();
476
477 void clear();
478
479 void setValue(dint i);
480 void setValue(duint i);
481 void setValue(long int i);
482 void setValue(long unsigned int i);
483 void setValue(duint64 i);
484 void setValue(dint64 i);
485 void setValue(ddouble d);
486 void setValue(void const *p);
487 void setValue(char const *s);
488 void setValue(String const &s);
489 void setValue(Base const &arg);
490 void setValue(std::array<char, 4> const &typecode);
491
492 template <typename ValueType>
set(ValueType const & s)493 Arg &set(ValueType const &s) { setValue(s); return *this; }
494
495 Arg &operator = (Arg const &other);
496
type()497 inline Type type() const { return _type; }
intValue()498 inline dint64 intValue() const {
499 DENG2_ASSERT(_type == IntegerArgument);
500 return _data.intValue;
501 }
floatValue()502 inline ddouble floatValue() const {
503 DENG2_ASSERT(_type == FloatingPointArgument);
504 return _data.floatValue;
505 }
stringValue()506 inline QString stringValue() const {
507 DENG2_ASSERT(_type == StringArgument);
508 return *_data.stringValue;
509 }
510
511 // Implements String::IPatternArg.
512 ddouble asNumber() const;
513 String asText() const;
514
515 // Implements ISerializable.
516 void operator >> (Writer &to) const;
517 void operator << (Reader &from);
518
519 public:
520 static Arg *newFromPool();
521 static void returnToPool(Arg *arg);
522
523 template <typename ValueType>
newFromPool(ValueType const & v)524 static inline Arg *newFromPool(ValueType const &v) {
525 return &(newFromPool()->set(v));
526 }
527
528 private:
529 Type _type;
530 union Data {
531 dint64 intValue;
532 ddouble floatValue;
533 String *stringValue;
534 } _data;
535 };
536
537 public:
538 enum Flag
539 {
540 /// In simple mode, only print the actual message contents,
541 /// without metadata.
542 Simple = 0x1,
543
544 /// Use escape sequences to format the entry with text styles
545 /// (for graphical output).
546 Styled = 0x2,
547
548 /// Omit the section from the entry text.
549 OmitSection = 0x4,
550
551 /// Indicate that the section is the same as on the previous line.
552 SectionSameAsBefore = 0x8,
553
554 /// Parts of the section can be abbreviated because they are clear
555 /// from the context (e.g., previous line).
556 AbbreviateSection = 0x10,
557
558 /// Entry is not from a local source. Could be used to mark entries
559 /// originating from a remote LogBuffer (over the network).
560 Remote = 0x20,
561
562 /// Entry level is not included in the output.
563 OmitLevel = 0x40,
564
565 /// Entry domain is not included in the output.
566 OmitDomain = 0x80
567 };
568 Q_DECLARE_FLAGS(Flags, Flag)
569
570 typedef QList<Arg *> Args;
571
572 public:
573 /**
574 * Constructs a disabled log entry.
575 */
576 LogEntry();
577
578 LogEntry(duint32 metadata, String const §ion, int sectionDepth, String const &format, Args args);
579
580 /**
581 * Copy constructor.
582 *
583 * @param other Log entry.
584 * @param extraFlags Additional flags to apply to the new entry.
585 */
586 LogEntry(LogEntry const &other, Flags extraFlags = 0);
587
588 ~LogEntry();
589
590 Flags flags() const;
591
592 /// Returns the timestamp of the entry.
when()593 Time when() const { return _when; }
594
metadata()595 inline duint32 metadata() const { return _metadata; }
596
context()597 inline duint32 context() const { return _metadata & ContextMask; }
598
level()599 inline Level level() const { return Level(_metadata & LevelMask); }
600
601 /// Returns a reference to the entry's section part. Reference is valid
602 /// for the lifetime of the entry.
section()603 String const §ion() const { return _section; }
604
605 /// Returns the number of sub-sections in the entry's section part.
sectionDepth()606 int sectionDepth() const { return _sectionDepth; }
607
format()608 String const &format() const { return _format; }
609
610 /**
611 * Converts the log entry to a string.
612 *
613 * @param flags Flags that control how the text is composed.
614 * @param shortenSection Number of characters to cut from the beginning of the section.
615 * With AbbreviateSection this limits which portion of the
616 * section is subject to abbreviation.
617 *
618 * @return Composed textual representation of the entry.
619 */
620 String asText(Flags const &flags = 0, int shortenSection = 0) const;
621
622 // Implements ISerializable.
623 void operator >> (Writer &to) const;
624 void operator << (Reader &from);
625
626 private:
627 void advanceFormat(String::const_iterator &i) const;
628
629 private:
630 Time _when;
631 duint32 _metadata;
632 String _section;
633 int _sectionDepth;
634 String _format;
635 Flags _defaultFlags;
636 bool _disabled;
637 Args _args;
638 };
639
640 QTextStream &operator << (QTextStream &stream, LogEntry::Arg const &arg);
641
Q_DECLARE_OPERATORS_FOR_FLAGS(LogEntry::Flags)642 Q_DECLARE_OPERATORS_FOR_FLAGS(LogEntry::Flags)
643
644 /**
645 * Provides means for adding log entries into the log entry buffer (LogBuffer).
646 * Each thread has its own Log instance. A thread's Log keeps track of the
647 * thread-local section stack.
648 *
649 * Note that there is only one LogBuffer where all the entries are collected.
650 *
651 * @ingroup core
652 */
653 class DENG2_PUBLIC Log
654 {
655 public:
656 class DENG2_PUBLIC Section
657 {
658 public:
659 /**
660 * The Section does not take a copy of @c name, so whatever
661 * it's pointing to must exist while the Section exists.
662 *
663 * @param name Name of the log section.
664 */
665 Section(char const *name);
666 ~Section();
667
668 Log &log() const { return _log; }
669
670 private:
671 Log &_log;
672 char const *_name;
673 };
674
675 public:
676 Log();
677 virtual ~Log();
678
679 /**
680 * Sets the metadata that applies to the current entry being staged. This can be
681 * checked by print methods interested in adapting their content to the context
682 * of the entry.
683 *
684 * @param metadata Entry metadata flags.
685 */
686 void setCurrentEntryMetadata(duint32 metadata);
687
688 /**
689 * Returns the metadata for the entry currently being staged.
690 *
691 * @return Metadata flags, or 0 if no entry is being staged.
692 */
693 duint32 currentEntryMetadata() const;
694
695 /**
696 * Determines if an entry is currently being staged using LogEntryStager.
697 */
698 bool isStaging() const;
699
700 /**
701 * Begins a new section in the log. Sections can be nested.
702 *
703 * @param name Name of the section. No copy of this string is made,
704 * so it must exist while the section is in use.
705 */
706 void beginSection(char const *name);
707
708 /**
709 * Ends the topmost section in the log.
710 *
711 * @param name Name of the topmost section.
712 */
713 void endSection(char const *name);
714
715 /**
716 * Begins an interactive section. All entries added while interactive get
717 * flagged as such. You must call endInteractive() to end the section.
718 * Interactive sections can be nested.
719 */
720 void beginInteractive();
721
722 /**
723 * Ends an interactive section. The number of endInteractive() calls must
724 * match the number of beginInteractive() calls.
725 */
726 void endInteractive();
727
728 bool isInteractive() const;
729
730 /**
731 * Creates a new log entry with the default (Message) level, targeted to the end-user.
732 *
733 * @param format Format template of the entry.
734 * @param arguments List of arguments. The entry is given ownership of
735 * each Arg instance.
736 */
737 LogEntry &enter(String const &format, LogEntry::Args arguments = LogEntry::Args());
738
739 /**
740 * Creates a new log entry with the specified log entry level.
741 *
742 * @param metadata Level of the entry and context bits.
743 * @param format Format template of the entry.
744 * @param arguments List of arguments. The entry is given ownership of
745 * each Arg instance.
746 */
747 LogEntry &enter(duint32 metadata, String const &format, LogEntry::Args arguments = LogEntry::Args());
748
749 public:
750 /**
751 * Returns the logger of the current thread.
752 */
753 static Log &threadLog();
754
755 /**
756 * Deletes the current thread's log. Threads should call this before
757 * they quit.
758 */
759 static void disposeThreadLog();
760
761 private:
762 DENG2_PRIVATE(d)
763 };
764
765 /**
766 * Stages a log entry for insertion into LogBuffer. Instances of LogEntryStager
767 * are built on the stack.
768 *
769 * You should use the @c LOG_* macros instead of using LogEntryStager directly.
770 */
771 class DENG2_PUBLIC LogEntryStager
772 {
773 public:
774 LogEntryStager(duint32 metadata, String const &format);
775
776 /// Appends a new argument to the entry.
777 template <typename ValueType>
778 inline LogEntryStager &operator << (ValueType const &v) {
779 // Args are created only if the level is enabled.
780 if (!_disabled) {
781 _args << LogEntry::Arg::newFromPool(v);
782 }
783 return *this;
784 }
785
786 ~LogEntryStager();
787
788 private:
789 bool _disabled;
790 duint32 _metadata;
791 String _format;
792 LogEntry::Args _args;
793 };
794
795 } // namespace de
796
797 #endif /* LIBDENG2_LOG_H */
798