一个简单的C++日志系统实现

一个跨平台的日志系统

用法如下

test_log.cpp
#include <iostream>
#include "log.h"

simpleLog::Log *g_p_default_Log = NULL;
int main()
{
    static simpleLog::Log mylog("./log/my.log");
    g_p_default_Log = &mylog;
    mylog.start();

    debug_log("this is a(n) debug log");
    info_log("this is a(n) info log");
    warn_log("this is a(n) warn log");
    err_log("this is a(n) error log");
    fatal_log("this is a(n) fatal log");

    //bt_log("this is a(n) backtrace log");

    return 0;
}

 

文件结构如下

log
src\
--ThreadGroup.h --log.h --simpleLog.cpp --simpleLog.h --test_log.cpp
以下是实现部分
线程部分的封装 ThreadGroup.h
#ifndef __THREAD_GROUP_H
#define __THREAD_GROUP_H

#ifdef _MSC_VER
#pragma once
#include <process.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#endif

#include <algorithm>
#include <functional>
#include <cstddef>
#include <ctime>
#include <vector>
#include <iterator>
//#include "mynoncopyable.h"

#ifndef func_type
#ifdef _MSC_VER
#define func_type  unsigned int __stdcall
#else
#define func_type  void*
#endif
#endif

/*
* by Summer
* 2017-03-12
* header-file only
*/
class ThreadMutex
{
private:  // noncopyable
    ThreadMutex( const ThreadMutex& );
    ThreadMutex& operator=( const ThreadMutex& );

public:
    ThreadMutex()
    {
#ifdef _MSC_VER
        m_threadParameter = CreateMutex(NULL, FALSE, NULL);
        InitializeCriticalSection(&m_criticalSection);
#else
        pthread_mutex_init(&m_mutex,NULL);
#endif
    }
    ~ThreadMutex()
    {
#ifdef _MSC_VER
        CloseHandle(m_threadParameter);
        DeleteCriticalSection(&m_criticalSection);
#else
        pthread_mutex_destroy( &m_mutex );
#endif
    }
    void lock()
    {
#ifdef _MSC_VER
        WaitForSingleObject(m_threadParameter, INFINITE);
#else
        pthread_mutex_lock(&m_mutex);
#endif
    }
    void unlock()
    {
#ifdef _MSC_VER
        ReleaseMutex(m_threadParameter);
#else
        pthread_mutex_unlock(&m_mutex);
#endif
    }
    void enterCriticalSection()
    {
#ifdef _MSC_VER
        EnterCriticalSection(&m_criticalSection);
#else
        lock();
#endif
    }
    void leaveCriticalSection()
    {
#ifdef _MSC_VER
        LeaveCriticalSection(&m_criticalSection);
#else
        unlock();
#endif
    }

private:
#ifdef _MSC_VER
    HANDLE  m_threadParameter;
    CRITICAL_SECTION m_criticalSection;
#else
    pthread_mutex_t m_mutex;
#endif
};

template <typename MutexType>
class ScopedLock
{
private:  // noncopyable
    ScopedLock( const ScopedLock& );
    ScopedLock& operator=( const ScopedLock& );

public:
    ScopedLock(MutexType & threadlock)
        : m_mutex(&threadlock), isLocked(false)
    {
        lock();
    }

    ~ScopedLock()
    {
        if (ownsLock())
        {
            m_mutex->unlock();
        }
    }

    void lock()
    {
        if (m_mutex == 0)
        {
            return;
        }
        if (ownsLock())
        {
            return;
        }
        m_mutex->lock();
        isLocked = true;
    }

    void unlock()
    {
        if (m_mutex == 0)
        {
            return;
        }
        if (!ownsLock())
        {
            return;
        }
        m_mutex->unlock();
        isLocked = false;
    }
protected:
    bool ownsLock() const
    {
        return isLocked;
    }
private:
    MutexType *m_mutex;
    bool isLocked;
};

inline 
#ifdef _MSC_VER
void SleepSeconds(DWORD counts)
#else
void SleepSeconds( unsigned int counts)
#endif
{
#ifdef _MSC_VER
    Sleep(counts*1000);
#else
    sleep(counts);
#endif
}

