-
Notifications
You must be signed in to change notification settings - Fork 127
2. Handlers
Handlers are the objects that actually write the log to their target.
A Handler
object is never instantiated directly, this class acts as a base class of different handler derived classes e.g. StreamHandler
, FileHandler
Each handler is responsible for only single target (e.g file, console, db), and each one owns a formatter object.
Each handler have its own instance of a formatter object which formats the messages to its destination.
Upon the handler creation, the handler object is registered and owned by a central manager object the HandlerCollection
For files, one handler is created per filename. For stdout and stderr a default handler for each one is always created during initialisation. It is possible for the user to create multiple stdout or stderr handles by providing a unique id per handle.
When creating a custom logger one or more handlers for this logger can be specified. This can only be done only the logger creation.
#include "quill/Quill.h"
quill::Handler* fhandler = quill::file_handler("a_filename.log", "w");
The above code creates a new FileHandler
capable of delivery log records to the file "a_filename.log"
It is possible to reuse the same handle object in multiple loggers for example all logger objects will be writing to the same file.
The following code is also thread-safe.
// The first time this function is called a file handler is created for this filename.
// Calling the function with the same filename will return the existing handler
quill::Handler* file_handler = quill::file_handler(filename, "w");
// Create a logger using this handler
quill::Logger* logger_foo = quill::create_logger("logger_foo", file_handler);
// Because a handler already created for this filename a pointer to the existing handler is returned
quill::Handler* file_handler_2 = quill::file_handler(filename, "w");
// Create a new logger using this handler
quill::Logger* logger_bar = quill::create_logger("logger_bar", file_handler_2);
While when operating to files, only one handle object can be created per file name, this is not the case for stdout
or stderr
.
It is possible to create multiple handlers to stdout
or stderr
by providing a unique name.
This is useful for when you want to have different loggers writing to stdout
using a different format. (also see example_stdout_multiple_formatters)
// Get the stdout file handler, with a unique name
quill::Handler* stdout_handler_1 = quill::stdout_handler("stdout_1");
stdout_handler_1->set_pattern(
"%(ascii_time) [%(process)] [%(thread)] LOG_%(level_name) %(logger_name) - %(message)", // message format
"%D %H:%M:%S.%Qms %z", // timestamp format
quill::Timezone::GmtTime); // timestamp's timezone
quill::Logger* logger_foo = quill::create_logger("logger_foo", stdout_handler_1);
// Get the stdout file handler, with another unique name
quill::Handler* stdout_handler_2 = quill::stdout_handler("stdout_2");
stdout_handler_2->set_pattern("%(ascii_time) LOG_%(level_name) %(logger_name) - %(message)", // message format
"%D %H:%M:%S.%Qms %z", // timestamp format
quill::Timezone::GmtTime); // timestamp's timezone
quill::Logger* logger_bar = quill::create_logger("logger_bar", stdout_handler_2);
The following useful handlers are provided in the library.
A different pattern can be set to the handlers by calling set_pattern(...)
. See Formatters
The ConsoleHandler
class sends logging output to streams stdout
or stderr
. Printing colour codes to terminal or windows console is also supported.
Handler* stdout_handler(std::string const& stdout_handler_name = std::string{"stdout"},
ConsoleColours const& console_colours = ConsoleColours{});
Obtains a handler to `stdout.
Handler* stderr_handler(std::string const& stderr_handler_name = std::string{"stderr"});
Obtains a handler to stderr
.
set_pattern(...)
can be used to set the formatting pattern on each pattern. stdout_handler_name
and stderr_handler_name
can be used in case multiple formatting to stdout
or stderr
is required. See example
By default no colour codes are printed to the terminal unless it is enabled. For more advanced example See example
// Enable console colours on the default constructed handler to stdout before calling quill:start()
quill::Config cfg;
cfg.enable_console_colours = true;
quill::configure(cfg);
// Start the logging backend thread
quill::start();
// The default logger and any created logger that prints to the console will also print colour codes.
Handler* file_handler(filename_t const& filename,
std::string const& mode = std::string{"a"},
FilenameAppend append_to_filename = FilenameAppend::None);
Creates a new instance of the FileHandler
class or looks up an existing instance. If the file is already opened the existing handler for this file is returned instead.
append_to_filename
can be used when the file handler is created to append additional text to the filename. e.g. FilenameAppend::Date
will append the date to the file name.
// Start the backend logging thread
quill::start();
// Create a new file handler
quill::Handler* file_handler = quill::file_handler(filename, "w");
// Create a logger using this handler
quill::Logger* logger_foo = quill::create_logger("logger_foo", file_handler);
LOG_INFO(logger_foo, "Hello from {}", "library foo");
Handler* rotating_file_handler(filename_t const& base_filename,
std::string const& mode = std::string{"a"},
size_t max_bytes = 0,
uint32_t backup_count = 0);
Creates a new instance of the RotatingFileHandler
class or looks up an existing instance. If a rotating file handler with base_filename exists then the existing instance is returned. If a rotating file handler does not exist then the specified file is opened and used as the stream for logging.
By default, the file grows indefinitely. You can use the max_bytes
and backup_count
values to allow the file to rollover at a predetermined size. When the size is about to be exceeded, the file is closed and a new file is silently opened for output. Rollover occurs whenever the current log file is nearly max_bytes
in length; but if either of max_bytes
or backup_count
is zero, rollover never occurs, so you generally want to set backup_count
to at least 1, and have a non-zero maxBytes.
When backup_count
is non-zero, the system will save old log files by appending the extensions ‘.1’, ‘.2’ etc., to the filename.
For example, with a backup_count
of 5 and a base_filename
of app.log
, you would get app.log
, app.1.log
, app.2.log
, up to app.5.log
. The file being written to is always app.log
. When this file is filled, it is closed and renamed to app.1.log
, and if files app.1.log
, app.2.log
, etc. exist, then they are renamed to app.2.log
, app.3.log
etc. respectively.
// Start the backend logging thread
quill::start();
// Create a rotating file handler with a max file size per log file and maximum rotation up to 5 times
quill::Handler* file_handler = quill::rotating_file_handler(base_filename, "w", 1024, 5);
// Create a logger using this handler
quill::Logger* logger_bar = quill::create_logger("rotating", file_handler);
for (uint32_t i = 0; i < 15; ++i)
{
LOG_INFO(logger_bar, "Hello from {} {}", "rotating logger", i);
}
// Get an instance to the existing rotating file handler
quill::Handler* file_handler = quill::rotating_file_handler(base_filename);
Handler* time_rotating_file_handler(filename_t const& base_filename,
std::string const& mode = std::string{"a"},
std::string const& when = std::string{"H"},
uint32_t interval = 1,
uint32_t backup_count = 0,
Timezone timezone = Timezone::LocalTime,
std::string const& at_time = std::string{"00:00"});
Creates a new instance of the TimeRotatingFileHandler
class or looks up an existing instance.
The specified file is opened and used as the stream for logging. Rotating happens based on the product of when
and interval
. You can use the when
to specify the type of interval
. when
can have the values of "H"
, "M"
or "daily"
.
If "daily"
is passed, then interval
is ignored.
The system will save old log files by appending extensions to the filename. The extensions are date-and-time based in the format of %Y-%m-%d_%H-%M-%S
. If the timezone
argument is GmtTime
, times in UTC will be used; otherwise local time is used
At most backup_count
files are be kept, and if more would be created when rollover occurs, the oldest one is deleted.
at_time
argument specifies the time of day when rollover occurs. This is only used when 'daily' is passed. It must be in the format HH:MM
.
For example to create a log file that rotates daily at 02:00
// Start the backend logging thread
quill::start();
// Create a rotating file handler which rotates daily at 02:00
quill::Handler* file_handler =
quill::time_rotating_file_handler(filename, "w", "daily", 1, 10, Timezone::LocalTime, "02:00");
// Create a logger using this handler
quill::Logger* logger_bar = quill::create_logger("daily_logger", file_handler);
LOG_INFO(logger_bar, "Hello from {}", "daily logger");
Or to create a log file that rotates every 1 hour
// Start the backend logging thread
quill::start();
// Create a rotating file handler which rotates every one hour and keep maximum 24 files
quill::Handler* file_handler =
quill::time_rotating_file_handler(filename, "w", "H", 24, 10);
// Create a logger using this handler
quill::Logger* logger_bar = quill::create_logger("daily_logger", file_handler);
LOG_INFO(logger_bar, "Hello from {}", "daily logger");