UE4_Boost.logAndUnreal Engine4Log

最近在研究怎么把boost跟UE4做结合使用,目前已经成功的将一些好用的boost library应用在专案上了,就先来整理一下相关该注意的技术事项吧。

首先我尝试导入的是boost的log机制,虽然UE4本身已经有LOG系统了,但稍微survey了一下,看起来其实还是不太完整,而且log写起来其实还蛮费劲的;如果能把boost.log导进来的话,不仅输出log的时候会更方便,而且又能够利用boost.log本身上提供的许多强大的功能。

其实boost.log还蛮容易跟其他引擎做结合的,我们只需要写一个backend,并将其所绑定的sink加入到boost.log系统里面就行了。而这个backend要做的事情也很简单,它只需要负责把传进来的讯息再原封不动的传给UE4原本的log系统。听起来是不是很简单?先让我们来看一下下面这段code:

namespace  logging  = boost::log;
namespace  attrs  = boost::log::attributes;
namespace  src  = boost::log::sources;
namespace  sinks  = boost::log::sinks;
namespace  expr  = boost::log::expressions;
namespace  keywords  = boost::log::keywords;
void  Horizo​​nCore::initDefaultLog ( const std::string& fileLogFullPath,
        horizo​​n::Horizo​​nLogImpFunc_t&& logFunc,
        boost::log::trivial::severity_level logFilterLevel,
        boost::log::trivial::severity_level fileLogFilterLevel){
        logging::core::get ()-> remove_all_sinks ();
        // typedef boost::log::sinks::synchronous_sink<Horizo​​nLogBasicBackend> Horizo​​nLogSink;
        typedef   boost:: log ::sinks::synchronous_sink<Horizo​​nLogBasicFormatedBackend> Horizo​​nLogSink;
        auto sink = boost::make_shared<Horizo​​nLogSink>();

        sink-> locked_backend ()-> setLogMessageImplemen (std::forward<horizo​​n::Horizo​​nLogImpFunc_t>(logFunc));
        boost::log::core::get ()-> add_sink (sink);

        boost::log::add_common_attributes ();
        auto logFormat = expr::format ( " %1%: [%2%] [%3%]: %4% " )
            % expr::attr< unsigned  int >( " RecordID " )
            % expr::attr< boost:: log ::trivial::severity_level >( " Severity " )
            % expr::attr< boost::posix_time::ptime >( " TimeStamp " )
            % expr::smessage;
        // % expr::attr< unsigned int >("Function");

        sink-> set_formatter (logFormat);
        sink-> set_filter (logging::trivial::severity >= logFilterLevel);

        logging::core::get ()-> add_global_attribute ( " RecordID " , attrs::counter< unsigned  int >());
        logging::core::get ()-> add_global_attribute ( " TimeStamp " , attrs::local_clock ());

        logging::add_file_log (
            keywords::file_name = fileLogFullPath
            , keywords::auto_flush = true
            , keywords::filter = expr::attr< boost:: log ::trivial::severity_level >( " Severity " ) >= fileLogFilterLevel
            , keywords::format = (logFormat)
            );
    }

 

把sink加入到log系统的方式如下:

typedef boost::log::sinks::synchronous_sink< Horizo nLogBasicFormatedBackend > Horizo nLogSink;

boost::log::core::get()->add_sink(sink);

红色的部份是我们自己写的backend,其实作如下:

# define  HORIZON_LOG_BUFFER_LEN  4 * 1024  // 4KB
 using Horizo​​nLogImpFunc_t = std::function< void ( const std::string&)>;
 class  Horizo​​nLogBasicFormatedBackend : public  boost ::log::sinks::basic_formatted_sink_backend < char > {
    public:
        void  consume ( const boost::log::r​​ecord_view& rec, string_type const & msg);
        inline  void  setLogMessageImplemen (Horizo​​nLogImpFunc_t&& impl) { logMessageImplement = std::move (impl); };
    private:
        void  logMessage ( const boost::log::r​​ecord_view& rec, const std::string& msg);        
    private:
        Horizo​​nLogImpFunc_t logMessageImplement;
    };
    
    
    void  Horizo​​nLogBasicFormatedBackend::logMessage ( const boost::log::r​​ecord_view& rec, const std::string& msg){
        // std::cout << msg;
        if (msg. size () > HORIZON_LOG_BUFFER_LEN){
            logMessageImplement (msg. substr ( 0 , HORIZON_LOG_BUFFER_LEN - 1 ));
            logMessage (rec, msg. substr (HORIZON_LOG_BUFFER_LEN - 1 , msg. size ()));

        }
        else {
            logMessageImplement (msg);
            logMessageImplement ( " \n " );
        }
    }
    void  Horizo​​nLogBasicFormatedBackend::consume ( const boost::log::r​​ecord_view& rec, string_type const & msg){

        const std::string msgStr = msg;
        logMessage (rec, msgStr);
    }

 

