Передача сообщения журнала форсирования в качестве аргумента вызова функции - PullRequest
0 голосов
/ 28 апреля 2020

Я написал код C ++ для захвата различных уровней серьезности сообщений. Для этого я использовал https://github.com/gklingler/simpleLogger.

  • Файл simpleLogger.cpp

    #include "simpleLogger.h"
    
    #include <boost/log/core/core.hpp>
    #include <boost/log/expressions/formatters/date_time.hpp>
    #include <boost/log/expressions.hpp>
    #include <boost/log/sinks/sync_frontend.hpp>
    #include <boost/log/sinks/text_ostream_backend.hpp>
    #include <boost/log/sources/severity_logger.hpp>
    #include <boost/log/support/date_time.hpp>
    #include <boost/log/trivial.hpp>
    #include <boost/core/null_deleter.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/shared_ptr.hpp>
    #include <fstream>
    #include <ostream>
    
    
    namespace logging = boost::log;
    namespace src = boost::log::sources;
    namespace expr = boost::log::expressions;
    namespace sinks = boost::log::sinks;
    namespace attrs = boost::log::attributes;
    
    BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
    
    BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
        src::severity_logger_mt<boost::log::trivial::severity_level> logger;
    
        // add attributes
        logger.add_attribute("LineID", attrs::counter<unsigned int>(1));     // lines are sequentially numbered
        logger.add_attribute("TimeStamp", attrs::local_clock());             // each log line gets a timestamp
    
        // add a text sink
        typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    
        // add a logfile stream to our sink
        sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(LOGFILE));
    
        // add "console" output stream to our sink
        sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
    
        // specify the format of the log message
        logging::formatter formatter = expr::stream
            << std::setw(7) << std::setfill('0') << line_id << std::setfill(' ') << " | "
            << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
            << "[" << logging::trivial::severity << "]"
            << " - " << expr::smessage;
        sink->set_formatter(formatter);
    
        // only messages with severity >= SEVERITY_THRESHOLD are written
        sink->set_filter(severity >= SEVERITY_THRESHOLD);
    
        // "register" our sink
        logging::core::get()->add_sink(sink);
    
        return logger;
    }
    
  • Файл simpleLogger.h

    #ifndef simpleLogger_h__
    #define simpleLogger_h__
    
    #define BOOST_LOG_DYN_LINK // necessary when linking the boost_log library dynamically
    
    #include <boost/log/trivial.hpp>
    #include <boost/log/sources/global_logger_storage.hpp>
    
    // the logs are also written to LOGFILE
    #define LOGFILE "logfile.log"
    
    // just log messages with severity >= SEVERITY_THRESHOLD are written
    #define SEVERITY_THRESHOLD logging::trivial::warning
    
    // register a global logger
    BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>)
    
    // just a helper macro used by the macros below - don't use it in your code
    #define LOG(severity) BOOST_LOG_SEV(logger::get(),boost::log::trivial::severity)
    
    // ===== log macros =====
    #define LOG_TRACE   LOG(trace)
    #define LOG_DEBUG   LOG(debug)
    #define LOG_INFO    LOG(info)
    #define LOG_WARNING LOG(warning)
    #define LOG_ERROR   LOG(error)
    #define LOG_FATAL   LOG(fatal)
    
    #endif
    
  • Файл app.cpp

    #include "simpleLogger.h"
    
    int main() {
      LOG_TRACE << "this is a trace message";
      LOG_DEBUG << "this is a debug message";
      LOG_WARNING << "this is a warning message";
      LOG_ERROR << "this is an error message";
      LOG_FATAL << "this is a fatal error message";
      return 0;
    }
    

Поэтому я отправил бы свое сообщение как

LOG_INFO << "This is info message"

Но это лог-сообщение мне нужно отправить в какую-то другую функцию в качестве аргумента. В этой функции я буду вносить некоторые другие изменения в сообщение журнала, например "This is info message".

Как отправить сообщение журнала повышения в качестве аргумента функции? Я не нашел соответствующего источника для этого.

Заранее спасибо

1 Ответ

0 голосов
/ 28 апреля 2020

Вы можете определить «ленивого актера», которого вы можете поместить в упакованное выражение форматера. Это в значительной степени ракетостроение Boost Log, но, возможно, этот простой пример поможет вам:

auto reverse_expr = [](auto fmt) {
    return expr::wrap_formatter([fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
        logging::formatting_ostream tmp;
        std::string text;
        tmp.attach(text);

        fmt(rec, tmp);

        std::reverse(text.begin(), text.end());
        strm << text;
    });
};

