根据上一次的测试,有缓存的日志类性能会更好。用到了time.h类函数,所以在linux下就要改动一下了,windows环境下写的。
思路采用(参照muduo库的日志,不过认为他线程不安全,和没用缓存,就改造了下)
1.有一个总的缓存,logboss,为一个恶汉模式的单例类,指针对象为智能指针,析构函数讲缓存写入文件。
2.有一个logger类,作为临时缓存,析构函数,将里面的缓存写入总缓存(在总缓存写入的时候加锁)。如果缓存超过一定限度,就将前面的缓存写入文件先。
void LogBoss::append( const char* data, int len ) { std::unique_lock<std::mutex> l(mutex_); if( buffer_.avail() > len ){ buffer_.append(data,len); }else{ //write log file writeLogFile(); buffer_.reset(); buffer_.append(data,len); } }
3.logger类中,有一个logstream类对象,支持各种基本类型的<<输入,然后将输入转化为字符串放入到这个对象里面缓存数组里。
4.使用一个SourceFile类,只是用来方便的获取文件名。
5.使用一个FixedBuffer类,方便对字符串数组的操作,append(),fflush()之类。
通过这样的宏定义:
#define LOG_TRACE cyc::Logger(__FILE__, __LINE__, cyc::Logger::TRACE, __FUNCTION__).stream() #define LOG_DEBUG cyc::Logger(__FILE__, __LINE__, cyc::Logger::DEBUG, __FUNCTION__).stream()
class Logger { public: enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NUM_LOG_LEVELS, }; Logger(SourceFile file, int line, LogLevel level, const char* func); ~Logger(void); LogStream& stream(){ return stream_;} private: LogStream stream_; };
调用方式为:(通过构造一个匿名Logger类对象,通过调用stream()返回一个Logstream对象来使用。
LOG_TRACE << "hello " << " " __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ; LOG_TRACE << "hello2 " << " " __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;
opeartor<< 将传入的参数都添加到临时变量的缓存中,缓存是LogStream的成员变量。最后在析构函数中,通过LogBoss的静态公开的方法,获取LogBoss指针,然后将临时变量的缓存加入到总缓存。
Logger::~Logger(void) { // add "\n" stream_ << "\n"; //output to logboss LogBoss::getInstance()->append(stream_.buffer().data(),stream_.buffer().length()); }
上代码了:
总过5个文件Logger.h,Logger.cpp,LogStream.h,LogStream.cpp,main.cpp
main.cpp
#include <iostream> #include "Logger.h" #include <future> #include <thread> #include <mutex> #include <memory> #include <fstream> using namespace std; void fun1(){ for(int i = 0; i <100;i++){ cout <<"thread : "<< this_thread::get_id() << " i = " << i << endl; { LOG_TRACE << i; } } } void fun2(){ for(int i = 0; i <100;i++){ cout <<"thread : "<< this_thread::get_id() << " i = " << i << endl; { LOG_TRACE << i; } } } void fun3(){ for(int i = 0; i <100;i++){ cout <<"thread : "<< this_thread::get_id() << " i = " << i << endl; { LOG_TRACE << i; } } } void getfilename(char * filename,size_t len) { time_t t = time(NULL); tm st; localtime_s(&st,&t); //will fill ‘\0‘ _snprintf_s(filename,len,_TRUNCATE,"%s\\%d%02d%02d%02d.log",LOGFILE, st.tm_year+1900,st.tm_mon+1,st.tm_mday,st.tm_hour); } char filename[32]; int main(){ getfilename(filename,sizeof(filename)); {std::ofstream file(filename,std::ios::out);} LOG_TRACE << "hello " << " " __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ; LOG_TRACE << "hello2 " << " " __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ; LOG_TRACE << "hello3 "; LOG_TRACE << "hello4 "; auto f1 = async(launch::async,fun1); auto f2 = async(launch::async,fun2); auto f3 = async(launch::async,fun3); f1.get(); f2.get(); f3.get(); system("pause"); return 0; }
Logger.h
#ifndef CYC_BASE_LOGGING_H #define CYC_BASE_LOGGING_H #include "LogStream.h" #include <memory> #include <mutex> using namespace cyc; using std::mutex; using std::shared_ptr; #define LOGFILE "log" namespace cyc{ const size_t filename_len = 32; class LogBoss{ public:
//需要声明为友元才能使用他的私有构造函数 friend shared_ptr<LogBoss>;
//声明一个总缓存类型,kSmallBuffer = 4096 typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer; void append(const char* data, int len);
//获取智能指针 static shared_ptr<LogBoss> getInstance(); ~LogBoss(); private:
//调用append()的时候使用的mutex,保证线程安全 mutex mutex_; Buffer buffer_; char file_name_[filename_len];
//使用智能指针是为了,让他在程序关闭后,将目前的缓存写入文件 static shared_ptr<LogBoss> Logp; LogBoss(); void writeLogFile(); }; class Logger { public: // compile time calculation of basename of source file class SourceFile { public: template<int N> inline SourceFile(const char (&arr)[N]) : data_(arr), size_(N-1) { const char* slash = strrchr(data_, ‘\\‘); // builtin function if (slash) { data_ = slash + 1; size_ -= static_cast<int>(data_ - arr); } } explicit SourceFile(const char* filename) : data_(filename) { const char* slash = strrchr(filename, ‘\\‘); if (slash) { data_ = slash + 1; } size_ = static_cast<int>(strlen(data_)); } const char* data_; int size_; };
//目前只使用了两个等级,还没加入设置等级的函数 enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NUM_LOG_LEVELS, };
//通过这些参数,来完善日志的格式 Logger(SourceFile file, int line, LogLevel level, const char* func); ~Logger(void); LogStream& stream(){ return stream_;} private: LogStream stream_; }; } #define LOG_TRACE cyc::Logger(__FILE__, __LINE__, cyc::Logger::TRACE, __FUNCTION__).stream() #define LOG_DEBUG cyc::Logger(__FILE__, __LINE__, cyc::Logger::DEBUG, __FUNCTION__).stream() #endif // CYC_BASE_LOGGING_H
Logger.cpp
#include "Logger.h" #include <fstream> #include <time.h> #include <ctime> namespace cyc{ std::string printf_loacltime(){ // equal ctime(&t); time_t t = time(NULL); std::string ts = std::asctime(localtime(&t)); ts.resize(ts.size()-1); //skip trailing newline return ts; } void getfilename(char * filename,size_t len) { time_t t = time(NULL); tm st; localtime_s(&st,&t); //will fill ‘\0‘ _snprintf_s(filename,len,_TRUNCATE,"%s\\%d%02d%02d%02d.log",LOGFILE, st.tm_year+1900,st.tm_mon+1,st.tm_mday,st.tm_hour); } const char* LogLevelName[Logger::NUM_LOG_LEVELS] = { "TRACE ", "DEBUG ", "INFO ", "WARN ", "ERROR ", "FATAL ", }; //-------------------------------LogBoss Start--------------------------------------------- shared_ptr<LogBoss> LogBoss::Logp(new LogBoss); LogBoss::LogBoss() { } LogBoss::~LogBoss() { //need had log folder before { std::unique_lock<std::mutex> l(mutex_); writeLogFile(); } } void LogBoss::append( const char* data, int len ) { std::unique_lock<std::mutex> l(mutex_); if( buffer_.avail() > len ){ buffer_.append(data,len); }else{ //write log file writeLogFile(); buffer_.reset(); buffer_.append(data,len); } } shared_ptr<LogBoss> LogBoss::getInstance() { return Logp; } //need lock before use void LogBoss::writeLogFile() { // out put getfilename(file_name_,sizeof(file_name_)); std::ofstream file(file_name_,std::ios::out | std::ios::app); file << buffer_.data(); } //-------------------------------LogBoss End---------------------------------------------- //-------------------------------Logger Start--------------------------------------------- Logger::Logger( SourceFile file, int line, LogLevel level, const char* func ) {
//日志格式 stream_ << LogLevelName[level] <<" "<< printf_loacltime() <<" "<<file.data_<<" "<<line <<" "<<func<<" : "; } Logger::~Logger(void) { // add "\n" stream_ << "\n"; //output to logboss LogBoss::getInstance()->append(stream_.buffer().data(),stream_.buffer().length()); } } //-------------------------------Logger End--------------------------------------------
LogStream.h
#ifndef CYC_BASE_LOGSTREAM_H #define CYC_BASE_LOGSTREAM_H #include <string> using std::string; namespace cyc{ namespace detail{ const int kTempBuffer = 512; const int kSmallBuffer = 4096; const int kLargeBuffer = 4096*1000; template<int SIZE> class FixedBuffer { public: FixedBuffer() : cur_(data_){} ~FixedBuffer(){} void append(const char* buf, size_t len) { // FIXME: append partially,ensure have \0 if (static_cast<size_t>(avail()) > len) { memcpy(cur_, buf, len); cur_ += len; *cur_ = ‘\0‘; } } const char* data() const { return data_; } int length() const { return static_cast<int>(cur_ - data_); } //count the least size,and leave ‘\n‘ and ‘\0‘ int avail() const { return static_cast<int>(end() - cur_ - 2); } void reset() { cur_ = data_; } private: const char* end() const { return data_ + sizeof data_; } char data_[SIZE]; char* cur_; private: FixedBuffer(FixedBuffer& ); FixedBuffer& operator=(FixedBuffer&); }; } class LogStream { public:
//声明一个临时缓存的类型,kTempBuffer = 512. typedef detail::FixedBuffer<detail::kTempBuffer> Buffer; LogStream(void); ~LogStream(void);
//这里让他能接受各种基本参数 LogStream& operator<<(short); LogStream& operator<<(unsigned short); LogStream& operator<<(int); LogStream& operator<<(unsigned int); LogStream& operator<<(long); LogStream& operator<<(unsigned long); LogStream& operator<<(long long); LogStream& operator<<(unsigned long long); LogStream& operator<<(const void*); LogStream& operator<<(float v) { *this << static_cast<double>(v); return *this; } LogStream& operator<<(double);
//本质就是,转化为字符,字符串类型,然后调用FixBuffer中的append()方法 LogStream& operator<<(char v) { buffer_.append(&v, 1); return *this; } LogStream& operator<<(const char* v) { buffer_.append(v, strlen(v)); return *this; } LogStream& operator<<(const string& v) { buffer_.append(v.c_str(), v.size()); return *this; } void append(const char* data, int len) { buffer_.append(data, len); } const Buffer& buffer() const { return buffer_; } private: template<typename T> void formatInteger(T); Buffer buffer_; static const int kMaxNumericSize = 32; }; } #endif // CYC_BASE_LOGSTREAM_H
LogStream.cpp
#include "LogStream.h" #include <cstdio> using namespace cyc; using namespace cyc::detail; namespace cyc { namespace detail { const char digits[] = "0123456789"; const char digitsHex[] = "0123456789ABCDEF"; // Efficient Integer to String Conversions, by Matthew Wilson. template<typename T> size_t convert(char buf[], T value) { T i = value; char* p = buf; do { int lsd = static_cast<int>(i % 10); i /= 10; *p++ = digits[lsd]; } while (i != 0); if (value < 0) { *p++ = ‘-‘; } *p = ‘\0‘; std::reverse(buf, p); return p - buf; } } } LogStream::LogStream(void) { } LogStream::~LogStream(void) { } LogStream& LogStream::operator<<(short v) { *this << static_cast<int>(v); return *this; } LogStream& LogStream::operator<<(unsigned short v) { *this << static_cast<unsigned int>(v); return *this; } LogStream& LogStream::operator<<(int v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned int v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(long long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned long long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(const void* p) { char temp[kMaxNumericSize]; int len = _snprintf_s(temp, kMaxNumericSize,_TRUNCATE, "0x%08X", p); if(len) buffer_.append(temp,len); return *this; } // FIXME: replace this with Grisu3 by Florian Loitsch. LogStream& LogStream::operator<<(double v) { char temp[kMaxNumericSize]; int len = _snprintf_s(temp, kMaxNumericSize,_TRUNCATE, "%.12g", v); if(len) buffer_.append(temp,len); return *this; } template<typename T> void LogStream::formatInteger(T v) { char temp[kMaxNumericSize]; size_t len = convert(temp, v);//在上面的实现中可以到,已经一步步把数据转化为字符放到缓冲区中 buffer_.append(temp,len);//增加偏移长度 }
原文:http://www.cnblogs.com/cycxtz/p/4841406.html