inline void SleepMicroseconds(time_t counts)
{
#ifdef _MSC_VER
    HANDLE timer;
    LARGE_INTEGER ft;

    ft.QuadPart = -(10*counts); // Convert to 100 nanosecond interval, negative value indicates relative time

    timer = CreateWaitableTimer(NULL, TRUE, NULL);
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
    WaitForSingleObject(timer, INFINITE);
    CloseHandle(timer);
#else
    usleep(counts);
#endif
}

class ThreadSleep
{
public:
    static void seconds(unsigned int counts)
    {
        SleepSeconds(counts);
    }
    static void microseconds(time_t counts)
    {
        SleepMicroseconds(counts);
    }

private:
    ThreadSleep( const ThreadSleep& );
    ThreadSleep& operator=( const ThreadSleep& );
};


class ThreadGroup
{
private:  // noncopyable
    ThreadGroup( const ThreadGroup& );
    ThreadGroup& operator=( const ThreadGroup& );


public:
#ifdef _MSC_VER
    typedef HANDLE thread_type;
#else
    typedef pthread_t thread_type;
#endif
    typedef size_t size_type;

    ThreadGroup()
        : joinedSize(0)
    {}

    template<typename F, typename A>
    bool createThread(F threadfunc, const A &arg)
    //bool createThread(func_type threadfunc(void *), void * arg)
    {
        ScopedLock<ThreadMutex> lock(m_mutex); // auto lock and unlock
        thread_type handle;
#ifdef _MSC_VER
        if((handle = (HANDLE)_beginthreadex(NULL, 0, threadfunc, (void*)&arg, 0, NULL)) == NULL)
        {
            return false;
        }

#else
        if(pthread_create(&handle, NULL, threadfunc, (void*)&arg) != 0)
        {
            return false;
        }
#endif
        m_threadHandle.push_back(handle);

        return true;
    }

    void join()
    {
        ScopedLock<ThreadMutex> lock(m_mutex);
        if(joinedSize == m_threadHandle.size())
            return;

#ifdef _MSC_VER
        WaitForMultipleObjects(m_threadHandle.size(), &m_threadHandle.front()+joinedSize, TRUE, INFINITE);
#else
        container_type::iterator start = m_threadHandle.begin();
        std::advance (start, joinedSize);
        std::for_each(start, m_threadHandle.end(), 
            std::bind2nd(std::ptr_fun(pthread_join), (void*)0));
#endif
        joinedSize = m_threadHandle.size();
    }

    size_type size() const
    {
        ScopedLock<ThreadMutex> lock(m_mutex);
        return m_threadHandle.size();
    }

    ~ThreadGroup()
    {
#ifdef _MSC_VER
        std::for_each(m_threadHandle.begin(), m_threadHandle.end(), CloseHandle);
#else
#endif
    }

private:
    typedef std::vector<thread_type> container_type;
    container_type m_threadHandle;

    mutable ThreadMutex m_mutex;
    std::vector<thread_type>::size_type joinedSize;
};



#endif //__THREAD_GROUP_H

log.h

#ifndef LOG_H
#define LOG_H

#include "simpleLog.h"

extern simpleLog::Log *g_p_default_Log;

#define debug_log(format,...) g_p_default_Log->print_log(simpleLog::LOG_LELEVE_DEBUG,"%s:%d|%s <DEBUG> "format,__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__)
#define info_log(format,...) g_p_default_Log->print_log(simpleLog::LOG_LELEVE_INFO,"<INFO> "format,##__VA_ARGS__)
#define warn_log(format,...) g_p_default_Log->print_log(simpleLog::LOG_LELEVE_WARNING,"%s:%d|%s <WARN> "format,__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__)
#define err_log(format,...) g_p_default_Log->print_log(simpleLog::LOG_LELEVE_ERROR,"%s:%d|%s <ERR> "format,__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__)
#define fatal_log(format,...) g_p_default_Log->print_log(simpleLog::LOG_LELEVE_FATAL,"%s:%d|%s <FATAL> "format,__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__)

