Add class 'BoostLog'.

This commit is contained in:
luocai 2023-07-21 10:47:31 +08:00
commit ef8d5b0b03
9 changed files with 528 additions and 0 deletions

5
CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.15)
project(Kylin)
add_subdirectory(Universal)

68
Universal/BoostLog.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "BoostLog.h"
#include <boost/log/expressions.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#ifdef ANDROID
#include "AndroidBoostLog.h"
#endif
static void logFormatter(boost::log::record_view const &record, boost::log::formatting_ostream &ostream);
BOOST_LOG_GLOBAL_LOGGER_INIT(location_logger, LocationLogger<boost::log::trivial::severity_level>) {
LocationLogger<boost::log::trivial::severity_level> lg;
auto consoleSink = boost::log::add_console_log();
consoleSink->set_formatter(&logFormatter);
boost::log::add_common_attributes();
return lg;
}
void initBoostLog(const std::string &name, boost::log::trivial::severity_level filter) {
static bool initialized = false;
using namespace boost::log;
if (!initialized) {
boost::log::core::get()->set_filter(boost::log::trivial::severity >= filter);
add_common_attributes();
#ifdef ANDROID
using AndroidSink = boost::log::sinks::synchronous_sink<AndroidSinkBackend>;
auto sink = boost::make_shared<AndroidSink>();
sink->set_formatter(&androidLogFormatter);
boost::log::core::get()->add_sink(sink);
#else
std::ostringstream oss;
oss << name << "_%Y-%m-%d_%H.%M.%S_%N.log";
auto fileSink =
add_file_log(keywords::file_name = oss.str(), keywords::auto_flush = true, keywords::target = "logs");
fileSink->set_formatter(&logFormatter);
#endif
initialized = true;
}
}
static void logFormatter(const boost::log::record_view &record, boost::log::formatting_ostream &ostream) {
using namespace boost::log;
std::string level;
boost::log::formatting_ostream oss(level);
oss << "[" << record[boost::log::trivial::severity] << "]";
auto dateTimeFormatter = expressions::stream << expressions::format_date_time<boost::posix_time::ptime>(
"TimeStamp", "[%m-%d %H:%M:%S.%f]");
dateTimeFormatter(record, ostream);
ostream << "[" << boost::log::extract<boost::log::attributes::current_thread_id::value_type>("ThreadID", record)
<< "]";
ostream << std::setw(8) << std::left << level;
auto &&category = record[AmassKeywords::category];
if (!category.empty()) ostream << " [" << category << "]";
const auto &filename = record[AmassKeywords::filename];
if (!filename.empty()) {
#ifdef QT_CREATOR_CONSOLE
ostream << " [file:///" << filename << ":" << record[AmassKeywords::line] << "] ";
#else
ostream << " [" << filename << ":" << record[AmassKeywords::line] << "] ";
#endif
}
// Finally, put the record message to the stream
ostream << record[expressions::smessage];
}

182
Universal/BoostLog.h Normal file
View File

