日志类实现

代码搬运地址

log.h

#ifndef LOG_H
#define LOG_H

#include "../buffer/buffer.h"
#include "blockqueue.hpp"
#include <assert.h>
#include <mutex>
#include <stdarg.h> //...
#include <string>
#include <sys/stat.h>
#include <sys/time.h>
#include <thread>

class Log
{
public:
  void init(int level = 0,
            const char* path = "./log",
            const char* suffix = ".log",
            int maxQueueCapacity = 1024);
  static Log* Instance();
  static void FlushLogThread();

  void write(int level, const char* format, ...);
  void flush();

  int GetLevel();
  void SetLevel(int level);
  bool IsOpen() { return _isOpen; }

private:
  Log();
  void _AppendLogLevelTitle(int level);
  virtual ~Log();
  void _AsyncWrite();

  static const int LOG_PATH_LEN = 256;
  static const int LOG_NAME_LEN = 256;
  static const int MAX_LINES = 50000;

  const char* _path;
  const char* _suffix;

  int _MAX_LINES;
  int _lineCount;
  int _toDay;

  bool _isOpen;
  Buffer _buff;
  int _level;
  bool _isAsync;

  FILE* _fp;
  std::unique_ptr<BlockDeque<std::string>> _deque;
  std::unique_ptr<std::thread> _writeThread;
  std::mutex _mtx;
};

