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 &section, 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 &section() 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