@ -0,0 +1,182 @@
#ifndef BOOSTLOG_H
#define BOOSTLOG_H
#include <boost/log/common.hpp>
#include <boost/log/core.hpp>
#include <boost/log/detail/light_rw_mutex.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/thread.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif
/* what i expected.
[2020-09-08 17:12:35.229152] [0x00007fa07fc94800] [info] Hello world!
[2020-09-08 17:12:35.229204] [0x00007fa07fc94800] [info] server ip: 127.0.0.1,port: 8080
[2020-09-08 17:12:35.229211] [0x00007fa07fc94800] [warning] server ip: 127.0.0.1,port: 8080
[2020-09-08 17:12:35.230961] [0x00007fa07fc94800] [info] server start successful .
*/
// clang-format off
#define LOG_IF(lvl,predic) \
if(predic) LOG(lvl)
#define LOG_IF_EVERY_N(lvl,predic,n) \
if(predic) for(static NumberPredicator pred(n);(!!pred);) \
LOG(lvl)
#define LOG_EVERY_N(lvl,n) \
LOG_IF_EVERY_N(lvl,true,n)
#define LOG_IF_FIRST_N(lvl,predic,n) \
if(predic) for(static CountPredicator pred(n);(!!pred);) \
LOG(lvl)
#define LOG_FIRST_N(lvl,n) \
LOG_IF_FIRST_N(lvl,true,n)
#define LOG_IF_EVERY_T(lvl,predic,n) \
if(predic) for(static TimePredicator pred(n);(!!pred);) \
LOG(lvl)
#define LOG_EVERY_T(lvl,n) \
LOG_IF_EVERY_T(lvl,true,n)
// clang-format on
#ifndef LOG_FILTER_LEVEL
#define LOG_FILTER_LEVEL (boost::log::trivial::info)
#endif
void initBoostLog(
const std::string &name = "app",
boost::log::trivial::severity_level filter = static_cast<boost::log::trivial::severity_level>(LOG_FILTER_LEVEL));
namespace AmassKeywords {
BOOST_PARAMETER_KEYWORD(FilenameNS, FilenameTag)
BOOST_PARAMETER_KEYWORD(LineNS, LineTag)
BOOST_PARAMETER_KEYWORD(CategoryNS, CategoryTag)
BOOST_LOG_ATTRIBUTE_KEYWORD(filename, "Filename", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(line, "Line", size_t)
BOOST_LOG_ATTRIBUTE_KEYWORD(category, "Category", std::string)
constexpr const char *pathEnd(const char *path) {
return *path ? pathEnd(path + 1) : path;
}
constexpr bool hasSlant(const char *path) {
return (*path == '\\' || *path == '/') ? true : (*path ? hasSlant(path + 1) : false);
}
constexpr const char *rightSlant(const char *path) {
return (*path == '\\' || *path == '/') ? (path + 1) : rightSlant(path - 1);
}
constexpr const char *fileName(const char *path) {
return hasSlant(path) ? rightSlant(pathEnd(path)) : path;
}
} // namespace AmassKeywords
template <typename BaseT>
class CategoryTaggerFeature : public BaseT {
public:
typedef typename BaseT::char_type char_type;
typedef typename BaseT::threading_model threading_model;
CategoryTaggerFeature() = default;
CategoryTaggerFeature(const CategoryTaggerFeature &obj);
template <typename ArgsT>
CategoryTaggerFeature(const ArgsT &args);
typedef typename boost::log::strictest_lock<boost::lock_guard<threading_model>, typename BaseT::open_record_lock,
typename BaseT::add_attribute_lock,
typename BaseT::remove_attribute_lock>::type open_record_lock;
protected:
template <typename ArgsT>
boost::log::record open_record_unlocked(const ArgsT &args);
};
struct CategoryTagger : public boost::mpl::quote1<CategoryTaggerFeature> {};
template <typename BaseT>
class FilenameTaggerFeature : public BaseT {
public:
typedef typename BaseT::char_type char_type;
typedef typename BaseT::threading_model threading_model;
FilenameTaggerFeature() = default;
FilenameTaggerFeature(const FilenameTaggerFeature &obj);
template <typename ArgsT>
FilenameTaggerFeature(const ArgsT &args);
typedef typename boost::log::strictest_lock<boost::lock_guard<threading_model>, typename BaseT::open_record_lock,
typename BaseT::add_attribute_lock,
typename BaseT::remove_attribute_lock>::type open_record_lock;
protected:
template <typename ArgsT>
boost::log::record open_record_unlocked(const ArgsT &args);
};
struct FilenameTagger : public boost::mpl::quote1<FilenameTaggerFeature> {};
template <typename BaseT>
class LineTaggerFeature : public BaseT {
public:
typedef typename BaseT::char_type char_type;
typedef typename BaseT::threading_model threading_model;
LineTaggerFeature() = default;
LineTaggerFeature(const LineTaggerFeature &obj);
template <typename ArgsT>
LineTaggerFeature(const ArgsT &args);
typedef typename boost::log::strictest_lock<boost::lock_guard<threading_model>, typename BaseT::open_record_lock,
typename BaseT::add_attribute_lock,
typename BaseT::remove_attribute_lock>::type open_record_lock;
protected:
template <typename ArgsT>
boost::log::record open_record_unlocked(const ArgsT &args);
};
struct LineTagger : public boost::mpl::quote1<LineTaggerFeature> {};
template <typename LevelT = int>
class LocationLogger
: public boost::log::sources::basic_composite_logger<
char, LocationLogger<LevelT>, boost::log::sources::multi_thread_model<boost::log::aux::light_rw_mutex>,
boost::log::sources::features<boost::log::sources::severity<LevelT>, FilenameTagger, LineTagger,
CategoryTagger>> {
typedef typename LocationLogger::logger_base base_type;
public:
BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE(LocationLogger)
};
BOOST_LOG_GLOBAL_LOGGER(location_logger, LocationLogger<boost::log::trivial::severity_level>)
// clang-format off
#define LOG( lvl) \
BOOST_LOG_STREAM_WITH_PARAMS(::location_logger::get(),\
(::boost::log::keywords::severity = ::boost::log::trivial::lvl) \
(AmassKeywords::FilenameTag = (AmassKeywords::fileName(__FILE__))) \
(AmassKeywords::LineTag = __LINE__))
#define LOG_CAT( lvl, cat) \
BOOST_LOG_STREAM_WITH_PARAMS(::location_logger::get(),\
(::boost::log::keywords::severity = ::boost::log::trivial::lvl) \
(AmassKeywords::FilenameTag = (AmassKeywords::fileName(__FILE__))) \
(AmassKeywords::LineTag = __LINE__) \
(AmassKeywords::CategoryTag = #cat))
// clang-format on
#include "BoostLog.inl"
#endif // BOOSTLOG_H

155
Universal/BoostLog.inl Normal file
View File

@ -0,0 +1,155 @@
#ifndef BOOSTLOG_INL
#define BOOSTLOG_INL
#include "BoostLog.h"
#include "StreamFormat.h"
#include "boost/log/sources/basic_logger.hpp"
#include "boost/log/trivial.hpp"
#include "boost/scope_exit.hpp"
#include <chrono>
#include <iomanip>
template <typename Derived>
class LogPredicator {
public:
operator bool() const {
bool ret = m_status;
m_status = !m_status;
if (ret) {
ret = doOnceInterface();
if (!ret) m_status = true;
}
return ret;
}
inline bool doOnceInterface() const { return static_cast<const Derived *>(this)->doOnce(); }
protected:
mutable bool m_status = true;
};
class NumberPredicator : public LogPredicator<NumberPredicator> {
public:
NumberPredicator(int limit) : m_limit(limit) {}
inline bool doOnce() const { return (m_current++ % m_limit) == 0; }
private:
int m_limit;
mutable int m_current{0};
};
class CountPredicator : public LogPredicator<CountPredicator> {
public:
CountPredicator(int limit) : m_limit(limit) {}
inline bool doOnce() const { return m_current++ < m_limit; }
private:
int m_limit;
mutable int m_current{0};
};
class TimePredicator : public LogPredicator<TimePredicator> {
public:
TimePredicator(int milliseconds) : m_limit(std::chrono::milliseconds(milliseconds)) {}
inline bool doOnce() const {
auto now = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_current);
bool ret = duration >= m_limit;
if (ret) m_current = now;
return ret;
}
private:
std::chrono::milliseconds m_limit;
mutable decltype(std::chrono::system_clock::now()) m_current{std::chrono::system_clock::now()};
};
template <typename BaseT>
CategoryTaggerFeature<BaseT>::CategoryTaggerFeature(const CategoryTaggerFeature &obj)
: BaseT(static_cast<const BaseT &>(obj)) {
}
template <typename BaseT>
template <typename ArgsT>
CategoryTaggerFeature<BaseT>::CategoryTaggerFeature(const ArgsT &args) : BaseT(args) {
}
template <typename BaseT>
template <typename ArgsT>
boost::log::record CategoryTaggerFeature<BaseT>::open_record_unlocked(const ArgsT &args) {
std::string tag_value = args[AmassKeywords::CategoryTag | std::string()];
boost::log::attribute_set &attrs = BaseT::attributes();
boost::log::attribute_set::iterator tag = attrs.end();
if (!tag_value.empty()) {
// Add the tag as a new attribute
std::pair<boost::log::attribute_set::iterator, bool> res =
BaseT::add_attribute_unlocked("Category", boost::log::attributes::constant<std::string>(tag_value));
if (res.second) tag = res.first;
}
BOOST_SCOPE_EXIT_TPL((&tag)(&attrs)) {
if (tag != attrs.end()) attrs.erase(tag);
}
BOOST_SCOPE_EXIT_END
return BaseT::open_record_unlocked(args);
}
template <typename BaseT>
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const FilenameTaggerFeature &obj)
: BaseT(static_cast<const BaseT &>(obj)) {
}
template <typename BaseT>
template <typename ArgsT>
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const ArgsT &args) : BaseT(args) {}
template <typename BaseT>
template <typename ArgsT>
boost::log::record FilenameTaggerFeature<BaseT>::open_record_unlocked(const ArgsT &args) {
std::string tag_value = args[AmassKeywords::FilenameTag | std::string()];
boost::log::attribute_set &attrs = BaseT::attributes();
boost::log::attribute_set::iterator tag = attrs.end();
if (!tag_value.empty()) {
// Add the tag as a new attribute
std::pair<boost::log::attribute_set::iterator, bool> res =
BaseT::add_attribute_unlocked("Filename", boost::log::attributes::constant<std::string>(tag_value));
if (res.second) tag = res.first;
}
BOOST_SCOPE_EXIT_TPL((&tag)(&attrs)) {
if (tag != attrs.end()) attrs.erase(tag);
}
BOOST_SCOPE_EXIT_END
return BaseT::open_record_unlocked(args);
}
template <typename BaseT>
template <typename ArgsT>
LineTaggerFeature<BaseT>::LineTaggerFeature(const ArgsT &args) : BaseT(args) {}
template <typename BaseT>
LineTaggerFeature<BaseT>::LineTaggerFeature(const LineTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {}
template <typename BaseT>
template <typename ArgsT>
boost::log::record LineTaggerFeature<BaseT>::open_record_unlocked(const ArgsT &args) {
size_t tag_value = args[AmassKeywords::LineTag | size_t()];
boost::log::attribute_set &attrs = BaseT::attributes();
boost::log::attribute_set::iterator tag = attrs.end();
// Add the tag as a new attribute
std::pair<boost::log::attribute_set::iterator, bool> res =
BaseT::add_attribute_unlocked("Line", boost::log::attributes::constant<size_t>(tag_value));
if (res.second) tag = res.first;
BOOST_SCOPE_EXIT_TPL((&tag)(&attrs)) {
if (tag != attrs.end()) attrs.erase(tag);
}
BOOST_SCOPE_EXIT_END
return BaseT::open_record_unlocked(args);
}
#endif // BOOSTLOG_INL

10
Universal/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
find_package(Boost REQUIRED COMPONENTS log log_setup)
add_library(Universal
BoostLog.h BoostLog.inl BoostLog.cpp
StreamFormat.h StreamFormat.inl StreamFormat.cpp
)
target_link_libraries(Universal
PRIVATE ${Boost_LIBRARIES}
)

View File

@ -0,0 +1,11 @@
#include "StreamFormat.h"
namespace std {
std::ostream &operator<<(std::ostream &stream, const std::chrono::milliseconds &element) {
stream << element.count() << "ms";
return stream;
}
std::ostream &operator<<(std::ostream &stream, const std::chrono::microseconds &element) {
stream << element.count() << "us";
return stream;
}
} // namespace std

47
Universal/StreamFormat.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef STREAMFORMAT_H
#define STREAMFORMAT_H
#include <chrono>
#include <list>
#include <ostream>
#include <vector>
namespace std {
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &stream, const std::pair<T1, T2> &element) {
stream << "[" << element.first << "," << element.second << "]";
return stream;
}
template <typename T>
std::ostream &operator<<(std::ostream &stream, const std::vector<T> &element) {
stream << "[";
for (const auto &e : element) {
stream << e << ",";
}
stream << "]";
return stream;
}
template <typename T>
std::ostream &operator<<(std::ostream &stream, const std::list<T> &element) {
stream << "[";
for (const auto &e : element) {
stream << e << ",";
}
stream << "]";
return stream;
}
std::ostream &operator<<(std::ostream &stream, const std::chrono::milliseconds &element);
std::ostream &operator<<(std::ostream &stream, const std::chrono::microseconds &element);
} // namespace std
template <typename T1, typename T2>
bool operator==(const std::pair<T1, T2> &lhs, const std::pair<T1, T2> &rhs) {
return lhs.first == rhs.first && lhs.second == rhs.second;
}
#include "StreamFormat.inl"
#endif // STREAMFORMAT_H

View File

@ -0,0 +1 @@
#include "StreamFormat.h"

49
resource/deploy.sh Normal file
View File

@ -0,0 +1,49 @@
#!/bin/bash
base_path=$(pwd)
libraries_root="/opt/Libraries"
if [ $base_path == /home/* ] || [ -n "${DRONE}" ]; then
build_path=${base_path}/build
else
build_path=/tmp/build
fi
echo "build directory: $build_path"
function cmake_scan() {
if [ ! -d ${build_path} ]; then
mkdir ${build_path}
fi
/opt/Qt/Tools/CMake/bin/cmake \
-G Ninja \
-S ${base_path} \
-B ${build_path} \
-DCMAKE_BUILD_TYPE=Debug \
-DBOOST_ROOT=${libraries_root}/boost_1_82_0
}
function build() {
if [ ! -f "${build_path}/CMakeCache.txt" ]; then
cmake_scan
fi
if [ $? -ne 0 ]; then
exit 1
fi
/opt/Qt/Tools/CMake/bin/cmake \
--build ${build_path} \
--target all
}
function main() {
local cmd=$1
shift 1
case $cmd in
build)
build
;;
*)
build
;;
esac
}
main $@