1.5 日志系统的设计
1. 公共头文件 public.hpp
enum class LOG_LEVEL
{FATAL = 0, // 致命错误ERROR = 1, // 错误WARN = 2, // 警告INFO = 3, // 信息 DEBUG = 4, // 调试TRACE = 5, // 跟踪,追踪 NUM_LOG_LEVELS, // 日志级别数
};
static const char * LLtoStr[]=
{{"FATAL"},{"ERROR"},{"WARN"},{"INFO"},{"DEBUG"},{"TRACE"},{"NUM_LOG_LEVELS"},
};
2. 时间戳类型的设计
enum class LOG_LEVEL
{FATAL = 0, // 致命错误ERROR = 1, // 错误WARN = 2, // 警告INFO = 3, // 信息 DEBUG = 4, // 调试TRACE = 5, // 跟踪,追踪 NUM_LOG_LEVELS, // 日志级别数
};static const char* LLtoStr[] =
{"FATAL","ERROR","WARN","INFO","DEBUG","TRACE","NUM_LOG_LEVELS"
};namespace tulun
{class Timestamp{private:int64_t microSecondsSinceEpoch_; // 从1970年1月1日到现在的微秒数public:explicit Timestamp(int64_t ms = 0);void swap(Timestamp& that);// 按不同格式输出时间string toString() const;string toFormattedString(bool showMs = true) const;// 时间戳是否有效bool valid() const;// for internal usage.int64_t getMicroSecond() const;time_t getSecond() const;public:// 获得当前时间的时间戳static Timestamp now();// 获得失效的时间戳// static Timestamp invalid();static const int kMicroSecondsPerSecond = 1000 * 1000;};// 内联函数减少函数调用的时间消耗inline bool operator<(const Timestamp& lhs, const Timestamp& rhs){return lhs.getMicroSecond() < rhs.getMicroSecond();}inline bool operator==(const Timestamp& lhs, const Timestamp& rhs){return lhs.getMicroSecond() == rhs.getMicroSecond();}inline int64_t timeDifference(const Timestamp& high, const Timestamp& low){return high.getMicroSecond() - low.getMicroSecond();}inline Timestamp addTime(const Timestamp& timestamp, const double seconds){int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);return Timestamp(delta);}
}
3. 日志流类型的设计
// C++
#include <string>
#include <sstream>
using namespace std;// own
#include "public.hpp"namespace tulun
{
class LogMessage
{
private:string header_; // 字段/标题[时间戳];[日志级别];[文件名称];[函数名称];[行号]string text_; // 正文信息LOG_LEVEL level_; // 日志信息的级别 public:// [日志级别];[文件名称];[函数名称];[行号] LogMessage(LOG_LEVEL level, const string& filename, const string& funcname, int linenumber);const string toString() const;LOG_LEVEL getLogLevel() const;// 添加正文信息template<typename T>LogMessage& operator<<(const T& txt){stringstream ss;ss << " : " << txt;text_ += ss.str();return *this;}
};
}
4. 日志类型设计
// C++
#include <functional> // function
#include <string>
using namespace std;// own
#include "public.hpp"
#include "LogMessage.hpp"namespace tulun
{
class Logger
{
public:// using OutputFunc = void (*)(const char *msg,int len);// using FlushFunc = void(*)();using OutputFunc = std::function<void(const std::string& msg)>;using FlushFunc = std::function<void()>;static OutputFunc output_;static FlushFunc flush_;public:static void setOutput(OutputFunc);static void setFlush(FlushFunc);private:LogMessage impl_; static LOG_LEVEL s_level_; // 日志级别public:// [日志级别];[文件名称];[函数名称];[行号] Logger(tulun::LOG_LEVEL level, const string& name, const string& func, const int line);~Logger();LogMessage& stream();static void setLogLevel(LOG_LEVEL level);static LOG_LEVEL getLogLevel();
};
}
5. 文件写类型的设计
// C++
#include <string>
#include <iostream>
#include <memory>
using namespace std;// C
#include <stdio.h>// not thread safe
namespace tulun
{class AppendFile{private:static const int FILEBUFFSIZE = 64 * 1024;FILE* fp_;// char buffer_[FILEBUFFSIZE]; // 64K;//8192std::unique_ptr<char[]> buffer_;size_t writtenBytes_; // 累计写入字节数size_t write(const char* info, const size_t len);public:AppendFile(const string& filename);~AppendFile();void append(const string& info);void append(const char* info, const size_t len);void flush();size_t writtenBytes() const;};
}
6. 日志文件类型的设计
// C++
#include <string>
#include <mutex>
#include <memory>
using namespace std;// C
#include <time.h> // own
#include "Timestamp.hpp"
#include "AppendFile.hpp"namespace tulun
{
class LogFile
{
private:const string basename_; // 日志文件名basename const size_t rollSize_; // 日志文件达到rollSize时生成新文件const int flushInterval_; // 日志写入间隔时间(秒级)const int checkEveryN_; // 每调用checkEveryN_次日志写就滚动一次日志int count_; // 写文件的次数 private:time_t startOfPeriod_; // 开始写日志的周期,同周期日志写入同一文件time_t lastRoll_; // 最后一次滚动日志的时间 time_t lastFlush_; // 上一次刷新日志文件时间private:std::unique_ptr<std::mutex> mutex_;std::unique_ptr<tulun::AppendFile> file_;static const int kRollPerSeconds_ = 60 * 60 * 24; // 滚动周期:1天private:// 不加锁的append方式void append_unlocked(const char* logline, const int len);// 获取日志文件的名称static string getLogFileName(const string& basename, const Timestamp& now);public:LogFile(const string& basename, // 日志文件basename size_t rollSize, // 日志文件达到rollsize生成新文件bool threadSafe = true, // 线程安全控制项,默认为true// 当只有一个后端AsnycLogging和后端线程时可置为falseint flushInterval = 3, // 日志写入间隔时间(秒级)int checkEveryN = 1024); // 每调用checkEveryN_次日志写就滚动一次日志~LogFile();void append(const std::string& info);void append(const char* info, const int len);void flush();bool rollFile();
};
}
1.6 类图
