一个简单小巧的日志类

2 篇文章 0 订阅

其实网上可以找到很多日志库,不过大多太大、考虑得太细致,所以索性自己写个最适合自己用的。基本的需求是当程序在7*24运行时,每天能自动生成当日日志文件;能写入日志信息和操作代码;能按层级显示操作的从属等级。

类定义logger.h如下:

// logger.h
#ifndef __LOGGER_H
#define __LOGGER_H

#include <string>

/**
 * Logger 通用日志类。
 *       日志文本格式为:yyyy-MM-dd hh:mm:ss:sss - log-message-text, message-code, extension-code.
 *       根据不同的level设定,log-message-text前面会有3*N个"---"作为层级标识。
 *
 *       日志文件名格式为:prefix-yyyy-MM-dd.suffix。其中prefix是程序指定的文件名前缀,默认为"run";suffix是程序指定的文件扩展名。
 *       默认为"log"。
 *       全局Logger实例runtimeLogger的默认文件名为"run-yyyy-MM-dd.log"。
 */
class Logger
{
   public:
      Logger();
      Logger(const char prefix[], const char suffix[]);
      Logger(const std::string& prefix, const  std::string& suffix);
      virtual ~Logger();

   public:
      /**
       * 写日志信息
       * @param msgText 字符数组,存放了日志消息正文
       * @param msgCode 整数,存放了日志消息代码,默认值为0
       * @param extCode 整数,存放了日志扩展代码,默认值为0
       * @param level   整数,指定日志层级,0是最高层级,默认值为0
       */
      virtual void write(const char msgText[], int msgCode = 0, int extCode = 0, int level = 0);

      /**
       * 写日志消息
       * @param msgText 字符串对象引用,存放了日志消息正文
       * @param msgCode 整数,存放了日志消息代码,默认值为0
       * @param extCode 整数,存放了日志扩展代码,默认值为0
       * @param level   整数,指定日志层级,0是最高层级,默认值为0
       */
      virtual void write(const std::string& msgText, int msgCode = 0, int extCode = 0, int level = 0);

      /**
       * 向日志文件中写入分隔行。分隔行由一系列相同的字符组成。
       * @param sepChar 字符类型常量,指定分隔行字符
       * @param length  整型常量,指定分隔行由多少个相同的字符组成
       */
      virtual void writeSeperatorLine(const char sepChar, const int length);

      /**
       * 根据前缀和后缀生成日志文件名
       * @param prefix  字符串常量对象引用,指定日志文件名前缀
       * @param suffix  字符串常量对象引用,指定日志文件名后缀
       * @return        形如prefix-yyyy-MM-dd.suffix的日志文件名字符串
       */
      static std::string generateFilename(const std::string& prefix, const std::string& suffix);

      /**
       * 根据指定的日期、前缀和后缀生成日志文件名
       * @param t       time_t类型,指定日志文件名的日期部分
       * @param prefix  字符串常量对象引用,指定日志文件名前缀
       * @param suffix  字符串常量对象引用,指定日志文件名后缀
       * @return        形如prefix-yyyy-MM-dd.suffix的日志文件名字符串
       */
      static std::string generateFilename(const time_t t, const std::string& prefix, const std::string& suffix);

      /**
       * 静态方法,向指定的日志文件中写入日志信息。
       * @param filename 字符串常量对象引用,指定日志文件名
       * @param msgText  字符串对象引用,存放了日志消息正文
       * @param msgCode  整数,存放了日志消息代码,默认值为0
       * @param extCode  整数,存放了日志扩展代码,默认值为0
       * @param level    整数,指定日志层级,0是最高层级,默认值为0
       */
      static void write(const std::string& filename, const std::string& msgText, int msgCode = 0, int extCode = 0, int level = 0);

      /**
       * 静态方法,向指定的日志文件中写入日志信息。
       * @param filename 字符串常量对象引用,指定日志文件名
       * @param msgText 字符数组,存放了日志消息正文
       * @param msgCode 整数,存放了日志消息代码,默认值为0
       * @param extCode 整数,存放了日志扩展代码,默认值为0
       * @param level   整数,指定日志层级,0是最高层级,默认值为0
       */
      static void write(const std::string& filename, const char msgText[], int msgCode = 0, int extCode = 0, int level = 0);

      /**
       * 静态方法,向指定的日志文件中写入分隔行
       * @param filename 字符串常量对象引用,指定日志文件名
       * @param sepChar  字符类型常量,指定分隔行字符
       * @param length   整型常量,指定分隔行由多少个相同的字符组成
       */
      static void writeSeperatorLine(const std::string& filename, const char sepChar, const int length);

