#include <iostream>
#include <fstream>
#include <string>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <cstdarg>
#include <map>
#include <mutex>
#include <deque>
#include <filesystem>
#include <algorithm>
#define LOG_MAX_COUNT 100 /* 最大日志行数,超过则写新文件 */
class Logger
{
public:
enum class Level { DEBUG, INFO, WARNING, ERROR };
Logger(const std::string& filename) : mFilename(filename) {
//splitFile(mFilename); // 程序开始时检查是否需要切分文件
}
void log(Level level, const char* format, ...)
{
std::unique_lock<std::mutex> lock(mMutex);
// 判断是否需要切分文件
if (getLineCount(mFilename) >= LOG_MAX_COUNT)
{
printf("out of range %d\n", getLineCount(mFilename));
splitFile(mFilename);
}
std::ofstream outfile(mFilename, std::ios_base::app);
if (outfile.is_open())
{
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::system_clock::to_time_t(now);
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
std::string levelStr = mLevelStrings[level];
va_list args;
va_start(args, format);
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
outfile << std::put_time(std::localtime(×tamp), "%F %T.") << std::setw(3) << std::setfill('0') << milliseconds << " [" << levelStr << "] " << buffer << std::endl;
outfile.close();
// 将日志消息同时输出到控制台
std::cout << std::put_time(std::localtime(×tamp), "%F %T.") << std::setw(3) << std::setfill('0') << milliseconds << " [" << levelStr << "] " << buffer << std::endl;
}
}
std::deque<std::string> getLastLogs(int numLogs)
{
std::deque<std::string> result;
std::ifstream infile(mFilename);
if (infile.is_open())
{
std::string line;
std::deque<std::string> lines;
while (std::getline(infile, line))
{
lines.push_back(line);
if (lines.size() > numLogs)
{
lines.pop_front();
}
}
result = lines;
infile.close();
}
else
{
std::cout << " open err " << std::endl;
}
return result;
}
private:
std::string mFilename;
std::map<Level, std::string> mLevelStrings {
{Level::DEBUG, "DEBUG"},
{Level::INFO, "INFO"},
{Level::WARNING, "WARNING"},
{Level::ERROR, "ERROR"}
};
std::mutex mMutex;
// 获取文件行数
int getLineCount(const std::string& filename) {
int count = 0;
std::ifstream infile(filename);
if (infile.is_open()) {
std::string line;
while (std::getline(infile, line)) {
count++;
}
infile.close();
}
return count;
}
// 切分文件
void splitFile(const std::string& filename) {
// Get the current time with millisecond precision
auto timepoint = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
timepoint.time_since_epoch()).count();
auto time = std::chrono::system_clock::to_time_t(timepoint);
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%Y_%m_%d:%H:%M:%S");
ss << ":" << std::setfill('0') << std::setw(3) << ms % 1000;
std::string new_filename = filename + "_" + ss.str();
std::rename(filename.c_str(), new_filename.c_str());
std::ofstream outfile(filename);
outfile.close();
}
};
int main()
{
Logger logger("log.txt");
// 记录不同级别的日志消息
logger.log(Logger::Level::INFO, "程序已启动");
logger.log(Logger::Level::DEBUG, "变量 x 的值为 %d", 444);
logger.log(Logger::Level::WARNING, "内存使用过高%f", 116.555);
logger.log(Logger::Level::ERROR, "无法打开文件");
// 读取最近10条日志消息
std::deque<std::string> lastLogs = logger.getLastLogs(10);
std::cout << "Last 10 logs:" << std::endl;
for (const auto& log : lastLogs)
{
std::cout << log << std::endl;
}
return 0;
}
C++简易日志
最新推荐文章于 2024-07-25 09:47:19 发布