#define LOG_BASE(level, format, ...)                                           \
  do {                                                                         \
    Log* log = Log::Instance();                                                \
    if (log->IsOpen() && log->GetLevel() <= level) {                           \
      log->write(level, format, ##__VA_ARGS__);                                \
      log->flush();                                                            \
    }                                                                          \
  } while (0);

#define LOG_DEBUG(format, ...)                                                 \
  do {                                                                         \
    LOG_BASE(0, format, ##__VA_ARGS__)                                         \
  } while (0);
#define LOG_INFO(format, ...)                                                  \
  do {                                                                         \
    LOG_BASE(1, format, ##__VA_ARGS__)                                         \
  } while (0);
#define LOG_WARN(format, ...)                                                  \
  do {                                                                         \
    LOG_BASE(2, format, ##__VA_ARGS__)                                         \
  } while (0);
#define LOG_ERROR(format, ...)                                                 \
  do {                                                                         \
    LOG_BASE(3, format, ##__VA_ARGS__)                                         \
  } while (0);
#endif //! LOG_H

log.cpp



#include "log.h"
#include <iostream>
using namespace std;

Log::Log()
{
  _lineCount = 0;
  _isAsync = false;
  _writeThread = nullptr;
  _deque = nullptr;
  _toDay = 0;
  _fp = nullptr;
}

Log::~Log()
{
  if (_writeThread && _writeThread->joinable()) {
    while (!_deque->empty()) {
      _deque->flush(); //将队列中的数据 pop()
    }
    _deque
      ->Close(); //里面会唤醒阻塞队列的生产者和消费者,把剩余的数据冲刷到_deque中
    _writeThread->join(); //等待线程把最后的数据写完结束
  }
  if (_fp) {
    std::lock_guard<std::mutex> locker(_mtx);
    flush();
    fclose(_fp);
  }
}

void
Log::init(int level, const char* path, const char* suffix, int maxQueneSize)
{
  _isOpen = true;
  _level = level;
  if (maxQueneSize > 0) {
    _isAsync = true;
    if (!_deque) {
      //阻塞队列
      std::unique_ptr<BlockDeque<std::string>> newDeque(
        new BlockDeque<std::string>);
      _deque = std::move(newDeque);
      std::unique_ptr<std::thread> NewThread(new std::thread(FlushLogThread));
      _writeThread = std::move(NewThread);
    }
  } else {
    _isAsync = false;
  }
  _lineCount = 0;
  time_t timer = time(nullptr);
  struct tm* systime = localtime(&timer);
  struct tm t = *systime;
  _path = path;
  _suffix = suffix;

  char fileName[LOG_NAME_LEN] = { 0 };
  //将可变参数 “…” 按照format的格式格式化为字符串,然后再将其拷贝至fileName中。
  snprintf(fileName,
           LOG_NAME_LEN - 1,
           "%s/%04d_%02d_%02d%s",
           _path,
           t.tm_year + 1900,
           t.tm_mon + 1,
           t.tm_mday,
           _suffix);
  _toDay = t.tm_mday;

  {

    std::lock_guard<std::mutex> locker(_mtx);
    _buff.RetrieveAll(); // clear
    if (_fp) {
      //将上次的数据冲刷出缓冲区
      flush();
      fclose(_fp);
    }

    //打开新的日志文件

    _fp = fopen(fileName, "a");
    if (_fp == nullptr) {

      mkdir(_path, 0777);
      _fp = fopen(fileName, "a");
    }
    assert(_fp != nullptr);
  }
}

void
Log::write(int level, const char* format, ...)
{
  struct timeval now = { 0, 0 };
  gettimeofday(&now, nullptr);
  time_t tSec = now.tv_sec;
  struct tm* sysTime = localtime(&tSec);
  struct tm t = *sysTime;
  va_list vaList;
  /*日志日期 日志行数*/
  if (_toDay != t.tm_mday || (_lineCount && (_lineCount % MAX_LINES == 0))) {
    std::unique_lock<std::mutex> locker(_mtx);
    locker.unlock();

    char newFile[LOG_NAME_LEN];
    char tail[36] = { 0 };
    snprintf(
      tail, 36, "%04d_%02d_%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
    if (_toDay != t.tm_mday) {
      snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s%s", _path, tail, _suffix);
      _toDay = t.tm_mday;
      _lineCount = 0;
    } else {
      snprintf(newFile,
               LOG_NAME_LEN - 72,
               "%s/%s-%d%s",
               _path,
               tail,
               (_lineCount / MAX_LINES),
               _suffix);
    }

    locker.lock();
    //冲刷上次的日志文件
    flush();
    fclose(_fp);
    _fp = fopen(newFile, "a");
    assert(_fp != nullptr);
  }

  {
    std::unique_lock<std::mutex> locker(_mtx);
    _lineCount++;
    int n = snprintf(_buff.BeginWrite(),
                     128,
                     "%d-%02d-%02d %02d:%02d:%02d.%06ld ",
                     t.tm_year + 1900,
                     t.tm_mon + 1,
                     t.tm_mday,
                     t.tm_hour,
                     t.tm_min,
                     t.tm_sec,
                     now.tv_usec);
    _buff.HasWritten(n);
    _AppendLogLevelTitle(level);

    va_start(vaList, format);
    int m =
      vsnprintf(_buff.BeginWrite(), _buff.WritableBytes(), format, vaList);
    va_end(vaList);
    _buff.HasWritten(m);
    _buff.Append("\n\0", 2);
    if (_isAsync && _deque && !_deque->full()) {
      //不是异步,队列还没满
      _deque->push_back(_buff.RetrieveAllToStr());
    } else {
      //直接写入数据
      fputs(_buff.Peek(), _fp);
    }
    _buff.RetrieveAll();
  }
}

int
Log::GetLevel()
{
  std::lock_guard<std::mutex> locker(_mtx);
  return _level;
}

void
Log::SetLevel(int level)
{
  std::lock_guard<std::mutex> locker(_mtx);
  _level = level;
}

void
Log::flush()
{
  if (_isAsync) {
    _deque->flush();
  }
  fflush(_fp);
}

void
Log::_AppendLogLevelTitle(int level)
{
  switch (level) {
    case 0:
      _buff.Append("[debug]: ", 9);
      break;
    case 1:
      _buff.Append("[info]:  ", 9);
      break;
    case 2:
      _buff.Append("[warn]:  ", 9);
      break;
    case 3:
      _buff.Append("[error]: ", 9);
      break;
    default:
      _buff.Append("[info]:  ", 9);
      break;
  }
}

void
Log::_AsyncWrite()
{
  std::string str = "";
  while (_deque->pop(str)) {
    std::lock_guard<std::mutex> locker(_mtx);
    fputs(str.c_str(), _fp);
  }
}

//日志对象单例
Log*
Log::Instance()
{
  static Log inst;
  return &inst;
}

void
Log::FlushLogThread()
{
  Log::Instance()->_AsyncWrite();
}
posted @ 2021-12-26 16:45  blackTree  阅读(53)  评论(0编辑  收藏  举报