前言
本篇是日志篇,流程是按照自己对sylar服务器框架视频的理解进行码字,部分代码可能会出现一些差别,只要输出内容没什么问题基本就是OK的。
代码流程
输出结果
首先先看一下日志的输出结果:
日期 线程号 协程号 日志级别 日志名称 文件名 行号 内容
2025-01-11 17:47:10 63450 0 [WARM] [SMlog] main.cc :36 message
基本框架(前期)
前期主要是包括五个部分:
一是日志级别 : 就是日志按照等级分类
二是日志事件:就是日志的组成部分 日期 线程号 协程号 日志级别 文件名 行号 内容 这些
三是日志器 :日志名称 日志级别阈值(只有logevent中的级别小于这个设置级别阈值,才可以输出) 日志的管理输出:真实的输出是logAppend类的log;
四是日志输出地:日志可以在终端输出 还可以在文件中进行输出 还是输出到远程服务器
五是日志格式器:传入logevent是乱序的,经过日志格式器的解析和和组装才可以按照我们想要的格式进行输出;
前期先简单实现这五个部分,后期在加上其他部分并且对这五个部分进行优化,本篇代码可能前期和后期会有些有些修改,具体完全体代码请看代码篇。
头文件
#ifndef __LOG_H__
#define __LOG_H__
#include<stdint.h>
#include<string>
#include<memory>
#include<list>
#include<time.h>
namespace sylar
{
// 日志级别
class LogLevel
{
public:
enum Level
{
UNKNOW = 0, // 未知
DEBUG = 1, // 调试
INFO = 2, // 输出
WARN = 3, // 警告
ERROR = 4, // 错误
FETAL = 5, // 灾难
};
};
class Logger;
// 日志事件
class LogEvent
{
public:
typedef std::shared_ptr<LogEvent> ptr;
// 有参构造
LogEvent(LogLevel::Level level, uint64_t dateTime, uint32_t threadId, uint32_t fiberId, const char *file, uint32_t line, const std::string &message);
// 析构
~LogEvent() = default;
// 获得日期
uint64_t getDateTime() const { return dateTime_; }
// 获得线程号
uint32_t getThreadId() const { return threadId_; }
// 获得协程号
uint32_t getFiberId() const { return fiberId_; }
// 获得日志级别
LogLevel::Level getLevel() const { return level_; }
// 获得文件名
const char *getFile() const { return file_; }
// 获得行号
uint32_t getLine() const { return line_; }
// 输出内容
const std::string &getMessage() const { return message_; }
// 获得运行时间
uint32_t getElapse() const { return elapse_; }
private:
uint64_t dateTime_ = 0; // 日期
uint32_t threadId_ = 0; // 线程号
uint32_t fiberId_ = 0; // 协程号
LogLevel::Level level_; // 日志级别
const char *file_ = nullptr; // 文件名
uint32_t line_ = 0; // 行号
std::string message_ = \”\”; // 输出内容
uint32_t elapse_ = 0; // 系统运行时间
};
// 日志输出目的地
class LogAppend
{
public:
typedef std::shared_ptr<LogAppend> ptr;
//有参构造
LogAppend() = default;
//析构函数
virtual ~LogAppend(){}
//log需要event中的成员 还有logger中的name
virtual void log(std::shared_ptr<Logger> logger,std::shared_ptr<LogEvent> event) = 0;
protected:
};
//输出到终端
class ToStdLogAppend:public LogAppend
{
public:
typedef std::shared_ptr<ToStdLogAppend> ptr;
//析构
virtual ~ToStdLogAppend()override{}
//log输出函数
virtual void log(std::shared_ptr<Logger> logger,std::shared_ptr<LogEvent> event)override;
private:
};
//输出到文件
class ToFileLogAppend:public LogAppend
{
public:
typedef std::shared_ptr<ToFileLogAppend>ptr;
//有参构造
ToFileLogAppend(const std::string& name);
//析构
virtual ~ToFileLogAppend()override{}
//log输出函数
virtual void log(std::shared_ptr<Logger> logger,std::shared_ptr<LogEvent> event)override;
private:
std::string name_; //文件名
};
// 日志器
class Logger:public std::enable_shared_from_this<Logger>
{
public:
typedef std::shared_ptr<Logger>ptr;
// 有参构造
Logger(LogLevel::Level level, std::string name = \”root\”);
// 析构函数
~Logger() = default;
// 调度输出 真实的输出是LogAppend中的log
void log(std::shared_ptr<LogEvent>event);
//添加日志输出地
void addAppend(std::shared_ptr<LogAppend>append);
//删除日志输出地
bool delAppend(std::shared_ptr<LogAppend>append);
// 获得日志器名字
const std::string &getName() const { return name_;}
//获得日志级别阈值
LogLevel::Level getLevel() const {return level_;}
private:
std::string name_; // 日志名称
LogLevel::Level level_; // 日志级别阈值
std::list<std::shared_ptr<LogAppend>> appends_; // 真实的输出类
};
// 日志解析器
class LogFormatter
{
public:
private:
};
}
#endif
源文件
#include \”log.h\”
#include<iostream>
namespace sylar
{
///LogEvent
// 构造函数 传日志参数
LogEvent::LogEvent(LogLevel::Level level, uint64_t dateTime, uint32_t threadId,
uint32_t fiberId, const char *file, uint32_t line, const std::string &message)
: level_(level), dateTime_(dateTime), threadId_(threadId),
fiberId_(fiberId), file_(file), line_(line), message_(message)
{
}
///Logger
// 有参构造 传日志参数
Logger::Logger(LogLevel::Level level, std::string name):name_(name),level_(level)
{
}
// 调度输出 真实的输出是LogAppend中的log
void Logger::log(std::shared_ptr<LogEvent>event)
{
//真实输出
auto self = shared_from_this();
for(auto it:appends_)
{
it ->log(self,event);
}
}
// 添加日志输出地
void Logger::addAppend(std::shared_ptr<LogAppend> append)
{
for(auto it : appends_)
{
//如果相等说明已经存在 就不用添加这个输出路径了
if(it == append)
{
return ;
}
}
//没有该路径就添加
appends_.push_back(append);
}
// 删除日志输出地
bool Logger::delAppend(std::shared_ptr<LogAppend> append)
{
for(auto it = appends_.begin();it!= appends_.end();it++)
{
if(*it == append)
{
//找到就删除
appends_.erase(it);
return true;
}
}
//没找到返回没找到
return false;
}
//LogAppend
//输出到终端
//log真实的输出 类内声明加virtual 类外不用再加virtual
void ToStdLogAppend::log(std::shared_ptr<Logger> logger,std::shared_ptr<LogEvent> event)
{
if(event->getLevel()<=logger->getLevel())
{
std::cout<<event->getDateTime()<<\” \”
<<event->getThreadId()<<\” \”
<<event->getFiberId()<<\” \”
<<event->getLevel()<<\” \”
<<logger->getName()<<\” \”
<<event->getFile()<<\” : \”
<<event->getLine()<<\” \”
<<event->getMessage()<<std::endl;
}
}
//输出到文件
//有参构造
ToFileLogAppend::ToFileLogAppend(const std::string& name):name_(name)
{
}
//log输出函数
void ToFileLogAppend::log(std::shared_ptr<Logger> logger,std::shared_ptr<LogEvent> event)
{
std::cout<<\”输出到file\”<<std::endl;
}
}
测试
#include<iostream>
#include\”log.h\”
int main()
{
sylar::Logger::ptr logger(new sylar::Logger(sylar::LogLevel::INFO,\”system\”));
sylar::LogEvent::ptr event(new sylar::LogEvent(sylar::LogLevel::DEBUG,time(0),555,0,\”log.txt\”,10,\”hello world\”));
sylar::LogAppend::ptr logappend(new sylar::ToStdLogAppend());
logger->addAppend(logappend);
logger->log(event);
return 0;
}
编译:
g++ -o project log.cc logtest1.cc -std=c++11
也是写的第四遍,没想到编译一下就通过了,所以大家一定要多写,虽然这个项目对我们学习者困难,但是只要不断的练习和分析和总结,一定能有特别大的收获。
目前我们已经能输出了,但是这并不是我们想要的结果,雏形已经形成了,下一步就是不断丰富代码,一步一步的实现最终结果。下一步我们先完成LogLevel的问题,目前我们输出的是数字,下一步我们把他实现为[ XXXX ]的形式.
代码实现: LogLevel正确输出
sylar使用了宏定义的
评论前必须登录!
注册