根据上一次的测试,有缓存的日志类性能会更好。用到了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);//增加偏移长度
}
原文链接: https://www.cnblogs.com/cycxtz/p/4841406.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/222313
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!