1、简单逻辑
进程中单例模式Logger类,第一次getInstance()进行初始化后,并启动log输出线程。
若不使用setLogFile()设置log输出文件,默认是标准输出std::cout。
用户格式化的log信息,加锁入队,使用条件变量通知唤醒等待的log输出线程。
log输出线程中,当队列不为空,就进行输出到制定文件或者std::cout,否者wait(),解锁,直到有入队唤醒,再次加锁。
2、Logger.h 源码
#pragma once
#include <queue>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <fstream>
#include <cstring>
#include <iostream>
#include <sstream>
enum Priority {
LOG_DEBUG, LOG_STATE, LOG_INFO, LOG_WARNING, LOG_ERROR,
};
class Logger
{
public:
Logger &operator=(const Logger &) = delete;
Logger(const Logger &) = delete;
static Logger& instance();
~Logger();
void setLogFile(char *pathname);
void log(Priority priority, const char* __file, const char* __func, int __line, const char *fmt, ...);
void log2(Priority priority, const char *fmt, ...);
private:
Logger();
void run();
std::atomic<bool> _shutdown;
std::thread _thread;
std::mutex _mutex;
std::condition_variable _cond;
std::queue<std::string> _queue;
std::ofstream _ofs;
};
}
#ifdef _DEBUG
#define LOG_DEBUG(fmt, ...) Logger::instance().log(LOG_DEBUG, __FILE__, __FUNCTION__,__LINE__, fmt, ##__VA_ARGS__)
#else
#define LOG_DEBUG(fmt, ...)
#endif
#define LOG_INFO(fmt, ...) Logger::instance().log2(LOG_INFO, fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) Logger::instance().log(LOG_ERROR, __FILE__, __FUNCTION__,__LINE__, fmt, ##__VA_ARGS__)
class Timestamp
{
public:
Timestamp()
: _beginTimePoint(std::chrono::high_resolution_clock::now())
{ }
void reset()
{
_beginTimePoint = std::chrono::high_resolution_clock::now();
}
int64_t elapsed()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - _beginTimePoint).count();
}
static std::string localtime();
private:
std::chrono::time_point<std::chrono::high_resolution_clock> _beginTimePoint;
};
3、Logger.cpp 源码
#if defined(WIN32) || defined(_WIN32)
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif
#include "Logger.h"
#include <stdarg.h>
#include <iostream>
#include "Timestamp.h"
const char* Priority_To_String[] =
{
"DEBUG",
"CONFIG",
"INFO",
"WARNING",
"ERROR"
};
Logger::Logger()
: _shutdown(false)
{
_thread = std::thread(&Logger::run, this);
}
Logger& Logger::instance()
{
static Logger s_logger;
return s_logger;
}
Logger::~Logger()
{
_shutdown = true;
_cond.notify_all();
_thread.join();
}
void Logger::setLogFile(char *pathname)
{
_ofs.open(pathname);
if (_ofs.fail())
{
std::cerr << "Failed to open logfile." << std::endl;
}
}
void Logger::log(Priority priority, const char* __file, const char* __func, int __line, const char *fmt, ...)
{
char buf[2048] = {0};
sprintf(buf, "[%s][%s:%s:%d] ", Priority_To_String[priority], __file, __func, __line);
va_list args;
va_start(args, fmt);
vsprintf(buf + strlen(buf), fmt, args);
va_end(args);
std::string entry(buf);
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(std::move(entry));
lock.unlock();
_cond.notify_all();
}
void Logger::log2(Priority priority, const char *fmt, ...)
{
char buf[4096] = { 0 };
sprintf(buf, "[%s] ", Priority_To_String[priority]);
va_list args;
va_start(args, fmt);
vsprintf(buf + strlen(buf), fmt, args);
va_end(args);
std::string entry(buf);
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(std::move(entry));
lock.unlock();
_cond.notify_all();
}
void Logger::run()
{
std::unique_lock<std::mutex> lock(_mutex);
while(!_shutdown)
{
if(!_queue.empty())
{
if(_ofs.is_open() && (!_shutdown))
_ofs << "[" << Timestamp::localtime() << "]"
<< _queue.front() << std::endl;
else
std::cout << "[" << Timestamp::localtime() << "]"
<< _queue.front() << std::endl;
_queue.pop();
}
else
{
_cond.wait(lock);
}
}
}
std::string Timestamp::localtime()
{
std::ostringstream stream;
auto now = system_clock::now();
time_t tt = system_clock::to_time_t(now);
#if defined(WIN32) || defined(_WIN32)
struct tm tm;
localtime_s(&tm, &tt);
stream << std::put_time(&tm, "%F %T");
#elif defined(__linux) || defined(__linux__)
char buffer[200] = {0};
std::string timeString;
std::strftime(buffer, 200, "%F %T", std::localtime(&tt));
stream << buffer;
#endif
return stream.str();
}
单例模式的日志记录器设计
本文介绍了一个基于单例模式实现的日志记录器类,详细解释了其工作流程,包括线程安全的初始化、日志消息的格式化与队列化、以及异步输出至文件或标准输出。此外,提供了完整的源代码实现。
4572

被折叠的 条评论
为什么被折叠?



