Support: quest -f cjk_ngram
[xapian.git] / xapian-core / common / debuglog.h
blobee01dcd4d35d3fdc3f361901f949747496a7de2c
1 /** @file debuglog.h
2 * @brief Debug logging macros.
3 */
4 /* Copyright (C) 2008,2009,2010,2011,2014,2015 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifndef XAPIAN_INCLUDED_DEBUGLOG_H
22 #define XAPIAN_INCLUDED_DEBUGLOG_H
24 // In places where we include library code in non-library contexts, we can't
25 // have debug logging enabled, as the support functions aren't visible, so
26 // we define XAPIAN_REALLY_NO_DEBUG_LOG there.
27 #ifdef XAPIAN_REALLY_NO_DEBUG_LOG
28 # ifdef XAPIAN_DEBUG_LOG
29 # undef XAPIAN_DEBUG_LOG
30 # endif
31 #endif
33 #ifdef XAPIAN_DEBUG_LOG
35 #include "output-internal.h"
36 #include "pretty.h"
38 #include <cstring>
39 #include <ostream>
40 #include <sstream>
41 #include <string>
43 /// A categorisation of debug log messages.
44 enum debuglog_categories {
45 // Never wanted.
46 DEBUGLOG_CATEGORY_NEVER = 0,
48 /// Public API method and function calls.
49 DEBUGLOG_CATEGORY_API = ('A' - '@'),
51 /// Related to database backends.
52 DEBUGLOG_CATEGORY_DB = ('D' - '@'),
54 /// Related to exception handling.
55 DEBUGLOG_CATEGORY_EXCEPTION = ('X' - '@'),
57 /// Query expansion.
58 DEBUGLOG_CATEGORY_EXPAND = ('E' - '@'),
60 /// Matcher.
61 DEBUGLOG_CATEGORY_MATCH = ('M' - '@'),
63 /// Debug output from the lemon-generated QueryParser code.
64 DEBUGLOG_CATEGORY_QUERYPARSER = ('Q' - '@'),
66 /// Related to the remote backend.
67 DEBUGLOG_CATEGORY_REMOTE = ('R' - '@'),
69 /// Related to replication.
70 DEBUGLOG_CATEGORY_REPLICA = ('C' - '@'),
72 /// Spelling correction.
73 DEBUGLOG_CATEGORY_SPELLING = ('S' - '@'),
75 /// Uncategorised.
76 DEBUGLOG_CATEGORY_UNKNOWN = ('U' - '@'),
78 /// Weight calculations.
79 DEBUGLOG_CATEGORY_WTCALC = ('W' - '@'),
81 /// Query stuff.
82 DEBUGLOG_CATEGORY_QUERY = ('Y' - '@'),
84 /// Messages which are always logged.
85 DEBUGLOG_CATEGORY_ALWAYS = 31
88 /// Class to actually do the logging.
89 class DebugLogger {
90 /// Don't allow assignment.
91 void operator=(const DebugLogger &);
93 /// Don't allow copying.
94 DebugLogger(const DebugLogger &);
96 /// Mask bitmap of categories the user wants log messages for.
97 unsigned int categories_mask;
99 /// File descriptor for debug logging.
100 int fd;
102 /// The current indent level.
103 int indent_level;
105 /// Initialise categories_mask.
106 void initialise_categories_mask();
108 public:
109 /// Constructor.
110 DebugLogger()
111 : categories_mask(1 << DEBUGLOG_CATEGORY_API), fd(-1), indent_level(0)
114 /// Destructor.
115 ~DebugLogger();
117 /// Check if the user wants debug log messages of category @a category.
118 bool is_category_wanted(debuglog_categories category) {
119 // The argument will almost always be constant, so these inline checks
120 // against DEBUGLOG_CATEGORY_ALWAYS and DEBUGLOG_CATEGORY_NEVER will
121 // usually be optimised away, or become the only code path.
122 if (category == DEBUGLOG_CATEGORY_ALWAYS) return true;
123 if (category == DEBUGLOG_CATEGORY_NEVER) return false;
124 if (fd == -1) initialise_categories_mask();
125 return (categories_mask >> category) & 1;
128 /// Log message @a msg of category @a category.
129 void log_line(debuglog_categories category, const std::string & msg);
131 void indent() { ++indent_level; }
133 void outdent() {
134 if (indent_level) --indent_level;
138 namespace Xapian {
139 /** Dummy type for "no arguments".
141 * We pull this into the global namespace, and overload operator<< so that
142 * writing it to a stream should generate no code.
144 typedef enum { NO_ARGS } NoArguments_;
147 inline std::ostream & operator<<(std::ostream &o, Xapian::NoArguments_) {
148 return o;
151 using Xapian::NO_ARGS;
153 extern DebugLogger xapian_debuglogger_;
155 /** Unconditionally log message @a MSG of category @a CATEGORY. */
156 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
157 #define LOGLINE_ALWAYS_(CATEGORY, MSG) do { \
158 std::ostringstream xapian_debuglog_ostream_; \
159 xapian_debuglog_ostream_ << MSG; \
160 xapian_debuglogger_.log_line(CATEGORY, xapian_debuglog_ostream_.str()); \
161 } while (false)
163 /** Log message @a MSG of category @a CATEGORY. */
164 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
165 #define LOGLINE_(CATEGORY, MSG) do { \
166 debuglog_categories xapian_debuglog_category_ = (CATEGORY); \
167 if (xapian_debuglogger_.is_category_wanted(xapian_debuglog_category_)) { \
168 LOGLINE_ALWAYS_(xapian_debuglog_category_, MSG); \
170 } while (false)
172 /** Helper class for debug logging of functions and methods.
174 * We instantiate a DebugLogFunc object at the start of each logged function
175 * and method. DebugLogFunc's constructor logs the parameters passed, the
176 * RETURN() macro sets the return value as a string, and DebugLogFunc's
177 * destructor logs this string. If an exception is thrown during the method
178 * and causes it to exit, DebugLogFunc's destructor detects and logs this
179 * fact.
181 class DebugLogFunc {
182 /// This pointer (or 0 if this is a static method or a non-class function).
183 const void * this_ptr;
185 /// The category of log message to use for this function/method.
186 debuglog_categories category;
188 /// Function/method name.
189 std::string func;
191 /// Was an uncaught exception active when we entered this function?
192 bool uncaught_exception;
194 public:
195 /// Constructor called when logging for a "normal" method or function.
196 DebugLogFunc(const void * this_ptr_, debuglog_categories category_,
197 const char * return_type, const char * func_name,
198 const std::string & params)
199 : this_ptr(this_ptr_), category(category_),
200 uncaught_exception(std::uncaught_exception())
202 if (is_category_wanted()) {
203 func.assign(return_type);
204 func += ' ';
205 func += func_name;
206 func += '(';
207 func += params;
208 func += ')';
209 LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
210 xapian_debuglogger_.indent();
214 /// Log the returned value.
215 void log_return_value(const std::string & return_value) {
216 xapian_debuglogger_.outdent();
217 LOGLINE_(category, '[' << this_ptr << "] " << func << " returned: " <<
218 return_value);
220 // Flag that we've logged the return already.
221 category = DEBUGLOG_CATEGORY_NEVER;
224 /// Check if the current category of log message is wanted.
225 bool is_category_wanted() const {
226 return xapian_debuglogger_.is_category_wanted(category);
229 /** Destructor.
231 * This logs that the function/method has returned if this is due to an
232 * exception or if the RETURN() macro hasn't been used.
234 ~DebugLogFunc() {
235 if (!is_category_wanted()) return;
236 xapian_debuglogger_.outdent();
237 if (!uncaught_exception && std::uncaught_exception()) {
238 // An exception is causing the stack to be unwound.
239 LOGLINE_(category, '[' << this_ptr << "] " << func <<
240 " exited due to exception");
241 } else {
242 LOGLINE_(category, '[' << this_ptr << "] " << func <<
243 " returned (not marked up for return logging)");
248 /** Helper class for debug logging of functions and methods returning void,
249 * and class constructors and destructors.
251 * We instantiate a DebugLogFuncVoid object at the start of each logged
252 * function and method. DebugLogFuncVoid's constructor logs the parameters
253 * passed, and DebugLogFunc's destructor logs that the function/method is
254 * returning. If an exception is thrown during the method and causes it to
255 * exit, DebugLogFunc's destructor detects and logs this fact.
257 class DebugLogFuncVoid {
258 /// This pointer (or 0 if this is a static method or a non-class function).
259 const void * this_ptr;
261 /// The category of log message to use for this function/method.
262 debuglog_categories category;
264 /// Function/method name.
265 std::string func;
267 /// Was an uncaught exception active when we entered this function?
268 bool uncaught_exception;
270 public:
271 /// Constructor called when logging for a "normal" method or function.
272 DebugLogFuncVoid(const void * this_ptr_, debuglog_categories category_,
273 const char * func_name,
274 const std::string & params)
275 : this_ptr(this_ptr_), category(category_),
276 uncaught_exception(std::uncaught_exception())
278 if (is_category_wanted()) {
279 func.assign("void ");
280 func += func_name;
281 func += '(';
282 func += params;
283 func += ')';
284 LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
285 xapian_debuglogger_.indent();
289 /// Constructor called when logging for a class constructor.
290 DebugLogFuncVoid(const void * this_ptr_, debuglog_categories category_,
291 const std::string & params,
292 const char * class_name)
293 : this_ptr(this_ptr_), category(category_),
294 uncaught_exception(std::uncaught_exception())
296 if (is_category_wanted()) {
297 func.assign(class_name);
298 func += "::";
299 // The ctor name is the last component if there are colons (e.g.
300 // for Query::Internal, the ctor is Internal.
301 const char * ctor_name = std::strrchr(class_name, ':');
302 if (ctor_name)
303 ++ctor_name;
304 else
305 ctor_name = class_name;
306 func += ctor_name;
307 func += '(';
308 func += params;
309 func += ')';
310 LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
311 xapian_debuglogger_.indent();
315 /// Constructor called when logging for a class destructor.
316 DebugLogFuncVoid(const void * this_ptr_, debuglog_categories category_,
317 const char * class_name)
318 : this_ptr(this_ptr_), category(category_),
319 uncaught_exception(std::uncaught_exception())
321 if (is_category_wanted()) {
322 func.assign(class_name);
323 func += "::~";
324 // The dtor name is the last component if there are colons.
325 const char * dtor_name = std::strrchr(class_name, ':');
326 if (dtor_name)
327 ++dtor_name;
328 else
329 dtor_name = class_name;
330 func += dtor_name;
331 func += "()";
332 LOGLINE_(category, '[' << this_ptr << "] " << func);
333 xapian_debuglogger_.indent();
337 /// Check if the current category of log message is wanted.
338 bool is_category_wanted() const {
339 return xapian_debuglogger_.is_category_wanted(category);
342 /** Destructor.
344 * This logs that the function/method has returned and whether this was
345 * due to an exception.
347 ~DebugLogFuncVoid() {
348 if (!is_category_wanted()) return;
349 xapian_debuglogger_.outdent();
350 const char * reason;
351 if (!uncaught_exception && std::uncaught_exception()) {
352 // An exception is causing the stack to be unwound.
353 reason = " exited due to exception";
354 } else {
355 reason = " returned";
357 LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func << reason);
361 #ifdef __GNUC__
362 // __attribute__((unused)) supported since at least GCC 2.95.3.
363 # define XAPIAN_UNUSED __attribute__((unused))
364 #else
365 # define XAPIAN_UNUSED
366 #endif
368 /// Log a call to a method returning non-void.
369 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) \
370 typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
371 std::string xapian_logcall_parameters_; \
372 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
373 std::ostringstream xapian_logcall_ostream_; \
374 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
375 xapian_logcall_stream_ << PARAMS; \
376 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
378 DebugLogFunc xapian_logcall_(static_cast<const void *>(this), DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, xapian_logcall_parameters_)
380 /// Log a call to a method returning void.
381 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) \
382 std::string xapian_logcall_parameters_; \
383 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
384 std::ostringstream xapian_logcall_ostream_; \
385 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
386 xapian_logcall_stream_ << PARAMS; \
387 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
389 DebugLogFuncVoid xapian_logcall_(static_cast<const void *>(this), DEBUGLOG_CATEGORY_##CATEGORY, FUNC, xapian_logcall_parameters_)
391 /// Log a constructor call.
392 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) \
393 std::string xapian_logcall_parameters_; \
394 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
395 std::ostringstream xapian_logcall_ostream_; \
396 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
397 xapian_logcall_stream_ << PARAMS; \
398 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
400 DebugLogFuncVoid xapian_logcall_(static_cast<const void *>(this), DEBUGLOG_CATEGORY_##CATEGORY, xapian_logcall_parameters_, CLASS)
402 /// Log a destructor call.
403 #define LOGCALL_DTOR(CATEGORY, CLASS) \
404 DebugLogFuncVoid xapian_logcall_(static_cast<const void *>(this), DEBUGLOG_CATEGORY_##CATEGORY, CLASS)
406 /// Log a call to a static method returning a non-void type.
407 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) \
408 typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
409 std::string xapian_logcall_parameters_; \
410 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
411 std::ostringstream xapian_logcall_ostream_; \
412 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
413 xapian_logcall_stream_ << PARAMS; \
414 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
416 DebugLogFunc xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, xapian_logcall_parameters_)
418 /// Log a call to a static method returning void.
419 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) \
420 std::string xapian_logcall_parameters_; \
421 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
422 std::ostringstream xapian_logcall_ostream_; \
423 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
424 xapian_logcall_stream_ << PARAMS; \
425 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
427 DebugLogFuncVoid xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, FUNC, xapian_logcall_parameters_)
429 /// Log returning a value.
430 #define RETURN(A) do { \
431 xapian_logcall_return_type_ xapian_logcall_return_ = A; \
432 if (xapian_logcall_.is_category_wanted()) { \
433 std::ostringstream xapian_logcall_ostream_; \
434 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
435 xapian_logcall_stream_ << xapian_logcall_return_; \
436 xapian_logcall_.log_return_value(xapian_logcall_ostream_.str()); \
438 return xapian_logcall_return_; \
439 } while (false)
441 /** Log message @a b of category @a a.
443 * The message is logged on a line by itself. To keep the debug log readable,
444 * it shouldn't have a trailing '\n', or contain an embedded '\n'.
446 #define LOGLINE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, b)
448 /** Log the value of variable or expression @a b. */
449 #define LOGVALUE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, #b" = " << b)
451 #else
453 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) (void)0
454 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) (void)0
455 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) (void)0
456 #define LOGCALL_DTOR(CATEGORY, CLASS) (void)0
457 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) (void)0
458 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) (void)0
459 #define RETURN(A) return A
460 #define LOGLINE(a,b) (void)0
461 #define LOGVALUE(a,b) (void)0
463 #endif
465 #endif // XAPIAN_INCLUDED_DEBUGLOG_H