      /**
       * 模板函数,合成可供写入日志的文本
       * @tparam T      指定待写入数据类型。该类型必须实现 << 操作符
       * @param oper    操作说明字符串
       * @param result  操作结果数据
       * @return        合成的文本,形如:OPERATOR: xxxxxx. RESULT: yyyyyy
       */
      template<typename T> static std::string getLogText(const std::string& oper, const T& result)
      {
         std::stringstream msgTxt;
         msgTxt << "OPERATOR: " << oper << ", RESULT: " << result;
         return msgTxt.str();
      }

      /**
       * 获取日志文件名前缀
       * @return 日志文件名前缀字符串
       */
      const std::string& getPrefix() const;
      /**
       * 设置日志文件名前缀
       * @param prefix 指定日志文件名的新前缀
       */
      void setPrefix(const std::string &prefix);

      /**
       * 获取日志文件名扩展名
       * @return 日志文件名扩展名字符串
       */
      const std::string& getSuffix() const;
      /**
       * 设置日志文件名扩展名
       * @param suffix 指定日志文件名的新的扩展名
       */
      void setSuffix(const std::string &suffix);

   protected:
      /**
       * 将指定的时间转换成日期字符串
       * @param t  time_t类型常量,指定待转换的时间值
       * @param sc 字符类型常量,指定年月日之间的分隔字符,默认为'-'
       * @return   返回形如yyyy-MM-dd的日期字符串
       */
      static std::string getDateString(const time_t t, const char sc = '-');

      /**
       * 将指定的时间转换为时刻字符串
       * @param t  time_t类型常量,指定待转换的时间值
       * @param sc 字符类型常量,指定时分秒之间的分隔字符,默认为':'
       * @return   返回形如hh:mm:ss的时间字符串
       */
      static std::string getTimeString(const time_t t, const char sc = ':');

      /**
       * 将指定的时间值转换为日期和时间字符串
       * @param t    time_t类型常量,指定待转换的时间值
       * @param dsc  字符常量,指定年月日之间的分隔符,默认为'-'
       * @param tsc  字符常量,指定时分秒之间的分隔符,默认为':'
       * @return     返回形如yyyy-MM-dd hh:mm:ss的日期及时间字符串
       */
      static std::string getDatetimeString(const time_t t, const char dsc = '-', const char tsc = ':');

      /**
       * 将指定的时间值转换为日期和时间(含毫秒)字符串
       * @param t    time_t类型常量,指定待转换的时间值
       * @param dsc  字符常量,指定年月日之间的分隔符,默认为'-'
       * @param tsc  字符常量,指定时分秒之间的分隔符,默认为':'
       * @param msc  字符常量,指定秒与毫秒之间的分隔符,默认为'.'
       * @return     返回形如yyyy-MM-dd hh:mm:ss.sss的日期及时间(含毫秒)字符串
       */
      static std::string getDatetimeString(const timeb& t, const char dsc = '-', const char tsc = ':', const char msc = '.');

   private:
      std::string prefix;
      std::string suffix;
};

extern Logger runtimeLogger;

#endif // __LOGGER_H

类实现logger.cpp代码如下:

// logger.cpp
#include <sys\timeb.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include "logger.h"

using namespace std;

Logger runtimeLogger("run", "log");

Logger::Logger()
{
   this->prefix = "run";
   this->suffix = "log";
}

Logger::Logger(const char prefix[], const char suffix[])
{
   this->prefix.append(prefix);
   this->suffix.append(suffix);
}

Logger::Logger(const std::string &prefix, const std::string &suffix)
{
   this->prefix.assign(prefix);
   this->suffix.assign(suffix);
}

Logger::~Logger()
{
}

void Logger::write(const char msgText[], int msgCode, int extCode, int level)
{
   struct timeb tb;
   ftime(&tb);
   string fullLogFilename = generateFilename(tb.time, prefix, suffix);
   ofstream ofs(fullLogFilename, ios::app);
   ofs << getDatetimeString(tb, '-', ':', '.') << " - ";
   for(int count = 0; count < level; count ++)
   {
      ofs << "---";
   }
   ofs << msgText << ", CODE: " << msgCode << ", " << extCode << "." << endl;
   ofs.close();
}

void Logger::write(const string& msgText, int msgCode, int extCode, int level)
{
   write(msgText.c_str(), msgCode, extCode, level);
}

std::string Logger::generateFilename(const std::string &prefix, const std::string &suffix)
{
   string fullLogFilename = generateFilename(time(nullptr), prefix, suffix);
   return fullLogFilename;
}

