Как зарегистрировать номер строки кодера в журнале повышения 2.0?

Могу ли я использовать для этого атрибут LineID? Я надеюсь, что я мог бы использовать для этого sint::set_formatter вместо использования

__LINE__

а также

__FILE__

в каждом операторе журнала.


person Dean Chen    schedule 28.02.2014    source источник
comment
Это должно быть так очевидно, но это не так. Я уже довольно давно ломаю голову над этим. Вам повезло с тех пор, как вы разместили свой вопрос? Если вы узнали, ответьте на свой вопрос и помогите товарищу!   -  person ixe013    schedule 11.03.2014
comment
См. также stackoverflow.com/questions/22095667/   -  person Jason Harrison    schedule 27.11.2018


Ответы (6)


Я боролся с этим, пока не нашел этот фрагмент

#define LFC1_LOG_TRACE(logger) \
BOOST_LOG_SEV(logger, trivial::trace) << "(" << __FILE__ << ", " << __LINE__ << ") "

Работает как шарм

person Chris    schedule 31.03.2014
comment
Я попробовал область имен, она работает, но более сложная. см. мой код в моем сообщении. blog.csdn.net/csfreebird/article/details/20213349#t9 У меня та же мысль, что и у вас, просто определите свои собственные макросы и используйте FILE, LINE - person Dean Chen; 01.04.2014
comment
Да, после публикации я тоже пошел по этому пути. В итоге получилось что-то немного другое, чем у вас, поскольку у меня есть named_scope как часть моего средства форматирования. Затем он может создавать вложенные вызовы области видимости. - person Chris; 01.04.2014
comment
Следует отметить, что это решение, хотя и очень простое, децентрализует управление форматированием журнала. Используя named_scope, как предложено @DeanChen, или global_attributes, как предложено ниже, сохраните все форматирование в одном месте под контролем библиотеки Boost.Log. - person mattdibi; 13.02.2019

Атрибут LineID — это порядковый номер, который увеличивается для каждого сообщения журнала. Так что вы не можете использовать это.

Вы можете использовать атрибуты для регистрации номеров строк и т. д. Это позволяет вам гибко форматировать с использованием строки формата, тогда как при использовании ответа Криса ваш формат фиксируется.

Зарегистрируйте глобальные атрибуты в вашей функции инициализации ведения журнала:

using namespace boost::log;
core::get()->add_global_attribute("Line", attributes::mutable_constant<int>(5));
core::get()->add_global_attribute("File", attributes::mutable_constant<std::string>(""));
core::get()->add_global_attribute("Function", attributes::mutable_constant<std::string>(""));

Установка этих атрибутов в макросе ведения журнала:

#define logInfo(methodname, message) do {                           \
    LOG_LOCATION;                                                       \
    BOOST_LOG_SEV(_log, boost::log::trivial::severity_level::info) << message; \
  } while (false)

#define LOG_LOCATION                            \
  boost::log::attribute_cast<boost::log::attributes::mutable_constant<int>>(boost::log::core::get()->get_global_attributes()["Line"]).set(__LINE__); \
  boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["File"]).set(__FILE__); \
  boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["Function"]).set(__func__);

Не совсем красиво, но это работает, и это был долгий путь для меня. Жаль, что Boost не предлагает эту функцию из коробки.

do {... } while(false) делает макрос семантически нейтральным.

person Horus    schedule 25.08.2015

Решение, показанное Крисом, работает, но если вы хотите настроить формат или выбрать, какая информация будет отображаться в каждом приемнике, вам нужно использовать изменяемые постоянные атрибуты:

   logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));
   logging::core::get()->add_global_attribute("Line", attrs::mutable_constant<int>(0));

Затем вы создаете собственный макрос, который включает следующие новые атрибуты:

// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
   BOOST_LOG_STREAM_WITH_PARAMS( \
      (logger), \
         (set_get_attrib("File", path_to_filename(__FILE__))) \
         (set_get_attrib("Line", __LINE__)) \
         (::boost::log::keywords::severity = (boost::log::trivial::sev)) \
   )

// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
   auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_global_attributes()[name]);
   attr.set(value);
   return attr.get();
}

// Convert file path to only the filename
std::string path_to_filename(std::string path) {
   return path.substr(path.find_last_of("/\\")+1);
}

Следующий полный исходный код создает два приемника. Первый использует атрибуты File и Line, второй нет.

#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/attributes/mutable_constant.hpp>

namespace logging  = boost::log;
namespace attrs    = boost::log::attributes;
namespace expr     = boost::log::expressions;
namespace src      = boost::log::sources;
namespace keywords = boost::log::keywords;

// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
   BOOST_LOG_STREAM_WITH_PARAMS( \
      (logger), \
         (set_get_attrib("File", path_to_filename(__FILE__))) \
         (set_get_attrib("Line", __LINE__)) \
         (::boost::log::keywords::severity = (boost::log::trivial::sev)) \
   )

// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
   auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_global_attributes()[name]);
   attr.set(value);
   return attr.get();
}

// Convert file path to only the filename
std::string path_to_filename(std::string path) {
   return path.substr(path.find_last_of("/\\")+1);
}

void init() {
   // New attributes that hold filename and line number
   logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));
   logging::core::get()->add_global_attribute("Line", attrs::mutable_constant<int>(0));

   // A file log with time, severity, filename, line and message
   logging::add_file_log (
    keywords::file_name = "sample.log",
    keywords::format = (
     expr::stream
      << expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f")
      << ": <" << boost::log::trivial::severity << "> "
      << '['   << expr::attr<std::string>("File")
               << ':' << expr::attr<int>("Line") << "] "
      << expr::smessage
    )
   );
   // A console log with only time and message
   logging::add_console_log (
    std::clog,
    keywords::format = (
     expr::stream
      << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      << " | " << expr::smessage
    )
   );
   logging::add_common_attributes();
}

int main(int argc, char* argv[]) {
   init();
   src::severity_logger<logging::trivial::severity_level> lg;

   CUSTOM_LOG(lg, debug) << "A regular message";
   return 0;
}

Оператор CUSTOM_LOG(lg, debug) << "A regular message"; генерирует два вывода, записывая файл журнала в этом формате...

2015-10-15_15:25:12.743153: <debug> [main.cpp:61] A regular message

... и выводит в консоль это:

2015-10-15 16:58:35 | A regular message
person Guillermo Ruiz    schedule 15.10.2015
comment
Возникла проблема завершения программы с приведенным выше кодом в программе, которая использует многопоточность. Программа завершилась, и точка останова оказалась в середине функций журнала повышения в Visual Studio. После того, как я перестал использовать вышеуказанный метод, проблема исчезла. В многопоточной среде требуется осторожность. - person Hill; 17.11.2017
comment
@Hil Для многопоточных сред вам нужно severity_logger_mt, а не severity_logger. У каждого регистратора есть многопоточный вариант (постфикс _mt). - person Samuel; 21.01.2020
comment
при установке значения строки возникает проблема с завершением... это происходит при замене значения строки параметром шаблона rhs. я не знаю, как это решить - person ahmed allam; 12.03.2020
comment
при установке значения строки возникает проблема с завершением... это происходит при замене значения строки параметром шаблона rhs. я не знаю, как это решить... Исключение: нарушение доступа для чтения. правая была 0x8. - person ahmed allam; 12.03.2020
comment
@ahmedallam Вам нужно заменить это (set_get_attrib("Line", __LINE__)) на (set_get_attrib("Line", (int)__LINE__)) - person gyro; 30.11.2020

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

Предполагая, что серьезность_logger идентифицируется с регистратором переменной:

boost::log::record rec = logger.open_record(boost::log::keywords::severity = <some severity value>);
if (rec)
{
    rec.attribute_values().insert(boost::log::attribute_name("Line"),
        boost::log::attributes::constant<unsigned int>(__LINE__).get_value());
    ... other stuff appended to record ...
}

Вышеупомянутое, конечно, будет упаковано в удобный макрос.