// specify the format of the log message
logging::formatter formatter = expr::stream
    << reverse_expr(expr::stream << std::setw(7) << std::setfill('0') << line_id) 
    << " | "
    << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
    << "[" << logging::trivial::severity << "]"
    << " - " 
    << reverse_expr(expr::stream << expr::smessage);

sink->set_formatter(formatter);

Результаты:

3000000 | 2020-04-28, 16:15:15.779204 [warning] - egassem gninraw a si siht                         
4000000 | 2020-04-28, 16:15:15.779308 [error] - egassem rorre na si siht                            
5000000 | 2020-04-28, 16:15:15.779324 [fatal] - egassem rorre lataf a si siht                       

Примечания: это не будет чрезмерно эффективен, потому что включает в себя «двойную буферизацию» с временным потоком, но он очень гибок, как вы можете видеть.

ОБНОВЛЕНИЕ Улучшенный метод ниже (см. раздел БОНУС )

Несколько более обобщенная c реализация может выглядеть следующим образом:

template <typename F> struct Xfrm {
    Xfrm(F f) : _f(f) {}
    F _f;

    template <typename E> auto operator[](E fmt) const {
        return expr::wrap_formatter(
            [f=_f,fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
                logging::formatting_ostream tmp;
                std::string text;
                tmp.attach(text);
                fmt(rec, tmp);

                strm << f(text);
            });
    }
};

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

std::string reversed(std::string v) {
    std::reverse(v.begin(), v.end());
    return v;
}
std::string to_uppercase(std::string v) {
    for (auto& ch : v) ch = std::toupper(ch);
    return v;
}

Который вы могли бы затем использовать как:

logging::formatter formatter = expr::stream
    << Xfrm(reversed)[expr::stream << std::setw(7) << std::setfill('0') << line_id]
    << " | "
    << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
    << "[" << logging::trivial::severity << "]"
    << " - " 
    << Xfrm(to_uppercase)[ expr::stream << expr::smessage ];

Или поместите манипуляторы в ваш заголовочный файл:

static inline constexpr Xfrm UC{to_uppercase};
static inline constexpr Xfrm REV{reversed};

Так что вы можете использовать его заранее:

logging::formatter formatter = expr::stream
    << REV[ expr::stream << std::setw(7) << std::setfill('0') << line_id ]
    << " | "
    << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
    << "[" << logging::trivial::severity << "]"
    << " - " 
    << UC[ expr::stream << expr::smessage ];

Вывод

3000000 | 2020-04-28, 16:36:46.184958 [warning] - THIS IS A WARNING MESSAGE
4000000 | 2020-04-28, 16:36:46.185034 [error] - THIS IS AN ERROR MESSAGE
5000000 | 2020-04-28, 16:36:46.185045 [fatal] - THIS IS A FATAL ERROR MESSAGE

Демонстрация в реальном времени

