Одновременный вход в консоль и в файл с использованием Boost

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

Текст журнала должен отправляться в оба приемника одновременно, однако мне нужно немного по-другому отформатировать вывод консоли (так как он будет просматриваться пользователем). Я смог получить основы ведения журнала для двух отдельных приемников, работающих с помощью пример кода повышения. Это слишком сложно для того, что мне нужно сделать, и это действительно сбивает с толку, когда речь идет о доступе к соответствующему регистратору. Все, что мне нужно сделать, это иметь сообщения с отметками времени, отправляемые в файл журнала, и иметь ту же информацию без отметок времени или новых строк, отправляемых в журнал консоли (явно добавляя новые строки только так, как я обычно делаю с << std::endl). Я действительно хотел бы придерживаться структуры ведения журналов boost, поскольку она обеспечивает гибкость для расширения в будущем.

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

Любая помощь или, что еще лучше, действительно простой пример кода для работы с основами будет высоко оценен.

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

// Setup the common formatter for all sinks
logging::formatter fmt = expr::stream
    << std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
    << ": <" << severity << ">\t"
    << expr::if_(expr::has_attr(tag_attr))
    [
        expr::stream << "[" << tag_attr << "] "
    ]
    << expr::smessage;

// Initialize sinks
typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;

boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>("full.log"));
sink->set_formatter(fmt);
// register the full log sink
logging::core::get()->add_sink(sink);

sink = boost::make_shared<text_sink>();
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>("important.log"));
// sink->set_formatter(fmt); (I removed this to not have any special formatting hopefully)
sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));
// register the important log sink
logging::core::get()->add_sink(sink);

// Add attributes
logging::add_common_attributes();

person johnco3    schedule 11.06.2014    source источник


Ответы (1)


Вот пример кода, в котором используется глобальный регистратор Boost-Log. Я вызываю init_term() для инициализации регистратора терминала и init_logfile() для инициализации файла журнала.

Обратите внимание на вызов auto_flush(true)

// Logging macro
#define LOG(level) BOOST_LOG_SEV(global_logger::get(), level)
// Initializing global boost::log logger
typedef boost::log::sources::severity_channel_logger_mt<
    severity_level, std::string> global_logger_type;

BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(global_logger, global_logger_type)
{
    return global_logger_type(boost::log::keywords::channel = "global_logger");
}

// Initialize terminal logger
void init_term()
{
    // create sink to stdout
    boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    sink->locked_backend()->add_stream(
        boost::shared_ptr<std::ostream>(&out, boost::empty_deleter()));

    // flush
    sink->locked_backend()->auto_flush(true);

    // format sink
    sink->set_formatter
    (
        /// TODO add your desired formatting
    );

    // filter
    // TODO add any filters

    // register sink
    bl::core::get()->add_sink(sink);
}

// Initialize logfile
void init_logfile(const std::string& logfilename)
{
    // create sink to logfile
    boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    sink->locked_backend()->add_stream(
        boost::make_shared<std::ofstream>(logfilename.c_str()));

    // flush
    sink->locked_backend()->auto_flush(true);

    // format sink
    sink->set_formatter
    (
        /// TODO add your desired formatting
    );

    // filter
    // TODO add any filters

    // register sink
    bl::core::get()->add_sink(sink);
}
person Dimitris Dakopoulos    schedule 17.06.2014
comment
Димитрис, спасибо, у вас есть соответствующие #includes ‹boost/log..› и псевдонимы пространств имен. Также я забыл упомянуть, что я разрабатываю для Visual Studio 2013, я, как правило, получаю множество предупреждений, которые необходимо подавить с помощью предупреждения #pragma (disable: 4510 4610) (обернутого внутри предупреждения #pragma (push/pop) - но я все еще получаю много предупреждений. - person johnco3; 17.06.2014
comment
На самом деле я только что понял, как заставить его скомпилироваться с помощью оптимизации времени выполнения Boost Log, за которую я проголосовал. Очень полезно. Так же по поводу автосброса - для моего лога это не так важно - а для терминала есть. На самом деле у меня будет 2 отдельных файла журнала (используемых для разных целей ведения журнала - так что фактически 3 приемника журнала - 2 файла и консоль) - я не могу понять, как заставить LOG (что-то) направлять его вывод соответствующий выход назначения. Я думаю, что это связано с фильтрами, но это не очевидно, я уверен, что мне понадобится какой-то атрибут, чтобы помочь - person johnco3; 17.06.2014
comment
@johnco3, я думаю, что решение кроется в фильтрах. Если вы хотите, чтобы LOG(LEVEL) сначала записывало в конкретный приемник, вам нужно передать severity_level всем функциям инициализации приемника, которые вы не хотите, чтобы они существовали, а затем отфильтровать их, например. sink->set_filter(boost::log::expressions::attr<severity_level>("Severity") == LEVEL); - person Dimitris Dakopoulos; 18.06.2014
comment
Я согласен, вчера я написал класс Logger с нуля, чтобы попытаться решить эту проблему с частичным успехом - используя несколько channel_loggers. Элементы channel_logger класса 3 регистратора инициализируются именем канала. Код очень похож на: ttps://github.com/boostorg/log/blob/master/example/doc/sources_net_connection_dynamic_chan.cpp Меня смущает то, почему необходимо вызывать BOOST_LOG_ATTRIBUTE_KEYWORD(channel, Channel, std: :string) в строке 33 - я думал, что канал был предопределенным атрибутом, связанным с регистраторами каналов. - person johnco3; 18.06.2014