2 * @brief Debug logging macros.
4 /* Copyright (C) 2008,2009,2010,2011,2014,2015,2021 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
33 #ifdef XAPIAN_DEBUG_LOG
35 #include "output-internal.h"
43 /// A categorisation of debug log messages.
44 enum debuglog_categories
{
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' - '@'),
58 DEBUGLOG_CATEGORY_EXPAND
= ('E' - '@'),
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' - '@'),
76 DEBUGLOG_CATEGORY_UNKNOWN
= ('U' - '@'),
78 /// Weight calculations.
79 DEBUGLOG_CATEGORY_WTCALC
= ('W' - '@'),
82 DEBUGLOG_CATEGORY_QUERY
= ('Y' - '@'),
84 /// Messages which are always logged.
85 DEBUGLOG_CATEGORY_ALWAYS
= 31
88 /// Class to actually do the logging.
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.
102 /// The current indent level.
105 /// Initialise categories_mask.
106 void initialise_categories_mask();
111 : categories_mask(1 << DEBUGLOG_CATEGORY_API
), fd(-1), indent_level(0)
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
; }
134 if (indent_level
) --indent_level
;
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_
) {
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()); \
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); \
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
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.
191 /// Number of uncaught exceptions when we entered this function.
192 int uncaught_exceptions
;
194 static int get_uncaught_exceptions() {
195 #if __cplusplus >= 201703L
196 return std::uncaught_exceptions();
198 return int(std::uncaught_exception());
203 /// Constructor called when logging for a "normal" method or function.
204 DebugLogFunc(const void* this_ptr_
, debuglog_categories category_
,
205 const char* return_type
, const char* func_name
,
206 const std::string
& params
)
207 : this_ptr(this_ptr_
), category(category_
),
208 uncaught_exceptions(get_uncaught_exceptions())
210 if (is_category_wanted()) {
211 func
.assign(return_type
);
217 LOGLINE_ALWAYS_(category
, '[' << this_ptr
<< "] " << func
);
218 xapian_debuglogger_
.indent();
222 /// Log the returned value.
223 void log_return_value(const std::string
& return_value
) {
224 xapian_debuglogger_
.outdent();
225 LOGLINE_(category
, '[' << this_ptr
<< "] " << func
<< " returned: " <<
228 // Flag that we've logged the return already.
229 category
= DEBUGLOG_CATEGORY_NEVER
;
232 /// Check if the current category of log message is wanted.
233 bool is_category_wanted() const {
234 return xapian_debuglogger_
.is_category_wanted(category
);
239 * This logs that the function/method has returned if this is due to an
240 * exception or if the RETURN() macro hasn't been used.
243 if (!is_category_wanted()) return;
244 xapian_debuglogger_
.outdent();
245 if (get_uncaught_exceptions() > uncaught_exceptions
) {
246 // An exception is causing the stack to be unwound.
247 LOGLINE_(category
, '[' << this_ptr
<< "] " << func
<<
248 " exited due to exception");
250 LOGLINE_(category
, '[' << this_ptr
<< "] " << func
<<
251 " returned (not marked up for return logging)");
256 /** Helper class for debug logging of functions and methods returning void,
257 * and class constructors and destructors.
259 * We instantiate a DebugLogFuncVoid object at the start of each logged
260 * function and method. DebugLogFuncVoid's constructor logs the parameters
261 * passed, and DebugLogFunc's destructor logs that the function/method is
262 * returning. If an exception is thrown during the method and causes it to
263 * exit, DebugLogFunc's destructor detects and logs this fact.
265 class DebugLogFuncVoid
{
266 /// This pointer (or 0 if this is a static method or a non-class function).
267 const void* this_ptr
;
269 /// The category of log message to use for this function/method.
270 debuglog_categories category
;
272 /// Function/method name.
275 /// Number of uncaught exceptions when we entered this function.
276 int uncaught_exceptions
;
278 static int get_uncaught_exceptions() {
279 #if __cplusplus >= 201703L
280 return std::uncaught_exceptions();
282 return int(std::uncaught_exception());
287 /// Constructor called when logging for a "normal" method or function.
288 DebugLogFuncVoid(const void* this_ptr_
, debuglog_categories category_
,
289 const char* func_name
,
290 const std::string
& params
)
291 : this_ptr(this_ptr_
), category(category_
),
292 uncaught_exceptions(get_uncaught_exceptions())
294 if (is_category_wanted()) {
295 func
.assign("void ");
300 LOGLINE_ALWAYS_(category
, '[' << this_ptr
<< "] " << func
);
301 xapian_debuglogger_
.indent();
305 /// Constructor called when logging for a class constructor.
306 DebugLogFuncVoid(const void* this_ptr_
, debuglog_categories category_
,
307 const std::string
& params
,
308 const char* class_name
)
309 : this_ptr(this_ptr_
), category(category_
),
310 uncaught_exceptions(get_uncaught_exceptions())
312 if (is_category_wanted()) {
313 func
.assign(class_name
);
315 // The ctor name is the last component if there are colons (e.g.
316 // for Query::Internal, the ctor is Internal.
317 const char* ctor_name
= std::strrchr(class_name
, ':');
321 ctor_name
= class_name
;
326 LOGLINE_ALWAYS_(category
, '[' << this_ptr
<< "] " << func
);
327 xapian_debuglogger_
.indent();
331 /// Constructor called when logging for a class destructor.
332 DebugLogFuncVoid(const void* this_ptr_
, debuglog_categories category_
,
333 const char* class_name
)
334 : this_ptr(this_ptr_
), category(category_
),
335 uncaught_exceptions(get_uncaught_exceptions())
337 if (is_category_wanted()) {
338 func
.assign(class_name
);
340 // The dtor name is the last component if there are colons.
341 const char* dtor_name
= std::strrchr(class_name
, ':');
345 dtor_name
= class_name
;
348 LOGLINE_(category
, '[' << this_ptr
<< "] " << func
);
349 xapian_debuglogger_
.indent();
353 /// Check if the current category of log message is wanted.
354 bool is_category_wanted() const {
355 return xapian_debuglogger_
.is_category_wanted(category
);
360 * This logs that the function/method has returned and whether this was
361 * due to an exception.
363 ~DebugLogFuncVoid() {
364 if (!is_category_wanted()) return;
365 xapian_debuglogger_
.outdent();
367 if (get_uncaught_exceptions() > uncaught_exceptions
) {
368 // An exception is causing the stack to be unwound.
369 reason
= " exited due to exception";
371 reason
= " returned";
373 LOGLINE_ALWAYS_(category
, '[' << this_ptr
<< "] " << func
<< reason
);
378 // __attribute__((unused)) supported since at least GCC 2.95.3.
379 # define XAPIAN_UNUSED __attribute__((unused))
381 # define XAPIAN_UNUSED
384 /// Log a call to a method returning non-void.
385 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) \
386 typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
387 std::string xapian_logcall_parameters_; \
388 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
389 std::ostringstream xapian_logcall_ostream_; \
390 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
391 xapian_logcall_stream_ << PARAMS; \
392 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
394 DebugLogFunc xapian_logcall_(static_cast<const void*>(this), \
395 DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, \
396 xapian_logcall_parameters_)
398 /// Log a call to a method returning void.
399 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) \
400 std::string xapian_logcall_parameters_; \
401 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
402 std::ostringstream xapian_logcall_ostream_; \
403 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
404 xapian_logcall_stream_ << PARAMS; \
405 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
407 DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
408 DEBUGLOG_CATEGORY_##CATEGORY, FUNC, \
409 xapian_logcall_parameters_)
411 /// Log a constructor call.
412 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) \
413 std::string xapian_logcall_parameters_; \
414 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
415 std::ostringstream xapian_logcall_ostream_; \
416 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
417 xapian_logcall_stream_ << PARAMS; \
418 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
420 DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
421 DEBUGLOG_CATEGORY_##CATEGORY, \
422 xapian_logcall_parameters_, CLASS)
424 /// Log a destructor call.
425 #define LOGCALL_DTOR(CATEGORY, CLASS) \
426 DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
427 DEBUGLOG_CATEGORY_##CATEGORY, CLASS)
429 /// Log a call to a static method returning a non-void type.
430 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) \
431 typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
432 std::string xapian_logcall_parameters_; \
433 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
434 std::ostringstream xapian_logcall_ostream_; \
435 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
436 xapian_logcall_stream_ << PARAMS; \
437 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
439 DebugLogFunc xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, xapian_logcall_parameters_)
441 /// Log a call to a static method returning void.
442 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) \
443 std::string xapian_logcall_parameters_; \
444 if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
445 std::ostringstream xapian_logcall_ostream_; \
446 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
447 xapian_logcall_stream_ << PARAMS; \
448 xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
450 DebugLogFuncVoid xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, FUNC, xapian_logcall_parameters_)
452 /// Log returning a value.
453 #define RETURN(A) do { \
454 xapian_logcall_return_type_ xapian_logcall_return_ = A; \
455 if (xapian_logcall_.is_category_wanted()) { \
456 std::ostringstream xapian_logcall_ostream_; \
457 PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
458 xapian_logcall_stream_ << xapian_logcall_return_; \
459 xapian_logcall_.log_return_value(xapian_logcall_ostream_.str()); \
461 return xapian_logcall_return_; \
464 /** Log message @a b of category @a a.
466 * The message is logged on a line by itself. To keep the debug log readable,
467 * it shouldn't have a trailing '\n', or contain an embedded '\n'.
469 #define LOGLINE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, b)
471 /** Log the value of variable or expression @a b. */
472 #define LOGVALUE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, #b" = " << b)
476 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) (void)0
477 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) (void)0
478 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) (void)0
479 #define LOGCALL_DTOR(CATEGORY, CLASS) (void)0
480 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) (void)0
481 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) (void)0
482 #define RETURN(A) return A
483 #define LOGLINE(a,b) (void)0
484 #define LOGVALUE(a,b) (void)0
488 #endif // XAPIAN_INCLUDED_DEBUGLOG_H