这个backend留有一个接口:

inline void setLogMessageImplemen(Horizo nLogImpFunc_t&& impl);

目的就是留一个log的实作方式给引擎方去设定,我们需要做的就是从UE4丢一个std::function到backend就行了。设定方式如下:

void  logMessageImplement ( const std::string& msg) {
    FString str = UTF8_TO_TCHAR (msg. c_str ());
    UE_LOG (LogTemp, Log, TEXT ( " %s " ), *str);
    // FPlatformMisc::LowLevelOutputDebugString(TEXT("Horizo​​nLogBasicBackend test"));
    // FPlatformMisc::LowLevelOutputDebugString(UTF8_TO_TCHAR(msg.c_str()));
}

FString savedPath = FPaths::ConvertRelativePathToFull(FPaths::GameLogDir());
std::string logFilePath = std::string(TCHAR_TO_UTF8(*savedPath)) + " Horizo​​nLog.txt " ;

horizo​​n::Horizo​​nCore::GetInstance ()->initDefaultLog(logFilePath, std::bind(logMessageImplement, std::placeholders::_1));   

然后由于UE4中的check macro跟boost中的有冲突,因此在include boost 相关的header之前需先做下面这件事:

#pragma push_macro(“check”)
#undef check
//include your boost header here
#pragma pop_macro(“check”)

接下来我们就可以很方便的使用boost所提供的log机制了:

static  class  Horizo​​nDevNull
{
} horizo​​n_dev_null;


// BOOST_LOG_FUNCTION()
// BOOST_LOG_FUNCTION()
# define  HORIZON_LOG_FUNCTION  "   [ " << __FILE__ << " ( " << __LINE__ << " ) " << " : " << BOOST_CURRENT_FUNCTION << " ]   "

# if defined(NDEBUG) || defined(_NDEBUG)

template < typename T>
Horizo​​nDevNull & operator <<(Horizo​​nDevNull & dest, T)
{
    return dest;
}
# define  HORIZON_TRACE      horizo​​n_dev_null
# else
# define  HORIZON_TRACE      BOOST_LOG_TRIVIAL (trace) << HORIZON_LOG_FUNCTION << "\n"
#endif
# define  HORIZON_DEBUG      BOOST_LOG_TRIVIAL (debug) << HORIZON_LOG_FUNCTION << "\n"
# define  HORIZON_INFO       BOOST_LOG_TRIVIAL (info) << HORIZON_LOG_FUNCTION << "\n"
# define  HORIZON_WARNING    BOOST_LOG_TRIVIAL (warning) << HORIZON_LOG_FUNCTION << "\n"
# define  HORIZON_ERROR      BOOST_LOG_TRIVIAL (error) << HORIZON_LOG_FUNCTION << "\n"
# define  HORIZON_FATAL      BOOST_LOG_TRIVIAL (fatal) << HORIZON_LOG_FUNCTION << "\n"

================================================== ============

HORIZON_INFO << "HELLO WORLD!!!";

 

只是目前的机制只能用在64位元的版本中,不知道为什么当运行起32位元的时候总是会在下面这行code crash:

if (_Myostr.rdbuf() != 0)

再往上追一层可以看到:

sentry guard(*this);

目前还在研究到底发生什么是,看code似乎是里面某个typedef std::basic_ostream< char, std::char_traits<char> > ostream_type;在呼叫rdbuf的时候叫到Myostr的dangle reference。

Author: 90cg