#define bt_log(format,...) g_p_default_Log->backtrace_log(simpleLog::LOG_LELEVE_INFO,"%s:%d|%s <BT> "format,__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__)

#endif

simpleLog.h

#ifndef __SIMPLE_LOG__H
#define __SIMPLE_LOG__H

#include <string>
#include <vector>
#include <stdio.h>
#include "ThreadGroup.h"

namespace simpleLog
{
    enum 
    {
        LOG_LELEVE_FATAL = 0,
        LOG_LELEVE_ERROR = 1,
        LOG_LELEVE_WARNING = 2,
        LOG_LELEVE_INFO = 3,
        LOG_LELEVE_DEBUG = 4,
        TOTLE_LOG_LELEVE_SIZE = 5
    };

    enum ConstructorMode
    {
        SET_NAME = 0,
        READ_CONFIG = 1  // Not implemented
    };


    class simpleLogTool
    {
    public:
        std::string getNewFileName(const std::string & oldname);
        bool TimeIs(int hour);
        bool isExist(const std::string &file);
        bool isFileEmpty(const std::string &file);
        void newfile(const std::string &name);
    };

    class Log
    {
    public:
        Log(const std::string & name, ConstructorMode mode = SET_NAME);
        ~Log();
        void start();

    public:
        void setRoll(int roll);//是否按日期滚动文件名,1滚动,0不滚动
        int getRoll() const { return m_roll;  }
        void print_log(int log_level, const char *fmt, ...);

        std::string getLogFileName() const;
    private:
        Log(const Log &);

        void logImpl(FILE * stream, const char *fmt, va_list argList);
        int checkLogLevel(int log_level);
        static const char *log_info[TOTLE_LOG_LELEVE_SIZE + 1];
        struct LogArgs
        {
            std::string name;
            ConstructorMode mode;
        };

        std::string m_logfilename;
        ThreadGroup m_LogThread;

        simpleLogTool m_tool;
        LogArgs m_LogArgs;
        int m_roll;
    };

};

#endif

simpleLog.cpp

#ifdef _MSC_VER
#pragma warning(disable:4996)
#include <windows.h>
#else
#include <execinfo.h>
#endif

#include "simpleLog.h"

#include <string>
#include <vector>
#include <fstream>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <climits>
#include <assert.h>


using namespace simpleLog;

#ifdef _MSC_VER
#ifndef snprintf
#define snprintf(_DstBuf, _SizeInBytes, format,...) _snprintf_s(_DstBuf, _SizeInBytes, _TRUNCATE, format,##__VA_ARGS__)
#endif
#endif

namespace simpleLog
{
    namespace detail
    {
        ThreadMutex s_writeLogLock;
        ThreadMutex &g_writeLogLock()
        {
            return s_writeLogLock;
        }

        std::string GetSystemDayTime2()
        {
            char timebuf[64] = {0};

            time_t timep;
            struct tm *t;
            time(&timep);
            t = localtime(&timep);
            snprintf(timebuf, sizeof(timebuf), "%4d-%02d-%02d %d:%02d:%02d",
                t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
            return std::string(timebuf);
        }

        std::string GetSystemDay()
        {
            char Time[30] = {0};
            time_t timep;
            struct tm *p;
            time(&timep);
            p = localtime(&timep);
            snprintf(Time, sizeof(Time), "%4d-%02d-%02d", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday);
            std::string SystemTime(Time);
            return SystemTime;
        }
        func_type dobackup(void * p)
        {
            simpleLogTool tool;
            Log *pLog = (Log*)p;
            std::string oldfile(pLog->getLogFileName());
            int roll = pLog->getRoll();
            while(true)
            {
                if(roll && tool.TimeIs(23)) //23::00::00~23::59::59
                {
                    try
                    {
                        if(tool.isExist(oldfile) && !tool.isFileEmpty(oldfile))
                        {
                            ScopedLock<ThreadMutex> lock(g_writeLogLock());
                            rename(oldfile.c_str(), tool.getNewFileName(oldfile).c_str());
                            tool.newfile(oldfile);
                        }
                    }
                    catch(std::exception &ex)
                    {
                        printf("[simpleLog::Log] [dobackup] %s\n", ex.what());
                    }
                    SleepSeconds(3600*2);
                }

                SleepSeconds(60*5);
            }
        }
    }; // namespace detail

