Mutex.hpp
#pragma once
#include<iostream>
#include<pthread.h>
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_lock , nullptr);
}
void Lock()
{
pthread_mutex_lock(&_lock);
}
pthread_mutex_t *Ptr()
{
return &_lock;
}
void Unlock()
{
pthread_mutex_unlock(&_lock);
}
~Mutex()
{
pthread_mutex_destroy(&_lock);
}
private:
pthread_mutex_t _lock;
};
class LockGuard
{
public:
LockGuard(Mutex &lock):_lockref(lock)
{
_lockref.Lock();
}
~LockGuard()
{
_lockref.Unlock();
}
private:
Mutex &_lockref;
};
Logger.hpp
#ifndef __LOGGER_HPP
#define __LOGGER_HPP
#include<iostream>
#include<cstdio>
#include<string>
#include<memory>
#include<sstream>
#include<ctime>
#include<sys/time.h>
#include<unistd.h>
#include<filesystem>
#include<sys/stat.h>
#include<fstream>
#include"Mutex.hpp"
namespace NS_LOG_MODULE
{
enum class LogLevel
{
INFO,
WARNING,
ERROR,
FATAL,
DEBUG
};
std::string LogLevel2Message(LogLevel level)
{
switch(level)
{
case LogLevel::INFO:
return "INFO";
case LogLevel::WARNING:
return "WARNING";
case LogLevel::ERROR:
return "ERROR";
case LogLevel::FATAL:
return "FATAL";
case LogLevel::DEBUG:
return "DEBUG";
default:
return "UNKNOWN";
}
}
std::string GetCurrentTime()
{
struct timeval current_time;
int n = gettimeofday(¤t_time , nullptr);
(void)n;
struct tm struct_time;
localtime_r(&(current_time.tv_sec) , &struct_time);
char timestr[128];
snprintf(timestr , sizeof(timestr) , "%04d-%02d-%02d %02d:%02d:%02d.%ld" ,
struct_time.tm_year + 1900,
struct_time.tm_mon + 1,
struct_time.tm_mday,
struct_time.tm_hour,
struct_time.tm_min,
struct_time.tm_sec,
current_time.tv_usec);
return timestr;
}
// 策略模式,策略接口
// 1. 显示器打印
// 2. 文件写入
class LogStrategy
{
public:
virtual ~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 控制台日志刷新策略, 日志将来要向显示器打印
class ConsoleStartegy : public LogStrategy
{
public:
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cerr <<message <<"\n";
}
~ConsoleStartegy()
{}
private:
Mutex _mutex;
};
const std::string defaultpath = "./log";
const std::string defaultname = "log.txt";
// 文件策略
class FileLogStrategy :public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath , const std::string &name = defaultname):
_logpath(path),
_logfilename(name)
{
if(std::filesystem::exists(_logpath))
return;
try
{
std::filesystem::create_directories(_logpath);
}
catch(const std::filesystem::filesystem_error &e)
{
std::cerr<<e.what() <<"\n";
}
}
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
if(!_logpath.empty() && _logpath.back() !='/')
{
_logpath += "/";
}
std::string targetlog = _logpath + _logfilename;
std::ofstream out(targetlog , std::ios::app); // 追加写入
if(!out.is_open())
{
std::cerr<<"open" <<targetlog <<"failen" <<std::endl;
return;
}
out << message << "\n";
out.close();
}
~FileLogStrategy()
{}
private:
std::string _logpath;
std::string _logfilename;
Mutex _mutex;
};
class Logger
{
// 日志生成
public:
Logger()
{
UseConsoleStrategy();
}
void UseConsoleStrategy()
{
_strategy = std::make_unique<ConsoleStartegy>();
}
void UseFileStrategy()
{
_strategy = std::make_unique<FileLogStrategy>();
}
// void Debug(const std::string &message)
// {
// if(_strategy != nullptr)
// {
// _strategy->SyncLog(message);
// }
// }
class LogMessage
{
public:
LogMessage(LogLevel level , std::string &filename , int line , Logger &logger):
_level(level),
_curr_time(GetCurrentTime()),
_pid(getpid()),
_filename(filename),
_line(line),
_logger(logger)
{
std::stringstream ss;
ss <<"[" <<_curr_time <<"] "
<<"[" <<LogLevel2Message(_level) <<"] "
<<"[" <<_pid <<"] "
<<"[" <<_filename <<"] "
<<"[" <<_line <<"] "
<<" : ";
_loginfo = ss.str();
}
template<typename T>
LogMessage &operator << (const T &info)
{
std::stringstream ss;
ss <<info;
_loginfo += ss.str();
return *this; // 返回当前LogMessage对象
}
~LogMessage()
{
if(_logger._strategy)
{
_logger._strategy->SyncLog(_loginfo);
}
}
private:
LogLevel _level;
std::string _curr_time;
pid_t _pid;
std::string _filename;
int _line;
std::string _loginfo;
Logger &_logger;
};
LogMessage operator()(LogLevel level , std::string filename , int line)
{
return LogMessage(level , filename , line , *this);
}
~Logger()
{}
private:
std::unique_ptr<LogStrategy> _strategy; // 刷新策略
};
Logger logger;
#define ENABLE_CONSOLE_LOG_STRATEGY() logger.UseConsoleStrategy();
#define ENABLE_FILE_LOG_STRATEGY() logger.UseFileStrategy();
#define LOG(level) logger(level , __FILE__ , __LINE__)
}
#endif
Main.cc
#include "Logger.hpp"
#include"Mutex.hpp"
using namespace NS_LOG_MODULE;
int main()
{
// ENABLE_CONSOLE_LOG_STRATEGY();
ENABLE_FILE_LOG_STRATEGY();
LOG(LogLevel::DEBUG) <<"hello world " <<"star";
LOG(LogLevel::WARNING) <<"hello world " <<"star";
LOG(LogLevel::FATAL) <<"hello world " <<"star";
LOG(LogLevel::ERROR) <<"hello world " <<"star";
LOG(LogLevel::INFO) <<"hello world " <<"star";
// ENABLE_CONSOLE_LOG_STRATEGY();
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// ENABLE_FILE_LOG_STRATEGY();
// logger.Debug("file strategy!");
// logger.Debug("file strategy!");
// logger.Debug("file strategy!");
// logger.Debug("file strategy!");
// ENABLE_CONSOLE_LOG_STRATEGY();
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// logger.Debug("console strategy!");
// std::cout<<GetCurrentTime() <<std::endl;
// sleep(1000);
// std::cout<<GetCurrentTime() <<std::endl;
// sleep(1);
// std::cout<<GetCurrentTime() <<std::endl;
// sleep(1);
// std::cout<<GetCurrentTime() <<std::endl;
// sleep(1);
return 0;
}
Makefile
logger : Main.cc
g++ -o $@ $^ -std=c++17 -g
.PHONY:clean
clean:
rm -f logger
