This is an effort to illustrate the usage of Boost.Log with simple logging, filtering and the usage of a configuration file to manage logging level.
Basic logging
The first thing we need is to setup our project. I will use CMake as the build system.
To add Boost.Log library, is necessary to use the FindBoost module. Since version 3.30, it’s recommended to use the configuration file provided by Boost, avoiding a warning due to the policy CMP0167, therefore the usage of CONFIG
in find_package
.
cmake_minimum_required(VERSION 3.28)
project(MyLogger)
set(CMAKE_CXX_STANDARD 20)
find_package(Boost 1.83.0 REQUIRED COMPONENTS log log_setup CONFIG)
add_executable(custom_logger)
target_sources(custom_logger PRIVATE main.cpp)
target_link_libraries(custom_logger PRIVATE ${Boost_LIBRARIES})
custom_logger
is the target (binary/executable).
Let’s take the Tutorial logging as reference.
Add a main.cpp
to have a minimal running project. BOOST_LOG_TRIVIAL
is intended to be a basic way to add console logs.
#include <boost/log/trivial.hpp>
int main() {
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
Adding filters
In order to show logs based on its severity, we use filters
. At this time, I show the logs with info
severity or higher.
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>
namespace logging = boost::log;
namespace trivial = logging::trivial;
void init() {
auto filter = trivial::severity >= trivial::info;
logging::core::get()->set_filter(filter);
}
int main() {
init();
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
External configuration
Sometimes is preferable to change the filtering level by updating parameters external to the binary, like a configuration file. For this purpose, use the Boost.PropertyTree
. It’s possible to use a variety of formats, I particularly stick with INI file.
Afterwards, create an app.ini
in your project root.
[logs]
level = debug
The executable will look up that file in the same path, as a result is necessary to copy it there every time it changes when compiling, thus I use a CMake target.
set(PROPERTIES_FILE ${CMAKE_SOURCE_DIR}/app.ini)
add_custom_target(copy_properties ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PROPERTIES_FILE}
$<TARGET_FILE_DIR:custom_logger>
DEPENDS ${PROPERTIES_FILE}
)
add_dependencies(custom_logger copy_properties)
The read_ini
loads the app.ini
. To access a value, we use get
on the property tree.
We want to allow trace
or debug
levels, with info
as fallback.
#include <boost/property_tree/ini_parser.hpp>
namespace logging = boost::log;
namespace trivial = logging::trivial;
trivial::severity_level getFilteringLevel() {
namespace property_tree = boost::property_tree;
property_tree::ptree pt;
read_ini("app.ini", pt);
const auto level = pt.get<std::string>("logs.level");
if (level == "trace")
return trivial::trace;
if (level == "debug")
return trivial::debug;
return trivial::info;
}
void init() {
auto filter = trivial::severity >= getFilteringLevel();
logging::core::get()->set_filter(filter);
}
In future posts I will elaborate formatting to have custom logs messages and sinks such as console and files to have different “channels” to send your logs.
Leave a Reply