Позже вы можете отобразить этот атрибут с помощью пользовательского средства форматирования для приемника:

sink->set_formatter( ...other stuff... << expr::attr<unsigned int>("Line") << ...other stuff... );

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

person Leon    schedule 25.08.2015

Ради потомства — я сделал этот набор макросов для очень простых нужд ведения журнала, который мне хорошо послужил — для простых нужд ведения журнала. Но они иллюстрируют, как это сделать в целом, и эта концепция легко работает с Boost. Они должны быть локальными для одного файла (который выполняется в нескольких процессах, иногда в нескольких потоках в нескольких процессах). Они сделаны для относительной простоты, а не для скорости. Их безопасно вставлять в операторы if и т. д., чтобы не украсть остальное. В начале функции, в которой нужно войти, вызывают

GLogFunc("function name");

Затем можно сделать это, чтобы зарегистрировать полную строку:

GLogL("this is a log entry with a string: " << some_string);

Они такие -

#define GLogFunc(x)     std::stringstream logstr; \
                        std::string logfunc; \
                        logfunc = x

#define GLog(x)         do { logstr << x; } while(0)

#define GLogComplete    do { \
                            _log << "[PID:" << _my_process << " L:" << __LINE__ << "] ((" << logfunc << ")) " << logstr.str() << endl; \
                             logstr.str(""); \
                             _log.flush(); \ 
                        } while(0)

#define GLogLine(x)     do { GLog(x); GLogComplete; } while(0)
#define GLogL(x)        GLogLine(x)
#define GLC             GLogComplete

Можно также построить журнал на несколько строк...

GLog("I did this.");
// later
GLog("One result was " << some_number << " and then " << something_else);
// finally
GLog("And now I'm done!");
GLogComplete;

Каким бы ни был поток _log (я открываю его как файл в конструкторе класса, который в данном случае гарантированно безопасен), он выводится следующим образом:

[PID:4848 L:348] ((SetTextBC)) ERROR: bad argument row:0 col:-64

И они могут быть условно отключены, а все потери производительности сведены на нет с помощью символа во время компиляции, например:

#ifdef LOGGING_ENABLED
... do the stuff above ...
#else

#define GLogFunc(x)
#define GLog(x)
#define GLogComplete
#define GLogLine(x) 
#define GLogL(x)

#endif
person std''OrgnlDave    schedule 26.08.2015

Вот мое решение.

Код установки

auto formatter =
    expr::format("[ %3% %1%:%2% :: %4%]")
    % expr::attr< std::string >("File")
    % expr::attr< uint32_t >("Line")
    % expr::attr< boost::posix_time::ptime >("TimeStamp")
    % expr::smessage
    ;

/* stdout sink*/
boost::shared_ptr< sinks::text_ostream_backend > backend =
    boost::make_shared< sinks::text_ostream_backend >();
backend->add_stream(
    boost::shared_ptr< std::ostream >(&std::clog, NullDeleter()));

// Enable auto-flushing after each log record written
backend->auto_flush(true);

// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink2_t;
boost::shared_ptr< sink2_t > sink_text(new sink2_t(backend));

logging::add_common_attributes();

sink_text->set_formatter(formatter);

Код использования журнала (короткая версия):

rec.attribute_values().insert("File", attrs::make_attribute_value(std::string(__FILE__))); \

полная версия :

#define LOG(s, message) { \
  src::severity_logger< severity_level > slg; \
  logging::record rec = slg.open_record(keywords::severity = s); \
  if (rec) \
  { \
    rec.attribute_values().insert("File", attrs::make_attribute_value(boost::filesystem::path(__FILE__).filename().string())); \
    rec.attribute_values().insert("Line", attrs::make_attribute_value(uint32_t(__LINE__))); \
    logging::record_ostream strm(rec); \
    strm << message; \
    strm.flush(); \
    slg.push_record(boost::move(rec)); \
  } \

}\

Если я определяю глобальный атрибут (как люди советовали ранее), т.е.

logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));

затем я получаю пустые файлы/стиринг.

person Ivan Baidakou    schedule 06.12.2016