c++ 日志类 线程安全+缓存

2020-10-10 16:42:04 浏览数 (1)

根据上一次的测试,有缓存的日志类性能会更好。用到了time.h类函数,所以在linux下就要改动一下了,windows环境下写的。

思路采用(参照muduo库的日志,不过认为他线程不安全,和没用缓存,就改造了下)

1.有一个总的缓存,logboss,为一个恶汉模式的单例类,指针对象为智能指针,析构函数讲缓存写入文件。

2.有一个logger类,作为临时缓存,析构函数,将里面的缓存写入总缓存(在总缓存写入的时候加锁)。如果缓存超过一定限度,就将前面的缓存写入文件先。

代码语言:javascript复制
    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()之类。

通过这样的宏定义:

代码语言:javascript复制
#define LOG_TRACE cyc::Logger(__FILE__, __LINE__, cyc::Logger::TRACE, __FUNCTION__).stream()
#define LOG_DEBUG cyc::Logger(__FILE__, __LINE__, cyc::Logger::DEBUG, __FUNCTION__).stream()
代码语言:javascript复制
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对象来使用。

代码语言:javascript复制
LOG_TRACE << "hello " << " "  __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;
    LOG_TRACE << "hello2 " << " "  __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;

opeartor<< 将传入的参数都添加到临时变量的缓存中,缓存是LogStream的成员变量。最后在析构函数中,通过LogBoss的静态公开的方法,获取LogBoss指针,然后将临时变量的缓存加入到总缓存。

代码语言:javascript复制
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

代码语言:javascript复制
#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 ''
    _snprintf_s(filename,len,_TRUNCATE,"%s\%dddd.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

代码语言:javascript复制
#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

代码语言:javascript复制
#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 ''
        _snprintf_s(filename,len,_TRUNCATE,"%s\%dddd.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

代码语言:javascript复制
#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 
                if (static_cast<size_t>(avail()) > len)
                {
                    memcpy(cur_, buf, len);
                    cur_  = len;
                    *cur_ = '';
                }
            }

            const char* data() const { return data_; }
            int length() const { return static_cast<int>(cur_ - data_); }

            //count the least size,and leave 'n' and ''
            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

代码语言:javascript复制
#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 = '';
            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, "0xX", 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);//增加偏移长度
}

0 人点赞