std::string Logger::generateFilename(const time_t t, const std::string &prefix, const std::string &suffix)
{
   string filename = prefix + "-" + getDateString(t, '-') + "." + suffix;
   return filename;
}

void Logger::write(const std::string &filename, const std::string &msgText, int msgCode, int exCode, int level)
{
   write(filename, msgText.c_str(), msgCode, exCode, level);
}

void Logger::write(const std::string &filename, const char msgText[], int msgCode, int extCode, int level)
{
   ofstream ofs(filename, ios::app);
   timeb tb;
   ftime(&tb);
   ofs << getDatetimeString(tb, '-', ':', '.') << " - ";
   for(int count = 0; count < level; count ++)
   {
      ofs << "---";
   }
   ofs << msgText << ", CODE: " << msgCode << ", " << extCode << "." << endl;
   ofs.close();
}

void Logger::writeSeperatorLine(const char sepChar, const int length)
{
   string filename = generateFilename(prefix, suffix);
   writeSeperatorLine(filename, sepChar, length);
}

void Logger::writeSeperatorLine(const std::string &filename, const char sepChar, const int length)
{
   FILE *ofp = fopen(filename.c_str(), "a");
   for(int count = 0; count < length; count ++)
   {
      fprintf(ofp, "%c", sepChar);
   }
   fprintf(ofp, "\n");
   fclose(ofp);
}

std::string Logger::getDateString(const time_t t, const char sc)
{
   struct tm *tp = localtime(&t);
   ostringstream oss;
   oss << setw(4) << setfill('0') << (tp->tm_year + 1900) << sc << setw(2) << setfill('0') << (tp->tm_mon + 1) << sc << setw(2) << setfill('0') << tp->tm_mday;
   return oss.str();
}

std::string Logger::getTimeString(const time_t t, const char sc)
{
   struct tm *tp = localtime(&t);
   ostringstream oss;
   oss << setw(2) << setfill('0') << tp->tm_hour << sc << setw(2) << setfill('0') << tp->tm_min << sc << setw(2) << setfill('0') << tp->tm_sec;
   return oss.str();
}

std::string Logger::getDatetimeString(const timeb& t, const char dsc, const char tsc, const char msc)
{
   ostringstream oss;
   oss << getDatetimeString(t.time, dsc, tsc) << msc << setw(3) << setfill('0') << t.millitm;
   return oss.str();
}

std::string Logger::getDatetimeString(const time_t t, const char dsc, const char tsc)
{
   ostringstream oss;
   oss << getDateString(t, dsc) << " " << getTimeString(t, tsc);
   return oss.str();
}

const std::string& Logger::getPrefix() const
{
   return prefix;
}

void Logger::setPrefix(const std::string &prefix)
{
   this->prefix = prefix;
}

const std::string& Logger::getSuffix() const
{
   return suffix;
}

void Logger::setSuffix(const std::string &suffix)
{
   this->suffix = suffix;
}

代码里有足够的注释,所以不解释了。

测试代码如下:

   void testLogger()
   {
      Logger logger("tc", "log");
      logger.write("write a message to the log file with default code, ext code and level.");
      logger.write("write a message to the log file with specified code and ext code.", 1, 2);
      logger.write("write a message to the log file with specified code, ext code and level.", 1, 2, 1);

      logger.write(Logger::getLogText<string>("calling getLogText", "OK"), 1);
      logger.write(Logger::getLogText<double>("calling getLogText", 0.142857), 2, 0, 1);
      logger.write(Logger::getLogText<int>("calling getLogText", 1024), 3, 1, 2);
   }

运行上述代码,将创建日志文件tc-2021-05-03.log,内容如下所示:

2021-05-03 09:18:33.244 - write a message to the log file with default code, ext code and level., CODE: 0, 0.
2021-05-03 09:18:33.244 - write a message to the log file with specified code and ext code., CODE: 1, 2.
2021-05-03 09:18:33.245 - ---write a message to the log file with specified code, ext code and level., CODE: 1, 2.
2021-05-03 09:18:33.245 - OPERATOR: calling getLogText, RESULT: OK, CODE: 1, 0.
2021-05-03 09:18:33.245 - ---OPERATOR: calling getLogText, RESULT: 0.142857, CODE: 2, 0.
2021-05-03 09:18:33.245 - ------OPERATOR: calling getLogText, RESULT: 1024, CODE: 3, 1.

以上日志文本中,每一行是一条日志,开头是动作发生的日期、时间(精确到毫秒),日志时间和日志正文之间用短横线分隔。日志正文前面没有三道短横线“---”的,表明这是顶级操作;前面有3*N个"---"的,表示这是上一条日志中所描述的动作的操作结果。这可以通过设置write(...)函数中的level的值来确定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值