The spdlog library is a widely-used and very capable C++ library for logging. We are
providing it for R users in package RcppSpdlog along with the additional C++/R
interface package spdl. For both demanding logging jobs and/or a need for customization,
these work reliably. Sometimes, however, one desires a smaller, more lightweight and minimalistic
approach. spdlite answers to that need. It is a C++20 header-only library which makes
some deliberate design choices to 'keep things simple' -- resulting in a logger that is "tiny, fast,
capable" (to quote the upstream README). Configuration is mostly at compile-time, though
logging levels can of course be adjusted at run-time. By using an inline instance under the 'one
definition rule', each 'application', or here each R package including the header, gets its own
instance of the logger (even if the header is included multiple times within a package). As before,
we provide a distinct 'sink' for R (to route into its output stream). A simple 'file sink' for
logging messages can be selected at compile time.
As seen in file examples/example.cpp we can use the same C++ example from the
spdlite docs, either directly accessing an instantiated logger object as in the first
example
// Direct use of the logger object as in the upstream docs
rspdlite::logger.info("Welcome to spdlog!");
rspdlite::logger.error("Some error message with arg: {}", 1);
rspdlite::logger.warn("Easy padding in numbers like {:08d}", 12);
rspdlite::logger.critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
rspdlite::logger.info("Support for floats {:03.2f}", 1.23456);
rspdlite::logger.info("Positional args are {1} {0}..", "too", "supported");
rspdlite::logger.info("{:<30}", "left aligned");
or, preferably, by using a level of indirection we added as shown in the second example (of which just show the first half)
rspdlite::log_critical("-- level to warn");
rspdlite::set_level(spdlite::level::warn);
rspdlite::log_info("Some more");
rspdlite::log_error("Some error message with arg: {}", 1);
rspdlite::log_warn("Easy padding in numbers like {:08d}", 12);
rspdlite::log_critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
rspdlite::log_info("Support for floats {:03.2f}", 1.23456);
rspdlite::log_info("Positional args are {1} {0}..", "too", "supported");
rspdlite::log_info("{:<30}", "left aligned");
The default logging level is 'info' so all these message appear by default. As seen in examples/example.cpp we can also alter the logging level dynamically to show more (or fewer) messages.
By using a #define also supplying a filename, we can (at compile-time) select a 'file sink'. (There
is also a rotating file sink (with a capacity limit) we could enable similarly.) Note that the
#define needs to precede the #include <rspdlite>.
#defined RSPDLITE_FILE_SINK "/tmp/my_log_file.txt"
#include <rspdlite>
Following the nice user experience offered by spdl, we similarly provide access via the 'package name colon colon' patter from R as can be seen in examples/example.R
rspdlite::log_critical("-- level to debug")
rspdlite::set_level("debug")
rspdlite::log_info("Some more at info")
rspdlite::log_error("Some error message with arg: {}", 1)
rspdlite::log_error("Some error message with more args: {} and {}", 1, "abc")
rspdlite::log_critical("-- level to error and calling example1 and example2")
rspdlite::set_level("error")
As for its cousin R packages, the format string here 'resembles' the full C++ format string, but remains simpler. As we convert each argument directly to a character (then passed on to the C++ layer) we do not offer the extra formatting options available directly from C++. We have not found this to be an issue.
spdlite is by design and choice very lightweight---and very fast. There are
essentially no state variables, and nearly every part of behaviour is governed by compile-time
choices as core ability of modern C++ is to (allow to) shift as much as possible to compile-time
rather than run-time. For example the basic formatting abilities are passed through by invoking the
constructor of the relevant object. So when we set one of the options individually (say via
set_precision() to change the time precision from the sane default of milliseconds to either
micro- or nanoseconds), an earlier formatting choice of also showing the thread id will fall back to
its default of 'off'. The best way around this is to use set_format() and simulatenously set all
values one desires changes.
Similarly, while we offer the null sink in this package too, truly lightweight performance (as
demonstrated by the upstream benchmarks) only happen when the logging level is also set to off.
But that cannot easily be accomplished just by instantiating the sink. (We have ideas about to
accommodate this in the R package so we may get to this.)
While this is an R package, we stress the 'symmetric' use from C++. Given the lightweight nature,
more advanced use from C++ is equally possible. One could for example use the (coloured) console
sink (but make sure not to use it in code going to CRAN as it will have issues with R CMD check),
or the rotating sink we do not (yet ?) expose---or the fact that from C++ it is easy to a)
instantiate multiple sinks (!!) or b) easily create multiple loggers at different levels or
configurations. The spdlite documentation has of course more on this.
The tl package offers a thin shim permitting more compact access from R
and C++ via its shorter namespace and shorter function names offering
e.g. tl::debug("Notice") from both R and C++ with the same formatting
options as rspdlite which is used under its hood.
Gabi Melman is the main author of both spdlog and spdlite.
Victor Zverovich is the main author of the embedded fmt library
offered as an alternative to the C++20 library std::format.
Dirk Eddelbuettel is author of this package and the R integration.
spdlog, spdlite and fmt are under the MIT license.
rspdlite is released under the GNU GPL, version 2 or later, just like R and Rcpp.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.