一个log的wapper类可以用来方便地对接各种logger,但是这对logger的性能来说可能会有影响。下面测试了Linux环境下log4cplus及其wapper类的性能。
打印100万条日志 | log4cplus | log4cplus with wapper | |
Linux | 同步logger | 4s | 4.5s |
异步logger | 1.9s | 1.9s |
由此可见,在Linux下使用该wapper类造成的性能损失为12.5%。 在Windows下使用该wapper造成的性能损失可能会非常大,当然这只是根据一些测试的推测,并没有精确的数据。
//logger.h
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <string>
#include <memory>
namespace mytest {
class ILogger {
public:
static const unsigned short LOG_FATAL = 5;
static const unsigned short LOG_ERROR = 4;
static const unsigned short LOG_WARNING = 3;
static const unsigned short LOG_INFO = 2;
static const unsigned short LOG_DEBUG = 1;
static const unsigned short LOG_VERBOSE = 0;
virtual ~ILogger() {}
virtual void Log(int level, const char* filename, int line,
const char* message) = 0;
};
namespace internal {
// Declare the LogFinisher class
class LogFinisher;
/// Defines the LogMessage class used to serialize the variables to be logged
class LogMessage {
public:
LogMessage(ILogger* logger, int level, const char* filename, int line);
LogMessage(std::shared_ptr<ILogger> logger, int level,
const char* filename, int line);
~LogMessage();
LogMessage& operator<<(const ::std::string& value);
LogMessage& operator<<(const char* value);
LogMessage& operator<<(char value);
LogMessage& operator<<(int value);
LogMessage& operator<<(unsigned int value);
LogMessage& operator<<(long value);
LogMessage& operator<<(unsigned long value);
LogMessage& operator<<(long long value);
LogMessage& operator<<(unsigned long long value);
LogMessage& operator<<(double value);
private:
friend class LogFinisher;
void Finish();
ILogger* logger_;
int level_;
const char* filename_;
int line_;
::std::string message_;
};
/// Used to make the entire "LOG(BLAH) << etc." expression have a void return
/// type and print a newline after each message.
class LogFinisher {
public:
LogFinisher();
void operator=(LogMessage& other);
};
} // namespace internal
} // namespace
#define MY_LOG(LOGGER, LEVEL) \
mytest::internal::LogFinisher() = \
mytest::internal::LogMessage( \
LOGGER, LEVEL, __FILE__, __LINE__)
#endif _LOGGER_H_
//logger.cc
#include <logger.h>
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
#define snprintf _snprintf_s
#endif
namespace mytest {
namespace internal {
LogMessage& LogMessage::operator<<(const std::string& value) {
message_ += value;
return *this;
}
LogMessage& LogMessage::operator<<(const char* value) {
message_ += value;
return *this;
}
#undef DECLARE_STREAM_OPERATOR
#define DECLARE_STREAM_OPERATOR(TYPE, FORMAT) \
LogMessage& LogMessage::operator<<(TYPE value) { \
/* 128 bytes should be big enough for any of the primitive */ \
/* values which we print with this, but well use snprintf() */ \
/* anyway to be extra safe. */ \
char buffer[128]; \
snprintf(buffer, sizeof(buffer), FORMAT, value); \
/* Guard against broken MSVC snprintf(). */ \
buffer[sizeof(buffer)-1] = '\0'; \
message_ += buffer; \
return *this; \
}
DECLARE_STREAM_OPERATOR(char, "%c")
DECLARE_STREAM_OPERATOR(int, "%d")
DECLARE_STREAM_OPERATOR(unsigned int, "%u")
DECLARE_STREAM_OPERATOR(long, "%ld")
DECLARE_STREAM_OPERATOR(unsigned long, "%lu")
DECLARE_STREAM_OPERATOR(long long, "%lld")
DECLARE_STREAM_OPERATOR(unsigned long long, "%llu")
DECLARE_STREAM_OPERATOR(double, "%g")
#undef DECLARE_STREAM_OPERATOR
LogMessage::LogMessage(ILogger* logger, int level,
const char* filename, int line)
: logger_(logger), level_(level), filename_(filename), line_(line) {}
LogMessage::LogMessage(std::shared_ptr<ILogger> logger, int level,
const char* filename, int line)
: logger_(logger.get()), level_(level), filename_(filename), line_(line) {}
LogMessage::~LogMessage() {}
void LogMessage::Finish() {
if(logger_)
{
logger_->Log(level_, filename_, line_, message_.c_str());
}
}
LogFinisher::LogFinisher() {}
void LogFinisher::operator=(LogMessage& other) {
other.Finish();
}
}
}
//main.cc
#include <string>
#include <iostream>
#include <logger.h>
#include <boost/property_tree/ini_parser.hpp>
#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <log4cplus/loggingmacros.h>
using namespace mytest;
int ReadFile(const char* file_path, std::vector<char>* o_data) {
std::ifstream in (file_path, std::ios::binary);
if (!in.good ()) {
return -1;
}
in.seekg(0, std::ifstream::end);
size_t size = (size_t) in.tellg ();
in.seekg(0, std::ifstream::beg);
o_data->resize (size);
in.read(&(*o_data)[0], size);
if (in.gcount() != size) {
return -1;
}
return 0;
}
class Log4cplusLogger : public ILogger {
public:
Log4cplusLogger(const std::string& logger_name) {
logger_ = log4cplus::Logger::getInstance(logger_name);
}
virtual ~Log4cplusLogger() {
// nothing
}
virtual void Log(int level, const char* filename, int line,
const char* message) {
switch (level) {
case ILogger::LOG_FATAL:
logger_.log(log4cplus::FATAL_LOG_LEVEL, message, filename, line);
break;
case ILogger::LOG_ERROR:
logger_.log(log4cplus::ERROR_LOG_LEVEL, message, filename, line);
break;
case ILogger::LOG_WARNING:
logger_.log(log4cplus::WARN_LOG_LEVEL, message, filename, line);
break;
case ILogger::LOG_INFO:
logger_.log(log4cplus::INFO_LOG_LEVEL, message, filename, line);
break;
case ILogger::LOG_DEBUG:
logger_.log(log4cplus::DEBUG_LOG_LEVEL, message, filename, line);
break;
case ILogger::LOG_VERBOSE:
logger_.log(log4cplus::TRACE_LOG_LEVEL, message, filename, line);
break;
default:
break;
}
}
private:
log4cplus::Logger logger_;
};
int main(int argc, char* argv[])
{
if (argc < 2) {
std::cout << "Please specify path of the confuguration file." << std::endl;
return 0;
}
std::string config_path(argv[1]);
std::string log_config;
std::vector<char> data;
int res = ReadFile(config_path.c_str(), &data);
if (0 != res) {
std::cout << "Read ini file failed!" << std::endl;
return -1;
}
std::string config_str(&data[0], data.size());
std::stringstream css(config_str);
boost::property_tree::ptree pt;
boost::property_tree::read_ini(css, pt);
std::stringstream ss;
boost::property_tree::write_ini(ss, pt.get_child("log4cplus"));
log4cplus::initialize();
log4cplus::PropertyConfigurator pc(ss);
pc.configure();
std::shared_ptr<ILogger> logger;
logger.reset(new Log4cplusLogger("global"));
logger.reset();
for(int i=0; i<1000000; ++i)
{
MY_LOG(logger, ILogger::LOG_INFO) << "logging test " << i;
}
return 0;
}
Makefile:
main:
g++ -std=c++11 -Wall -O3 -L/home/wqf/usr/lib/boost_1_67_0/lib/ -lboost_system -lboost_filesystem -lboost_locale -lboost_date_time -llog4cplus main.cc logger.cc -o main
配置文件同上一篇文章https://blog.csdn.net/wqfhenanxc/article/details/88892996