什么是设计模式
IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式
日志认识
计算机中的日志是记录系统和软件运行中发发事件的文件,主要作用是监控运行状态、记录异常信
息,帮助快速定位问题并支持程序员进行问题修复。它是系统维护、故障排查和安全管理的重要工
具。
日志格式以下几个指标是必须得有的
- 时间戳
- 日志等级
- 日志内容
以下几个指标是可选的
- 文件名行号
- 进程,线程相关id信息等
日志有现成的解决方案,如:spdlog、glog、Boost.Log、Log4cxx等等,我们依旧采用自定义日志的方式。
这里我们采用设计模式-策略模式来进行日志的设计,我们想要的日志格式如下:
[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容,⽀持可
变参数
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world
[2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world
日志功能:
- 形成完整日志
- 刷新到目标文件(显示器,指定文件打印日志)
多态实现两种策略的实现
Log.hpp
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
namespace LogModule
{
const std::string sep = "\r\n";
using namespace MutexModule;
// 2.刷新策略
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 显示器刷新日志的策略
class ConsoleLogStrategy : public LogStrategy
{
public:
ConsoleLogStrategy() {}
~ConsoleLogStrategy() {}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cout << message << sep;
}
private:
Mutex _mutex;
};
// 缺省文件路径以及文件本身
const std::string defaultpath = "./log";
const std::string defaultfile = "my.log";
// 文件刷新日志的策略
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
: _path(path), _file(file)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_path)) // 判断路径是否存在
{
return;
}
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app); // 追加写入
if (!out.is_open())
{
return;
}
out << message << sep;
out.close();
}
~FileLogStrategy() {}
private:
Mutex _mutex;
std::string _path; // 日志文件的路径
std::string _file; // 要打印的日志文件
};
}
#endif
Main.cc
#include <iostream>
#include "Log.hpp"
#include <memory>
using namespace LogModule;
int main()
{ // 显示器刷新
std::unique_ptr<LogStrategy> strategy1 = std::make_unique<ConsoleLogStrategy>(); // c++14
strategy1->SyncLog("hello log1!");
// 指定文件刷新
std::unique_ptr<LogStrategy> strategy2 = std::make_unique<FileLogStrategy>(); // c++14
strategy2->SyncLog("hello log2!");
return 0;
}
形成完整日志
Log.hpp
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
namespace LogModule
{
const std::string sep = "\r\n";
using namespace MutexModule;
// 2.刷新策略
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 显示器刷新日志的策略
class ConsoleLogStrategy : public LogStrategy
{
public:
ConsoleLogStrategy() {}
~ConsoleLogStrategy() {}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cout << message << sep;
}
private:
Mutex _mutex;
};
// 缺省文件路径以及文件本身
const std::string defaultpath = "./log";
const std::string defaultfile = "my.log";
// 文件刷新日志的策略
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
: _path(path), _file(file)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_path)) // 判断路径是否存在
{
return;
}
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app); // 追加写入
if (!out.is_open())
{
return;
}
out << message << sep;
out.close();
}
~FileLogStrategy() {}
private:
Mutex _mutex;
std::string _path; // 日志文件的路径
std::string _file; // 要打印的日志文件
};
// 形成日志等级
enum class Loglevel
{
DEBUG,
INIF,
WARNING,
ERROR,
FATAL
};
std::string Level2Str(Loglevel level)
{
switch (level)
{
case Loglevel::DEBUG:
return "DEBUG";
case Loglevel::INIF:
return "INIF";
case Loglevel::WARNING:
return "WARNING";
case Loglevel::ERROR:
return "ERROR";
case Loglevel::FATAL:
return "FATAL";
default:
return "UNKNOWN";
}
}
std::string GetTimeStamp()
{
return "XXX";
}
class Logger
{
public:
Logger()
{
EnableConsoleLogStrategy();
}
// 选择某种策略
// 1.文件
void EnableFileLogStrategy()
{
_ffush_strategy = std::make_unique<FileLogStrategy>();
}
// 显示器
void EnableConsoleLogStrategy()
{
_ffush_strategy = std::make_unique<ConsoleLogStrategy>();
}
// 表示的是未来的一条日志
class LogMessage
{
public:
LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger)
: _curr_time(GetTimeStamp())
, _level(level)
, _pid(getpid())
, _src_name(src_name)
, _line_number(line_number)
, _logger(logger)
{
// 合并左半部分
std::stringstream ss;
ss << "[" << _curr_time << "] "
<< "[" << Level2Str(_level) << "] "
<< "[" << _pid << "] "
<< "[" << _src_name << "] "
<< "[" << _line_number << "] "
<< "- ";
_loginfo = ss.str();
}
template <typename T>
LogMessage &operator<<(const T &info)
{
// 右半部分,可变
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this;
}
~LogMessage()
{
if (_logger._ffush_strategy)
{
_logger._ffush_strategy->SyncLog(_loginfo);
}
}
private:
std::string _curr_time; // 日志时间
Loglevel _level; // 日志状态
pid_t _pid; // 进程pid
std::string _src_name; // 文件名称
int _line_number; // 对应的行号
std::string _loginfo; // 合并之后的一条完整信息
Logger &_logger;
};
LogMessage operator()(Loglevel level, std::string src_name, int line_number)
{
return LogMessage(level, src_name, line_number, *this);
}
~Logger() {}
private:
std::unique_ptr<LogStrategy> _ffush_strategy;
};
}
#endif
Main.cc
#include <iostream>
#include "Log.hpp"
#include <memory>
using namespace LogModule;
int main()
{
Logger log;
//显示器打印
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
//日志文件打印
log.EnableFileLogStrategy();
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
log(Loglevel::DEBUG,"Main.cc",10)<<"hello log!";
return 0;
}
使用宏简化代码
Log.hpp
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
namespace LogModule
{
const std::string sep = "\r\n";
using namespace MutexModule;
// 2.刷新策略
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 显示器刷新日志的策略
class ConsoleLogStrategy : public LogStrategy
{
public:
ConsoleLogStrategy() {}
~ConsoleLogStrategy() {}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cout << message << sep;
}
private:
Mutex _mutex;
};
// 缺省文件路径以及文件本身
const std::string defaultpath = "./log";
const std::string defaultfile = "my.log";
// 文件刷新日志的策略
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
: _path(path), _file(file)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_path)) // 判断路径是否存在
{
return;
}
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app); // 追加写入
if (!out.is_open())
{
return;
}
out << message << sep;
out.close();
}
~FileLogStrategy() {}
private:
Mutex _mutex;
std::string _path; // 日志文件的路径
std::string _file; // 要打印的日志文件
};
// 形成日志等级
enum class Loglevel
{
DEBUG,
INIF,
WARNING,
ERROR,
FATAL
};
std::string Level2Str(Loglevel level)
{
switch (level)
{
case Loglevel::DEBUG:
return "DEBUG";
case Loglevel::INIF:
return "INIF";
case Loglevel::WARNING:
return "WARNING";
case Loglevel::ERROR:
return "ERROR";
case Loglevel::FATAL:
return "FATAL";
default:
return "UNKNOWN";
}
}
std::string GetTimeStamp()
{
return "XXX";
}
class Logger
{
public:
Logger()
{
EnableConsoleLogStrategy();
}
// 选择某种策略
// 1.文件
void EnableFileLogStrategy()
{
_ffush_strategy = std::make_unique<FileLogStrategy>();
}
// 显示器
void EnableConsoleLogStrategy()
{
_ffush_strategy = std::make_unique<ConsoleLogStrategy>();
}
// 表示的是未来的一条日志
class LogMessage
{
public:
LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger)
: _curr_time(GetTimeStamp())
, _level(level)
, _pid(getpid())
, _src_name(src_name)
, _line_number(line_number)
, _logger(logger)
{
// 合并左半部分
std::stringstream ss;
ss << "[" << _curr_time << "] "
<< "[" << Level2Str(_level) << "] "
<< "[" << _pid << "] "
<< "[" << _src_name << "] "
<< "[" << _line_number << "] "
<< "- ";
_loginfo = ss.str();
}
template <typename T>
LogMessage &operator<<(const T &info)
{
// 右半部分,可变
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this;
}
~LogMessage()
{
if (_logger._ffush_strategy)
{
_logger._ffush_strategy->SyncLog(_loginfo);
}
}
private:
std::string _curr_time; // 日志时间
Loglevel _level; // 日志状态
pid_t _pid; // 进程pid
std::string _src_name; // 文件名称
int _line_number; // 对应的行号
std::string _loginfo; // 合并之后的一条完整信息
Logger &_logger;
};
LogMessage operator()(Loglevel level, std::string src_name, int line_number)
{
return LogMessage(level, src_name, line_number, *this);
}
~Logger() {}
private:
std::unique_ptr<LogStrategy> _ffush_strategy;
};
//全局日志对象
Logger logger;
//使用宏,简化用户操作,获取文件名和行号
// __FILE__ 一个宏,替换完成后目标文件的文件名
// __LINE__ 一个宏,替换完成后目标文件对应的行号
#define LOG(level) logger(level,__FILE__,__LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}
#endif
Main.cc
#include <iostream>
#include "Log.hpp"
#include <memory>
using namespace LogModule;
int main()
{
Logger log;
//显示器打印
Enable_Console_Log_Strategy();
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
//日志文件打印
Enable_File_Log_Strategy();
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
return 0;
}
补上时间,收工
完整日志实现代码
Log.hpp
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
#include<ctime>
namespace LogModule
{
const std::string sep = "\r\n";
using namespace MutexModule;
// 2.刷新策略
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 显示器刷新日志的策略
class ConsoleLogStrategy : public LogStrategy
{
public:
ConsoleLogStrategy() {}
~ConsoleLogStrategy() {}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cout << message << sep;
}
private:
Mutex _mutex;
};
// 缺省文件路径以及文件本身
const std::string defaultpath = "./log";
const std::string defaultfile = "my.log";
// 文件刷新日志的策略
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
: _path(path), _file(file)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_path)) // 判断路径是否存在
{
return;
}
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app); // 追加写入
if (!out.is_open())
{
return;
}
out << message << sep;
out.close();
}
~FileLogStrategy() {}
private:
Mutex _mutex;
std::string _path; // 日志文件的路径
std::string _file; // 要打印的日志文件
};
// 形成日志等级
enum class Loglevel
{
DEBUG,
INIF,
WARNING,
ERROR,
FATAL
};
std::string Level2Str(Loglevel level)
{
switch (level)
{
case Loglevel::DEBUG:
return "DEBUG";
case Loglevel::INIF:
return "INIF";
case Loglevel::WARNING:
return "WARNING";
case Loglevel::ERROR:
return "ERROR";
case Loglevel::FATAL:
return "FATAL";
default:
return "UNKNOWN";
}
}
std::string GetTimeStamp()
{
time_t cuur =time(nullptr);
struct tm curr_tm;
localtime_r(&cuur,&curr_tm);
char buffer[128];
snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d",
curr_tm.tm_year+1900,
curr_tm.tm_mon+1,
curr_tm.tm_mday,
curr_tm.tm_hour,
curr_tm.tm_min,
curr_tm.tm_sec
);
return buffer;
}
class Logger
{
public:
Logger()
{
EnableConsoleLogStrategy();
}
// 选择某种策略
// 1.文件
void EnableFileLogStrategy()
{
_ffush_strategy = std::make_unique<FileLogStrategy>();
}
// 显示器
void EnableConsoleLogStrategy()
{
_ffush_strategy = std::make_unique<ConsoleLogStrategy>();
}
// 表示的是未来的一条日志
class LogMessage
{
public:
LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger)
: _curr_time(GetTimeStamp())
, _level(level)
, _pid(getpid())
, _src_name(src_name)
, _line_number(line_number)
, _logger(logger)
{
// 合并左半部分
std::stringstream ss;
ss << "[" << _curr_time << "] "
<< "[" << Level2Str(_level) << "] "
<< "[" << _pid << "] "
<< "[" << _src_name << "] "
<< "[" << _line_number << "] "
<< "- ";
_loginfo = ss.str();
}
template <typename T>
LogMessage &operator<<(const T &info)
{
// 右半部分,可变
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this;
}
~LogMessage()
{
if (_logger._ffush_strategy)
{
_logger._ffush_strategy->SyncLog(_loginfo);
}
}
private:
std::string _curr_time; // 日志时间
Loglevel _level; // 日志状态
pid_t _pid; // 进程pid
std::string _src_name; // 文件名称
int _line_number; // 对应的行号
std::string _loginfo; // 合并之后的一条完整信息
Logger &_logger;
};
LogMessage operator()(Loglevel level, std::string src_name, int line_number)
{
return LogMessage(level, src_name, line_number, *this);
}
~Logger() {}
private:
std::unique_ptr<LogStrategy> _ffush_strategy;
};
//全局日志对象
Logger logger;
//使用宏,简化用户操作,获取文件名和行号
// __FILE__ 一个宏,替换完成后目标文件的文件名
// __LINE__ 一个宏,替换完成后目标文件对应的行号
#define LOG(level) logger(level,__FILE__,__LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}
#endif
Mutex.hpp
#include <pthread.h>
#include <iostream>
namespace MutexModule
{
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_mutex, nullptr);
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
(void)n;
}
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
(void)n;
}
~Mutex()
{
pthread_mutex_destroy(&_mutex);
}
pthread_mutex_t *get()
{
return &_mutex;
}
private:
pthread_mutex_t _mutex;
};
class LockGuard
{
public:
LockGuard(Mutex &mutex):_mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex &_mutex;
};
}
Main.cc
#include <iostream>
#include "Log.hpp"
#include <memory>
using namespace LogModule;
int main()
{
Logger log;
//显示器打印
Enable_Console_Log_Strategy();
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
//日志文件打印
Enable_File_Log_Strategy();
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
LOG(Loglevel::DEBUG)<<"hello"<<" log!";
return 0;
}