2015-07-27 2 views
0

У меня есть исполняемый файл как для Linux, так и для Windows с тремя DLL/разделяемыми библиотеками, и я использую boost.log для ведения журнала. Я хочу иметь отдельный файл для каждого модуля. Мой подход заключался в создании severity_channel_logger_mt для каждого модуля, построенного в одноэлементном режиме для каждого модуля с именем канала.усиление фильтрации канала регистрации в разделяемых библиотеках не работает как ожидалось на linux

class LogSingleton final 
{ 
    src::severity_channel_logger_mt<logging::trivial::severity_level, std::string> slg_; 
... 
protected: 
    LogSingleton() : slg_(boost::log::keywords::channel = "ModuleOne") {} 
public: 
    src::severity_channel_logger_mt<logging::trivial::severity_level, std::string>& logger() { return slg_; } 
... 
};  

Каждый модуль затем создает свой собственный радиатор файла по инициализации и устанавливает фильтр канала, чтобы сообщения журнала только тот модуль должны достичь этой раковины.

logging::add_file_log(logging::keywords::file_name = "module_one.log", 
    logging::keywords::open_mode = std::ios::app, 
    ->set_filter(logging::trivial::severity >= level 
    && expr::attr<std::string>("Channel") == "ModuleOne"); 

Я создал собственный макрос конкретного модуля для ведения журнала, который переходит к правильному регистратору.

#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((LogSingleton::instance()->logger()), (::boost::log::keywords::severity = ::boost::log::trivial::lvl)) 

, который используется, как это:

BLOG(info) << "Hello world"; 

Хотя на лесозаготовительных работах Windows, как и ожидалось, на Linux файлы журнала для ModuleOne (инициализируется первый) и ModuleThree (инициализирован третий) не получают журнал Сообщения. ModuleTwo работает правильно. Код регистрации для всех трех модулей идентичен, кроме имени файла журнала, имен каналов и имени класса singleton.

Я задаюсь вопросом, есть ли проблема с моим макросом, но любые идеи приветствуются вместе с комментариями к подходу.

Update

я сократил эту проблему до минимального, например, все в одном исполняемом файле. Проблема с общей библиотекой была неправильной. Проблема заключается в том, что если я создам файл severity_channel_logger в одноэлементном режиме, регистрация завершится с ошибкой. Если я использую локальный регистратор, работает журнал. Даже если я создаю регистратор в одноэлементном режиме и не использую его, он запрещает работу локального регистратора. Несмотря на прослеживание кода, я не понимаю, почему это происходит. (Платформа = Fedora 21, GCC 4.9.2, форсирует 1.58)

#include <boost/config.hpp> 
#include <boost/filesystem.hpp> 
#include <boost/log/core.hpp> 
#include <boost/log/trivial.hpp> 
#include <boost/log/sources/severity_feature.hpp> 
#include <boost/log/sources/severity_channel_logger.hpp> 
#include <boost/log/expressions.hpp> 
#include <boost/log/expressions/formatters/date_time.hpp> 
#include <boost/log/support/date_time.hpp> 
#include <boost/log/attributes/current_thread_id.hpp> 
#include <boost/log/utility/setup/file.hpp> 
#include <boost/log/utility/setup/console.hpp> 
#include <boost/log/utility/setup/common_attributes.hpp> 

namespace logging = boost::log; 
namespace expr = boost::log::expressions; 
namespace src = boost::log::sources; 
namespace fs = boost::filesystem; 

class LogSingleton 
{ 
    src::severity_channel_logger<logging::trivial::severity_level, std::string> slg_; 
    static LogSingleton* instance_; 

    LogSingleton(const LogSingleton&); 
    LogSingleton& operator=(const LogSingleton&); 

protected: 
    LogSingleton() : slg_(boost::log::keywords::channel = "Core") {} 
public: 
    src::severity_channel_logger<logging::trivial::severity_level, std::string>& logger() 
    { 
     return slg_; 
    } 

    static LogSingleton* instance() 
    { 
     if (!instance_) 
     { 
      instance_ = new LogSingleton; 
     } 
     return instance_; 
    } 
}; 

LogSingleton* LogSingleton::instance_ = nullptr; 

// 1. doesn't work 
//#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((LogSingleton::instance()->logger()), (::boost::log::keywords::severity = ::boost::log::trivial::lvl)) 

// access to logger via reference. Works if it is passed a ref to logger declared in main. Doesn't work if it is ref to logger in singleton 
#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((rlogger), (::boost::log::keywords::severity = ::boost::log::trivial::lvl)) 

int main(int argc, char **argv) 
{ 
    logging::add_common_attributes(); 
    logging::trivial::severity_level level = logging::trivial::trace; 
     auto formatter = expr::stream 
     << "[" << expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%dT%H:%M:%S.%f") 
     << "] (" << logging::trivial::severity 
     << "): " << expr::message; 

    fs::path plog = fs::system_complete(fs::path("..")); 
    if (!fs::exists(plog) || !fs::is_directory(plog)) 
     throw std::invalid_argument("Log directory doesn't exist, or path isn't a directory"); 
    plog /= "core.log"; 

    logging::add_file_log(logging::keywords::file_name = plog.string(), 
     logging::keywords::open_mode = std::ios::app, 
     logging::keywords::format = formatter) 
     ->set_filter(logging::trivial::severity >= level && expr::attr<std::string>("Channel") == "Core"); 

    // this works with rlogger macro variant 
    src::severity_channel_logger<logging::trivial::severity_level, std::string> logger(boost::log::keywords::channel = "Core"); 
    auto& rlogger = logger; 

    // 2. this doesn't work, with same macro 
    //auto& rlogger = LogSingleton::instance()->logger(); 

    // 3. just creating the singleton, before or after the creation of the local logger, stops logging from working 
    //LogSingleton::instance(); 

    BLOG(info) << "Hello world"; 

    return 0; 
} 

В настоящее время написано, этот пример работает с местным регистратором. Комментарий с меткой 1 - это вариант макроса, который использует singleton logger direct (fail). Кроме того, комментирует местный регистратор и дает ссылку на синглтон. (комментарий 2). Комментарий, отмеченный 3, показывает, что просто создание одноточечного регистратора приводит к сбою регистрации в логгере.

+0

Немного больше деталей. Я не могу использовать один глобальный регистратор в Windows, поскольку они не разделяются между разными dll, если они определены в одном из них (единственное место, где я контролирую). Кроме того, добавление канала в макрос не изменяет поведение, как в '#define BLOG (lvl) BOOST_LOG_STREAM_WITH_PARAMS ((LogSingleton :: instance() -> logger()), (:: boost :: log :: keywords :: channel = "ModuleOne") (:: boost :: log :: keywords :: severity = :: boost :: log :: trivial :: lvl)) ' – nostep

ответ

2

Проблема связана с утечкой LogSingleton в ваш код. Синглтон содержит регистратор, который не позволяет уничтожить ядро ​​и стоки регистрации. Запись журнала, которую вы делаете, правильно обрабатывается и записывается в поток файлов, но не сбрасывается (т. Е. Заканчивается в буферах потока файлов). Обычно потоковые буферы очищаются при уничтожении потока (в случае Boost.Log это происходит, когда стоки уничтожаются, при завершении программы) или после каждой записи журнала, если вы включаете автозагрузку (передайте аргумент keywords::auto_flush = true функции add_file_log вызов).

Вы можете исправить это, если вы измените LogSingleton::instance_ как std::unique_ptr.

+0

Конечно, теперь это имеет смысл. Спасибо, Андрей. – nostep

0

Сначала убедитесь, что вы связываете все свои модули с общими библиотеками Boost.Log, а не статическими. Это requirement Boost.Log при использовании в многомодульных приложениях на всех платформах. Кроме того, убедитесь, что вы построили все свои модули с тем же набором макросов настройки, которые влияют на Boost.Log.

Далее, если вы управляете символом visibility, убедитесь, что вы экспортируете информационные символы типа из ваших библиотек. Это особенно важно для типов исключений и типов значений атрибутов. Если вы не знакомы с видимостью символов, вы можете начать с экспорта всех символов, что может быть достигнуто путем создания ваших модулей без каких-либо флагов -fvisibility*.

Если все вышеперечисленное не помогает, вам придется отлаживать ваше приложение. В частности, убедитесь, что установленные фильтры фактически передают записи журнала. Вы можете временно удалить фильтры, чтобы узнать, помогает ли это. Если это еще не помогает, пройдите через пошаговый код в отладчике, чтобы увидеть, в каких точках записи журнала удалены.

+0

Андрей, большое спасибо за эти предложения, которые я дважды проверил. Я теперь уменьшил проблему до одного исполняемого примера, включенного в обновление исходного вопроса выше. – nostep

+0

См. Мой другой ответ. –

 Смежные вопросы

  • Нет связанных вопросов^_^