Macro-Free Mode

The library provides a macro-free mode that allows logging using functions instead of macros. While the macro mode remains the main and recommended approach for logging, the macro-free mode offers an alternative with cleaner code in some scenarios, at the cost of additional overhead.

The macro-free mode is implemented using compiler built-ins (__builtin_FILE(), __builtin_FUNCTION(), __builtin_LINE()) which may vary by compiler.

Performance Trade-offs

The macro-free approach comes with performance implications:

  1. Higher Latency: Format metadata isn’t available at compile time, requiring additional runtime copying of metadata to the backend thread

  2. Always-Evaluated Arguments: Unlike macros which skip evaluation for disabled log levels, arguments to these functions are always evaluated

  3. No Compile-Time Removal: Cannot be completely compiled out with QUILL_COMPILE_ACTIVE_LOG_LEVEL_<LEVEL> as macros can

  4. Backend Thread Impact: Reduced throughput in the backend due to runtime metadata storage and processing

  5. Additional Safety Checks: The macro-free functions perform a runtime check for nullptr logger. This can be useful in cases where a user might forget to initialize a logger or intentionally wants logging calls to become no-ops when the logger is null. (Note that there are also multiple ways to achieve no-op logging with macros, e.g., by setting the log level to None)

For performance-critical logging paths, the macro-based logging interface is recommended.

Available Functions

The following logging functions are available in the macro-free mode:

  • quill::tracel3(logger, fmt, args...) - Trace level 3 (most detailed)

  • quill::tracel2(logger, fmt, args...) - Trace level 2

  • quill::tracel1(logger, fmt, args...) - Trace level 1

  • quill::debug(logger, fmt, args...) - Debug level

  • quill::info(logger, fmt, args...) - Information level

  • quill::notice(logger, fmt, args...) - Notice level

  • quill::warning(logger, fmt, args...) - Warning level

  • quill::error(logger, fmt, args...) - Error level

  • quill::critical(logger, fmt, args...) - Critical level

  • quill::backtrace(logger, fmt, args...) - Backtrace level

Each function also accepts a quill::Tags object as an optional parameter after the logger.

Named placeholders (e.g., {name} instead of {}) work the same way as with LOG_ macros. See Named Placeholders for details.

Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogFunctions.h"
#include "quill/Logger.h"
#include "quill/sinks/ConsoleSink.h"

#include <string>
#include <utility>

/**
 * Console logging using the macro-free API from `quill/LogFunctions.h`.
 *
 * Note: You can also pass STL types by including the relevant header files from
 * `quill/std/`.
 */

int main()
{
  quill::BackendOptions backend_options;
  quill::Backend::start(backend_options);

  // Frontend
  auto console_sink = quill::Frontend::create_or_get_sink<quill::ConsoleSink>("sink_id_1");

  // [ %(tags)] is for demonstration purposes only, you might remove it if you don't want it
  quill::Logger* logger = quill::Frontend::create_or_get_logger(
    "root", std::move(console_sink),
    quill::PatternFormatterOptions{"%(time) [%(thread_id)] %(short_source_location:<28) "
                                   "LOG_%(log_level:<9) %(logger:<12) [ %(tags)] %(message)"});

  // Change the LogLevel to print everything
  logger->set_log_level(quill::LogLevel::TraceL3);

  int a = 123;
  std::string l = "log";

  quill::tracel3(logger, "A {} message with number {}", l, a);
  quill::tracel3(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::tracel2(logger, "A {} message with number {}", l, a);
  quill::tracel2(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::tracel1(logger, "A {} message with number {}", l, a);
  quill::tracel1(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::debug(logger, "A {} message with number {}", l, a);
  quill::debug(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::info(logger, "A {} message with number {}", l, a);
  quill::info(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::notice(logger, "A {} message with number {}", l, a);
  quill::notice(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::warning(logger, "A {} message with number {}", l, a);
  quill::warning(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::error(logger, "A {} message with number {}", l, a);
  quill::error(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);

  quill::critical(logger, "A {} message with number {}", l, a);
  quill::critical(logger, quill::Tags{"TAG"}, "A {} message with number {}", l, a);
}