    using namespace detail;
    std::string simpleLogTool::getNewFileName(const std::string & oldname)
    {
        std::string tryname (oldname);

        std::string dateSuffix = "." + GetSystemDay();
        tryname += dateSuffix;
        std::string newname(tryname);
        char numSuffix[64 + 2] = {0};
        for(int i = 1; isExist(newname) && i < INT_MAX; i++)
        {
            newname = tryname;
            memset(numSuffix, 0, sizeof(numSuffix));
            snprintf(numSuffix, sizeof(numSuffix), ".%d", i);
            newname += numSuffix;
        }
        return newname;
    }
    bool simpleLogTool::TimeIs(int hour)
    {
        time_t timep;
        struct tm *t;
        time(&timep);
        t = localtime(&timep);
        return (/*t->tm_min == 0 &&*/ t->tm_hour == hour);
    }
    bool simpleLogTool::isExist(const std::string &file)
    {
        std::ifstream ifs(file.c_str());
        return ifs.good();
    }
    bool simpleLogTool::isFileEmpty(const std::string &file)
    {
        std::ifstream ifs(file.c_str());
        return ifs.peek() == std::ifstream::traits_type::eof();
    }

    void simpleLogTool::newfile(const std::string &name)
    {
        std::ofstream ofs(name.c_str());
        if (!ofs)
        {
            printf("[simpleLog::Log] Cannot open the output file: %s", name.c_str());
        }
        ofs.close();
    }



};

Log::Log(const std::string & name, ConstructorMode mode)
{
    m_LogArgs.mode = mode;
    m_LogArgs.name = name;

    m_roll = 1;
}

Log::~Log()
{
    if(getRoll())
    {
        rename(m_logfilename.c_str(), m_tool.getNewFileName(m_logfilename).c_str());
        m_tool.newfile(m_logfilename);
    }
}

void Log::start()
{
    if(m_LogArgs.name.empty())
    {
        printf("[simpleLog::Log] log filename is empty\n");
        assert(false);
    }
    switch(m_LogArgs.mode)
    {
    case SET_NAME:
        m_logfilename = m_LogArgs.name;
        break;
    case READ_CONFIG:
        // TODO
        break;
    default:
        assert(false);
        break;
    }

    m_LogThread.createThread(dobackup, *this);
}

const char * Log::log_info[TOTLE_LOG_LELEVE_SIZE + 1] = 
{"FATAL", "ERROR", "WARNING", "INFO", "DEBUG", ""};

void Log::setRoll(int roll)
{
    m_roll = roll;
}

std::string Log::getLogFileName() const
{
    return m_logfilename;
}

int Log::checkLogLevel(int log_level)
{
    // TODO;
    return 0;
}

void Log::logImpl(FILE * stream, const char *fmt, va_list argList)
{
    int ret=fprintf(stream,"%s ", GetSystemDayTime2().c_str());
    if(ret<0)
    {
        return;
    }

    ret=vfprintf (stream, fmt, argList);
    if(ret<0)
    {
        return ;
    }

    ret=fprintf(stream,"\n");
    if(ret<0)
    {
        return;
    }
}

void Log::print_log(int log_level, const char *fmt, ...)
{
    if(checkLogLevel(log_level) != 0)
    {
        return;
    }

    ScopedLock<ThreadMutex> lock(g_writeLogLock());
    FILE *fp = fopen(m_logfilename.c_str(), "a");
    if(fp == NULL)
        return;

    va_list ap;
    va_start (ap, fmt);
    logImpl(fp, fmt, ap);
    va_end (ap);

    fclose(fp);
}

编译命令

g++ -g -c *.cpp
g++ -g -o simpleLog *.o -O2 -lpthread

 

原文链接: https://www.cnblogs.com/summer010/p/10529353.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    一个简单的C++日志系统实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/291469

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 下午1:41
下一篇 2023年2月15日 下午1:41

相关推荐