Passing boost log message as an argument to function call

后端 未结 1 1549
再見小時候
再見小時候 2021-01-27 10:55

I have written C++ code for capturing the various severity levels of messages. I have used https://github.com/gklingler/simpleLogger for this.

  • File simple

相关标签:
1条回答
  • 2021-01-27 11:16

    You can define a "lazy actor" that you can put into a wrapped formatter expression. This is pretty much the rocket science of Boost Log, but perhaps this simple example will help you:

    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);
    

    Results in:

    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                       
    

    Notes: it will not be overly efficient because it involves "double buffering" with a temporary stream, but it is highly flexible, as you can see.

    UPDATE Improved method below (see section BONUS)

    A slightly more generic implementation might look like:

    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);
                });
        }
    };
    

    So you can actually use it for other functions:

    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;
    }
    

    Which you could then use like:

    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 ];
    

    Or put the manipulators in your header file:

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

    So you can use it pre-fab:

    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 ];
    

    Output

    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 Demo

    Live On Wandbox

    • File 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
      
    • File 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;
       }
      
    • File 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;
       }
      

    BONUS: More Efficient

    To avoid double-buffering, we can avoid using the original formatter. This works best for known attributes:

    struct OddEvenId {
        void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
            auto vr = line_id.or_throw()(rec);
            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 vr = expr::smessage.or_throw()(rec);
            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{});
    

    Now we can simply say qmessage instead of expr::smessage to get the quoted message value:

    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;
    

    Prints

     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"
    
    0 讨论(0)
提交回复
热议问题