Live On Wandbox

  • Файл simpleLogger.h

    #ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
    #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
    
    #pragma once
    #define BOOST_LOG_DYN_LINK \
        1 // necessary when linking the boost_log library dynamically
    
    #include <boost/log/sources/global_logger_storage.hpp>
    #include <boost/log/trivial.hpp>
    
    // the logs are also written to LOGFILE
    #define LOGFILE "logfile.log"
    
    // just log messages with severity >= SEVERITY_THRESHOLD are written
    #define SEVERITY_THRESHOLD logging::trivial::warning
    
    // register a global logger
    BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<
                                        boost::log::trivial::severity_level>)
    
    // just a helper macro used by the macros below - don't use it in your code
    #define LOG(severity) \
        BOOST_LOG_SEV(logger::get(), boost::log::trivial::severity)
    
    // ===== log macros =====
    #define LOG_TRACE LOG(trace)
    #define LOG_DEBUG LOG(debug)
    #define LOG_INFO LOG(info)
    #define LOG_WARNING LOG(warning)
    #define LOG_ERROR LOG(error)
    #define LOG_FATAL LOG(fatal)
    
    #endif
    
  • Файл simpleLogger.cpp

    #include "simpleLogger.h"
    
    #include <boost/core/null_deleter.hpp>
    #include <boost/log/core/core.hpp>
    #include <boost/log/expressions.hpp>
    #include <boost/log/expressions/formatters/char_decorator.hpp>
    #include <boost/log/expressions/formatters/date_time.hpp>
    #include <boost/log/sinks/sync_frontend.hpp>
    #include <boost/log/sinks/text_ostream_backend.hpp>
    #include <boost/log/sources/severity_logger.hpp>
    #include <boost/log/support/date_time.hpp>
    #include <boost/log/trivial.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/phoenix.hpp>
    #include <boost/phoenix/function.hpp>
    #include <boost/shared_ptr.hpp>
    #include <fstream>
    #include <ostream>
    
    namespace logging = boost::log;
    namespace src = boost::log::sources;
    namespace expr = boost::log::expressions;
    namespace sinks = boost::log::sinks;
    namespace attrs = boost::log::attributes;
    
    BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity",
                                logging::trivial::severity_level)
    
    namespace /*extend locally*/ {
        template <typename F> struct Xfrm {
            constexpr Xfrm(F f) : _f(f) {}
            F _f;
    
            template <typename E> auto operator[](E fmt) const {
                return expr::wrap_formatter(
                    [f = _f, fmt](logging::record_view const& rec,
                                  logging::formatting_ostream& strm) {
                        logging::formatting_ostream tmp;
                        std::string text;
                        tmp.attach(text);
                        fmt(rec, tmp);
    
                        strm << f(text);
                    });
            }
        };
    
        std::string reversed(std::string v) {
            std::reverse(v.begin(), v.end());
            return v;
        }
        std::string to_uppercase(std::string v) {
            for (auto& ch : v) {
                ch = std::toupper(ch);
            }
            return v;
        }
    
        inline constexpr Xfrm UC{ to_uppercase };
        inline constexpr Xfrm REV{ reversed };
    } // namespace
    
    BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
        src::severity_logger_mt<boost::log::trivial::severity_level> logger;
    
        // add attributes
        logger.add_attribute("LineID", attrs::counter<unsigned int>(
                                           1)); // lines are sequentially numbered
        logger.add_attribute(
            "TimeStamp", attrs::local_clock()); // each log line gets a timestamp
    
        // add a text sink
        using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>;
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    
        // add a logfile stream to our sink
        sink->locked_backend()->add_stream(
            boost::make_shared<std::ofstream>(LOGFILE));
    
        // add "console" output stream to our sink
        sink->locked_backend()->add_stream(
            boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
    
        // specify the format of the log message
        logging::formatter formatter =
            expr::stream
            << REV[expr::stream << std::setw(7) << std::setfill('0') << line_id]
            << " | "
            << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
            << "[" << logging::trivial::severity << "]"
            << " - " << UC[expr::stream << expr::smessage];
    
        sink->set_formatter(formatter);
        // only messages with severity >= SEVERITY_THRESHOLD are written
        sink->set_filter(severity >= SEVERITY_THRESHOLD);
    
        // "register" our sink
        logging::core::get()->add_sink(sink);
    
        return logger;
    }
    
  • Файл test.cpp

    #include "simpleLogger.h"
    
    int main() {
        LOG_TRACE << "this is a trace message";
        LOG_DEBUG << "this is a debug message";
        LOG_WARNING << "this is a warning message";
        LOG_ERROR << "this is an error message";
        LOG_FATAL << "this is a fatal error message";
        return 0;
    }
    

БОНУС: более эффективный

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

struct OddEvenId {
    void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
        auto q = rec.attribute_values()[logging::aux::default_attribute_names::line_id()];
        auto vr = q.extract<uint32_t>();
        if (!vr.empty()) {
            strm << std::setw(4) << (vr.get<uint32_t>()%2? "Odd":"Even");
        }
    }
};

struct QuotedMessage {
    void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
        auto q = rec.attribute_values()[logging::aux::default_attribute_names::message()];
        auto vr = q.extract<std::string>();
        if (!vr.empty())
            strm << std::quoted(vr.get<std::string>());
    }
};

static inline auto oddeven_id = expr::wrap_formatter(OddEvenId{});
static inline auto qmessage = expr::wrap_formatter(QuotedMessage{});

Теперь мы можем просто сказать qmessage вместо expr::smessage, чтобы получить значение цитируемого сообщения:

Live On Wandbox

logging::formatter formatter = expr::stream
    << oddeven_id
    << " | "
    << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
    << "[" << logging::trivial::severity << "]"
    << " - " << qmessage;

Отпечатки

 Odd | 2020-04-28, 17:21:12.619565 [warning] - "this is a warning message"
Even | 2020-04-28, 17:21:12.619666 [error] - "this is an error message"
 Odd | 2020-04-28, 17:21:12.619684 [fatal] - "this is a fatal error message"
...