В блоках Boost.Log (объекты, которые записывают файлы журналов) и регистраторы (объекты, через которые ваше приложение испускает записи журнала) напрямую не подключены, и любой приемник может получать сообщение журнала из любого регистратора. Для того, чтобы записи из определенных журналов появлялись только в частности приемниках, вам придется организовать фильтры в раковинах, чтобы ненужные записи были подавлены для приемников, которые не должны их получать и передавать другим. Чтобы отличать записи от разных регистраторов, регистраторы должны добавлять разные атрибуты к каждой сделанной им записи. Обычно это достигается с помощью channels - регистраторы свяжут атрибут канала, который может использоваться для идентификации регистратора в фильтрах, форматировщиках или приемниках. Каналы могут быть combined с другими атрибутами, такими как уровни серьезности. Следует отметить, что каналы и уровни серьезности ортогональны, и любой канал может иметь записи любого уровня. Значения различных атрибутов анализируются отдельно в фильтрах.
Так, например, если вы хотите записать записи из канала A в файл A.log, а с канала B - в B.log, вы должны создать два приемника - по одному для каждого файла и установить их фильтрует соответственно.
BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_channel, "Channel", std::string)
logging::add_file_log(
keywords::file_name = "A.log",
keywords::filter = a_channel == "A");
logging::add_file_log(
keywords::file_name = "B.log",
keywords::filter = a_channel == "B");
Смотрите документацию о defining attribute keywords и convenience setup functions. Теперь вы можете создавать регистраторы для каждого канала, а записи журналов будут направляться в приемники с помощью фильтров.
typedef src::severity_channel_logger< severity_level, std::string > logger_type;
logger_type lg_a(keywords::channel = "A");
logger_type lg_b(keywords::channel = "B");
BOOST_LOG_SEV(lg_a, info) << "Hello, A.log!";
BOOST_LOG_SEV(lg_b, info) << "Hello, B.log!";
Вы можете иметь столько регистраторы для одного канала, как вы хотите - сообщений от каждого из них будет направлена на одной раковине.
Однако здесь есть две проблемы.Во-первых, библиотека не знает природы канала и считает ее просто непрозрачной. Он не знает иерархии каналов, поэтому «A» и «A.bb» считаются разными и не связанными каналами. Во-вторых, настройка таких фильтров, как описано выше, может быть затруднена, если вы хотите записать несколько каналов в один файл (например, «A» и «A.bb»). Все станет еще сложнее, если вы захотите разного уровня серьезности для разных каналов.
Если иерархия каналов не имеет для вас значения, вы можете сделать конфигурацию фильтра проще с помощью severity threshold filter. С помощью этого фильтра вы можете установить минимальный уровень серьезности для каждого соответствующего канала. Если вы хотите наследовать пороговые значения серьезности в подканалах, тогда ваш единственный способ - написать собственный фильтр; библиотека не предоставляет это из коробки.
Существует несколько способов создания фильтра, но это сводится к написанию функции, которая принимает значения атрибута из записей журнала и возвращает true
, если эта запись прошла фильтр и false
в противном случае. Возможно, самый простой способ показан в Tutorial, см. Пример с phoenix::bind
от Boost.Phoenix.
bool my_filter(
logging::value_ref< severity_level, tag::a_severity > const& level,
logging::value_ref< std::string, tag::a_channel > const& channel,
channel_hierarchy const& thresholds)
{
// See if the log record has the severity level and the channel attributes
if (!level || !channel)
return false;
std::string const& chan = channel.get();
// Parse the channel string, look for it in the hierarchy
// and find out the severity threshold for this channel
severity_level threshold = thresholds.find(chan);
return level.get() >= threshold;
}
Теперь настройка поглотителей изменятся, как это использовать ваш новый фильтр:
logging::add_file_log(
keywords::file_name = "A.log",
keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_A));
logging::add_file_log(
keywords::file_name = "B.log",
keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_B));
Здесь hierarchy_A
и hierarchy_B
являются вашими структурами данных, используемых для хранения критичности пороговых значений для различных каналов для два лога файлы.
Спасибо за ваш ответ. Я ценю вашу помощь, чтобы начать меня. Иерархия каналов важна для меня, поэтому мне придется идти с дополнительной сложностью. С контуром, который вы мне дали, мне кажется, что моя проблема сводится к написанию структур данных и логики для поддержки фильтров порога и приемника с дочерними узлами, наследующими от родительских узлов. С log4j они подчеркивают, как у них есть оптимальная схема для этого. Знаете ли вы, выполнил ли кто-то подобную схему с помощью библиотеки Boost.Log? – Phil
Нет, я не думаю, что знаю, кто это делает. Однако создание структуры данных для порогов серьезности выглядит не так сложно - вы можете реализовать ее с помощью множества вложенных 'std :: map' /' std :: unordered_map' или с Boost.PropertyTree (http: // www. boost.org/doc/libs/1_60_0/doc/html/property_tree.html), который уже имеет сериализацию/десериализацию в/из текста. –
Как определяется channel_hierarchy? –