当前位置: 首页 > news >正文

做网站首页多少钱企业页面

做网站首页多少钱,企业页面,建立个人博客网站,成都广告公司简介目录 1.项目介绍 2.整体框架设计 3.⽇志输出格式化类设计 4.⽇志落地(LogSink)类设计 5.⽇志器类(Logger)设计#xff08;建造者模式#xff09; 6.双缓冲区异步任务处理器#xff08;AsyncLooper#xff09;设计 7.⽇志宏全局接⼝设计#xff08;代理模式建造者模式 6.双缓冲区异步任务处理器AsyncLooper设计 7.⽇志宏全局接⼝设计代理模式 8.性能测试 9.扩展 10.参考资料 1.项目介绍 本项⽬主要实现⼀个⽇志系统 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.整体框架设计 本项⽬实现的是⼀个多⽇志器⽇志系统主要实现的功能是让程序员能够轻松的将程序运⾏⽇志信息落地到指定的位置且⽀持同步与异步两种⽅式的⽇志落地⽅式。主要分为 1.日志等级模块对输出⽇志的等级进⾏划分以便于控制⽇志的输出并提供等级枚举转字符串功 能。 ◦ OFF关闭 ◦ DEBUG调试调试时的关键信息输出。 ◦ INFO提⽰普通的提⽰型⽇志信息。 ◦ WARN警告不影响运⾏但是需要注意⼀下的⽇志。 ◦ ERROR错误程序运⾏出现错误的⽇志 ◦ FATAL致命⼀般是代码异常导致程序⽆法继续推进运⾏的⽇志 2.日志消息模块中间存储⽇志输出所需的各项要素信息 ◦ 时间描述本条⽇志的输出时间。 ◦ 线程ID描述本条⽇志是哪个线程输出的。 ◦ 日志等级描述本条⽇志的等级。 ◦ 日志数据本条⽇志的有效载荷数据。 ◦ 日志⽂件名描述本条⽇志在哪个源码⽂件中输出的。 ◦ 日志⾏号描述本条⽇志在源码⽂件的哪⼀⾏输出的。 3. ⽇志消息格式化模块设置⽇志输出格式并提供对⽇志消息进⾏格式化功能。 ◦ 系统的默认⽇志输出格式%d{%H:%M:%S}%T[%t]%T[%p]%T[%c]%T%f:%l%T%m%n ◦ - 13:26:32 [2343223321] [FATAL] [root] main.c:76 套接字创建失败\n ◦ %d{%H:%M:%S}表⽰⽇期时间花括号中的内容表⽰⽇期时间的格式。 ◦ %T表⽰制表符缩进。 ◦ %t表⽰线程ID ◦ %p表⽰⽇志级别 ◦ %c表⽰⽇志器名称不同的开发组可以创建⾃⼰的⽇志器进⾏⽇志输出⼩组之间互不影 响。 ◦ %f表⽰⽇志输出时的源代码⽂件名。 ◦ %l表⽰⽇志输出时的源代码⾏号。 ◦ %m表⽰给与的⽇志有效载荷数据 ◦ %n表⽰换⾏ ◦ 设计思想设计不同的⼦类不同的⼦类从⽇志消息中取出不同的数据进⾏处理。 4. ⽇志消息落地模块决定了⽇志的落地⽅向可以是标准输出也可以是⽇志⽂件也可以滚动⽂ 件输出.... ◦ 标准输出表⽰将⽇志进⾏标准输出的打印。 ◦ ⽇志⽂件输出表⽰将⽇志写⼊指定的⽂件末尾。 ◦ 滚动⽂件输出当前以⽂件⼤⼩进⾏控制当⼀个⽇志⽂件⼤⼩达到指定⼤⼩则切换下⼀个 ⽂件进⾏输出 ◦ 后期也可以扩展远程⽇志输出创建客⼾端将⽇志消息发送给远程的⽇志分析服务器。 ◦ 设计思想设计不同的⼦类不同的⼦类控制不同的⽇志落地⽅向。 5. ⽇志器模块 ◦ 此模块是对以上⼏个模块的整合模块⽤⼾通过⽇志器进⾏⽇志的输出有效降低⽤⼾的使⽤ 难度。 ◦ 包含有⽇志消息落地模块对象⽇志消息格式化模块对象⽇志输出等级。 6. ⽇志器管理模块 ◦ 为了降低项⽬开发的⽇志耦合不同的项⽬组可以有⾃⼰的⽇志器来控制输出格式以及落地⽅ 向因此本项⽬是⼀个多⽇志器的⽇志系统。 ◦ 管理模块就是对创建的所有⽇志器进⾏统⼀管理。并提供⼀个默认⽇志器提供标准输出的⽇志 输出。 7. 异步线程模块 ◦ 实现对⽇志的异步输出功能⽤⼾只需要将输出⽇志任务放⼊任务池异步线程负责⽇志的落 地输出功能以此提供更加⾼效的⾮阻塞⽇志输出。 3.⽇志输出格式化类设计 ⽇志格式化Formatter类主要负责格式化⽇志消息。其主要包含以下内容 • pattern成员保存⽇志输出的格式字符串。 ◦ %d ⽇期 ◦ %T 缩进 ◦ %t 线程id ◦ %p ⽇志级别 ◦ %c ⽇志器名称 ◦ %f ⽂件名 ◦ %l ⾏号 ◦ %m ⽇志消息 ◦ %n 换⾏ • std::vectorFormatItem::ptr items成员⽤于按序保存格式化字符串对应的⼦格式化对象。 FormatItem类主要负责⽇志消息⼦项的获取及格式化。其包含以下⼦类 • MsgFormatItem 表⽰要从LogMsg中取出有效⽇志数据 • LevelFormatItem 表⽰要从LogMsg中取出⽇志等级 • NameFormatItem 表⽰要从LogMsg中取出⽇志器名称 • ThreadFormatItem 表⽰要从LogMsg中取出线程ID • TimeFormatItem 表⽰要从LogMsg中取出时间戳并按照指定格式进⾏格式化 • CFileFormatItem 表⽰要从LogMsg中取出源码所在⽂件名 • CLineFormatItem 表⽰要从LogMsg中取出源码所在⾏号 • TabFormatItem 表⽰⼀个制表符缩进 • NLineFormatItem 表⽰⼀个换⾏ • OtherFormatItem 表⽰⾮格式化的原始字符串 ⽰例[%d{%H:%M:%S}] %m%n pattern [%d{%H:%M:%S}] %m%n items {{OtherFormatItem(), [},{TimeFormatItem(), %H:%M:%S},{OtherFormatItem(), ]},{MsgFormatItem (), },{NLineFormatItem (), } } LogMsg msg {size_t _line 22;size_t _ctime 12345678;std::thread::id _tid 0x12345678;std::string _name logger;std::string _file main.cpp;std::string _payload 创建套接字失败;LogLevel::value _level ERROR; }; 格式化的过程其实就是按次序从Msg中取出需要的数据进⾏字符串的连接的过程。 最终组织出来的格式化消息 [22:32:54] 创建套接字失败\n 代码实现 #ifndef __M_FMT_H__ #define __M_FMT_H__ #include util.hpp #include message.hpp #include level.hpp #include memory #include vector #include tuple namespace bitlog{ class FormatItem{public:using ptr std::shared_ptrFormatItem;virtual ~FormatItem() {}virtual void format(std::ostream os, const LogMsg msg) 0; }; class MsgFormatItem : public FormatItem {public:MsgFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os msg._payload;} }; class LevelFormatItem : public FormatItem {public:LevelFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os LogLevel::toString(msg._level);} }; class NameFormatItem : public FormatItem {public:NameFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os msg._name;} }; class ThreadFormatItem : public FormatItem {public:ThreadFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os msg._tid;} }; class TimeFormatItem : public FormatItem {private:std::string _format;public:TimeFormatItem(const std::string format %H:%M:%S):_format(format){if (format.empty()) _format %H:%M:%S;}virtual void format(std::ostream os, const LogMsg msg) {time_t t msg._ctime;struct tm lt;localtime_r(t, lt);char tmp[128];strftime(tmp, 127, _format.c_str(), lt);os tmp;} }; class CFileFormatItem : public FormatItem {public:CFileFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os msg._file;} }; class CLineFormatItem : public FormatItem {public:CLineFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os msg._line;} }; class TabFormatItem : public FormatItem {public:TabFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os \t;} }; class NLineFormatItem : public FormatItem {public:NLineFormatItem(const std::string str ){}virtual void format(std::ostream os, const LogMsg msg) {os \n;} }; class OtherFormatItem : public FormatItem {private:std::string _str;public:OtherFormatItem(const std::string str ):_str(str){}virtual void format(std::ostream os, const LogMsg msg) {os _str;} }; class Formatter {public:using ptr std::shared_ptrFormatter;/*%d ⽇期%T 缩进%t 线程id%p ⽇志级别%c ⽇志器名称%f ⽂件名%l ⾏号%m ⽇志消息%n 换⾏*/Formatter(const std::string pattern [%d{%H:%M:%S}][%t][%p][%c] [%f:%l] %m%n):_pattern(pattern){assert(parsePattern());}const std::string pattern() { return _pattern; }std::string format(const LogMsg msg) {std::stringstream ss;for (auto it : _items) {it-format(ss, msg);}return ss.str();}std::ostream format(std::ostream os, const LogMsg msg) {for (auto it : _items) {it-format(os, msg);}return os;}FormatItem::ptr createItem(const std::string fc, const std::string subfmt) {if (fc m) return FormatItem::ptr(new MsgFormatItem(subfmt));if (fc p) return FormatItem::ptr(new LevelFormatItem(subfmt));if (fc c) return FormatItem::ptr(new NameFormatItem(subfmt));if (fc t) return FormatItem::ptr(new ThreadFormatItem(subfmt));if (fc n) return FormatItem::ptr(new NLineFormatItem(subfmt));if (fc d) return FormatItem::ptr(new TimeFormatItem(subfmt));if (fc f) return FormatItem::ptr(new CFileFormatItem(subfmt));if (fc l) return FormatItem::ptr(new CLineFormatItem(subfmt));if (fc T) return FormatItem::ptr(new TabFormatItem(subfmt));return FormatItem::ptr();}//pattern解析bool parsePattern() {//std::string _pattern sg{}fsg%d{%H:%M:%S}%Tsdf%t%T[%p]%T[%c]%T%f:%l%T%m%n;//std::cout _pattern std::endl;//每个要素分为三部分// 格式化字符 : %d %T %p...// 对应的输出⼦格式 {%H:%M:%S}// 对应数据的类型 0-表⽰原始字符串也就是⾮格式化字符1-表⽰格式化数据 类型// 默认格式 %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%nstd::vectorstd::tuplestd::string, std::string, int arry;std::string format_key;//存放%后的格式化字符std::string format_val;//存放格式化字符后边 {} 中的⼦格式字符串std::string string_row;//存放原始的⾮格式化字符bool sub_format_error false;int pos 0;while (pos _pattern.size()) {if (_pattern[pos] ! %) {string_row.append(1, _pattern[pos]);continue;}if (pos1 _pattern.size() _pattern[pos1] %) {string_row.append(1, %);pos 2;continue;}if (string_row.empty() false) {arry.push_back(std::make_tuple(string_row, , 0));string_row.clear();}//当前位置是%字符位置pos 1;//pos指向格式化字符位置if (pos _pattern.size() isalpha(_pattern[pos])) {format_key _pattern[pos];//保存格式化字符}else {std::cout _pattern[pos-1] 位置附近格式错误\n;return false;}//pos指向格式化字符的下⼀个位置判断是否包含有⼦格式 %d{%Y-%m-%d}pos 1;if (pos _pattern.size() _pattern[pos] {) {sub_format_error true;pos 1;//pos指向花括号下⼀个字符处while(pos _pattern.size()) {if (_pattern[pos] }) {sub_format_error false;pos 1;//让pos指向}的下⼀个字符处break;}format_val.append(1, _pattern[pos]);}}arry.push_back(std::make_tuple(format_key, format_val, 1));format_key.clear();format_val.clear();}if (sub_format_error) {std::cout {}对应出错\n;return false;}if (string_row.empty() false) arry.push_back(std::make_tuple(string_row, , 0));if (format_key.empty() false) arry.push_back(std::make_tuple(format_key, format_val, 1));for (auto it : arry) {if (std::get2(it) 0) {FormatItem::ptr fi(new OtherFormatItem(std::get0(it)));_items.push_back(fi);}else {FormatItem::ptr fi createItem(std::get0(it), std::get1(it));if (fi.get() nullptr) {std::cout 没有对应的格式化字符: %std::get0(it) std::endl;return false;}_items.push_back(fi);}}return true;}private:std::string _pattern;std::vectorFormatItem::ptr _items; }; } #endif 4.⽇志落地(LogSink)类设计 ⽇志落地类主要负责落地⽇志消息到⽬的地。 它主要包括以下内容 • Formatter⽇志格式化器主要是负责格式化⽇志消息 • mutex互斥锁保证多线程⽇志落地过程中的线程安全避免出现交叉输出的情况。 这个类⽀持可扩展其成员函数log设置为纯虚函数当我们需要增加⼀个log输出⽬标 可以增加⼀个类继承⾃该类并重写log⽅法实现具体的落地⽇志逻辑。 ⽬前实现了三个不同⽅向上的⽇志落地 • 标准输出StdoutSink • 固定⽂件FileSink • 滚动⽂件RollSink ◦ 滚动⽇志⽂件输出的必要性 ▪ 由于机器磁盘空间有限 我们不可能⼀直⽆限地向⼀个⽂件中增加数据   如果⼀个⽇志⽂件体积太⼤⼀⽅⾯是不好打开另⼀⽅⾯是即时打开了由于包含数据巨 ⼤也不利于查找我们需要的信息 ▪ 所以实际开发中会对单个⽇志⽂件的⼤⼩也会做⼀些控制即当⼤⼩超过某个⼤⼩时如 1GB我们就重新创建⼀个新的⽇志⽂件来滚动写⽇志。 对于那些过期的⽇志 ⼤部分 企业内部都有专⻔的运维⼈员去定时清理过期的⽇志或者设置系统定时任务定时清理过 期⽇志。 ◦ ⽇志⽂件的滚动思想 ⽇志⽂件滚动的条件有两个:⽂件⼤⼩ 和 时间。我们可以选择 ▪ ⽇志⽂件在⼤于 1GB 的时候会更换新的⽂件 ▪ 每天定点滚动⼀个⽇志⽂件 本项⽬基于⽂件⼤⼩的判断滚动⽣成新的⽂件 #ifndef __M_SINK_H__ #define __M_SINK_H__ #include util.hpp #include message.hpp #include formatter.hpp #include memory #include mutex namespace bitlog{ class LogSink {public:using ptr std::shared_ptrLogSink;LogSink() {}virtual ~LogSink() {}virtual void log(const char *data, size_t len) 0; }; class StdoutSink : public LogSink {public:using ptr std::shared_ptrStdoutSink;StdoutSink() default;void log(const char *data, size_t len) {std::cout.write(data, len);} }; class FileSink : public LogSink {public:using ptr std::shared_ptrFileSink;FileSink(const std::string filename):_filename(filename) {util::file::create_directory(util::file::path(filename));_ofs.open(_filename, std::ios::binary | std::ios::app);assert(_ofs.is_open());}const std::string file() {return _filename; }void log(const char *data, size_t len) {_ofs.write((const char*)data, len);if (_ofs.good() false) {std::cout ⽇志输出⽂件失败\n;}}private:std::string _filename;std::ofstream _ofs; }; class RollSink : public LogSink {public:using ptr std::shared_ptrRollSink;RollSink(const std::string basename, size_t max_fsize):_basename(basename), _max_fsize(max_fsize), _cur_fsize(0){util::file::create_directory(util::file::path(basename));}void log(const char *data, size_t len) {initLogFile();_ofs.write(data, len);if (_ofs.good() false) {std::cout ⽇志输出⽂件失败\n;}_cur_fsize len;}private:void initLogFile() {if (_ofs.is_open() false || _cur_fsize _max_fsize) {_ofs.close();std::string name createFilename();_ofs.open(name, std::ios::binary | std::ios::app);assert(_ofs.is_open());_cur_fsize 0;return;}return;} std::string createFilename() {time_t t time(NULL);struct tm lt;localtime_r(t, lt);std::stringstream ss;ss _basename;ss lt.tm_year 1900;ss lt.tm_mon 1;ss lt.tm_mday;ss lt.tm_hour;ss lt.tm_min;ss lt.tm_sec;ss .log;return ss.str();}private:std::string _basename;std::ofstream _ofs;size_t _max_fsize;size_t _cur_fsize; }; class SinkFactory {public:templatetypename SinkType, typename ...Argsstatic LogSink::ptr create(Args ...args) {return std::make_sharedSinkType(std::forwardArgs(args)...);} }; } #endif 5.⽇志器类(Logger)设计建造者模式 ⽇志器主要是⽤来和前端交互 当我们需要使⽤⽇志系统打印log的时候 只需要创建Logger对象 调⽤该对象debug、info、warn、error、fatal等⽅法输出⾃⼰想打印的⽇志即可⽀持解析可变参数 列表和输出格式 即可以做到像使⽤printf函数⼀样打印⽇志。 当前⽇志系统⽀持同步⽇志 异步⽇志两种模式两个不同的⽇志器唯⼀不同的地⽅在于他们在⽇志 的落地⽅式上有所不同 同步⽇志器直接对⽇志消息进⾏输出。 异步⽇志器将⽇志消息放⼊缓冲区由异步线程进⾏输出。 因此⽇志器类在设计的时候先设计出⼀个Logger基类在Logger基类的基础上继承出SyncLogger同 步⽇志器和AsyncLogger异步⽇志器。 且因为⽇志器模块是对前边多个模块的整合想要创建⼀个⽇志器需要设置⽇志器名称设置⽇志输出等级设置⽇志器类型设置⽇志输出格式设置落地⽅向且落地⽅向有可能存在多个整个⽇志器的创建过程较为复杂为了保持良好的代码⻛格编写出优雅的代码因此⽇志器的创建这⾥采⽤了建造者模式来进⾏创建。 #ifndef __M_LOG_H__ #define __M_LOG_H__ #include util.hpp #include level.hpp #include message.hpp #include formatter.hpp #include sink.hpp #include looper.hpp #include vector #include list #include atomic #include unordered_map #include cstdarg #include type_traits namespace bitlog{ class Logger {public:enum class Type {LOGGER_SYNC 0,LOGGER_ASYNC};using ptr std::shared_ptrLogger; Logger(const std::string name, Formatter::ptr formatter,std::vectorLogSink::ptr sinks, LogLevel::value level LogLevel::value::DEBUG): _name(name), _level(level), _formatter(formatter),_sinks(sinks.begin(), sinks.end()){}std::string loggerName() { return _name; }LogLevel::value loggerLevel() { return _level; }void debug(const char *file, size_t line, const char *fmt, ...) {if (shouldLog(LogLevel::value::DEBUG) false) {return ;}va_list al;va_start(al, fmt);log(LogLevel::value::DEBUG, file, line, fmt, al);va_end(al);}void info(const char *file, size_t line, const char *fmt, ...) {if (shouldLog(LogLevel::value::INFO) false) return ;va_list al;va_start(al, fmt);log(LogLevel::value::INFO, file, line, fmt, al);va_end(al);}void warn(const char *file, size_t line, const char *fmt, ...) {if (shouldLog(LogLevel::value::WARN) false) return ;va_list al;va_start(al, fmt);log(LogLevel::value::WARN, file, line, fmt, al);va_end(al);}void error(const char *file, size_t line, const char *fmt, ...) {if (shouldLog(LogLevel::value::ERROR) false) return ;va_list al;va_start(al, fmt);log(LogLevel::value::ERROR, file, line, fmt, al);va_end(al);}void fatal(const char *file, size_t line, const char *fmt, ...) {if (shouldLog(LogLevel::value::FATAL) false) return ;va_list al;va_start(al, fmt);log(LogLevel::value::FATAL, file, line, fmt, al);va_end(al);}public:class Builder {public:using ptr std::shared_ptrBuilder;Builder():_level(LogLevel::value::DEBUG), _logger_type(Logger::Type::LOGGER_SYNC) {}void buildLoggerName(const std::string name) { _logger_name name; }void buildLoggerLevel(LogLevel::value level) { _level level; }void buildLoggerType(Logger::Type type) { _logger_type type; }void buildFormatter(const std::string pattern) { _formatter std::make_sharedFormatter(pattern); }void buildFormatter(const Formatter::ptr formatter) { _formatter formatter; }templatetypename SinkType, typename ...Argsvoid buildSink(Args ...args) { auto sink SinkFactory::createSinkType (std::forwardArgs(args)...);_sinks.push_back(sink); }virtual Logger::ptr build() 0;protected:Logger::Type _logger_type;std::string _logger_name;LogLevel::value _level;Formatter::ptr _formatter;std::vectorLogSink::ptr _sinks;};protected:bool shouldLog(LogLevel::value level) { return level _level; }void log(LogLevel::value level,const char *file, size_t line, const char *fmt, va_list al) {char *buf;std::string msg;int len vasprintf(buf, fmt, al);if (len 0) {msg 格式化⽇志消息失败;}else {msg.assign(buf, len);free(buf);}//LogMsg(name, file, line, payload, level)LogMsg lm(_name, file, line, std::move(msg), level);std::stringstream ss;_formatter-format(ss, lm);logIt(std::move(ss.str()));}virtual void logIt(const std::string msg) 0;protected:std::mutex _mutex;std::string _name;Formatter::ptr _formatter;std::atomicLogLevel::value _level;std::vectorLogSink::ptr _sinks; }; class SyncLogger : public Logger {public:using ptr std::shared_ptrSyncLogger;SyncLogger(const std::string name, Formatter::ptr formatter,std::vectorLogSink::ptr sinks, LogLevel::value level LogLevel::value::DEBUG): Logger(name, formatter, sinks, level){ std::cout LogLevel::toString(level) 同步⽇志器: name 创 建成功...\n;}private:virtual void logIt(const std::string msg) {std::unique_lockstd::mutex lock(_mutex);if (_sinks.empty()) { return ; }for (auto it : _sinks) {it-log(msg.c_str(), msg.size());}} }; class LocalLoggerBuilder: public Logger::Builder {public:virtual Logger::ptr build() {if (_logger_name.empty()) {std::cout ⽇志器名称不能为空;abort();}if (_formatter.get() nullptr) {std::cout当前⽇志器 _logger_name;std::cout 未检测到⽇志格式,默认设置为: ;std::cout %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n\n;_formatter std::make_sharedFormatter();}if (_sinks.empty()) {std::cout当前⽇志器_logger_name 未检测到落地⽅向默认为 标准输出!\n;_sinks.push_back(std::make_sharedStdoutSink());}Logger::ptr lp;if (_logger_type Logger::Type::LOGGER_ASYNC) {lp std::make_sharedAsyncLogger(_logger_name,_formatter, _sinks, _level);}else {lp std::make_sharedSyncLogger(_logger_name, _formatter, _sinks, _level);}return lp;} }; } #endif 6.双缓冲区异步任务处理器AsyncLooper设计 设计思想异步处理线程 数据池 使⽤者将需要完成的任务添加到任务池中由异步线程来完成任务的实际执⾏操作。 任务池的设计思想双缓冲区阻塞数据池 优势避免了空间的频繁申请释放且尽可能的减少了⽣产者与消费者之间锁冲突的概率提⾼了任务处理效率。 在任务池的设计中有很多备选⽅案⽐如循环队列等等但是不管是哪⼀种都会涉及到锁冲突的情况因为在⽣产者与消费者模型中任何两个⻆⾊之间都具有互斥关系因此每⼀次的任务添加与取出都有可能涉及锁的冲突⽽双缓冲区不同双缓冲区是处理器将⼀个缓冲区中的任务全部处理完毕后然后交换两个缓冲区重新对新的缓冲区中的任务进⾏处理虽然同时多线程写⼊也会冲突但是冲突并不会像每次只处理⼀条的时候频繁减少了⽣产者与消费者之间的锁冲突且不涉及到空间的频繁申请释放所带来的消耗。 #include iostream #include string #include vector #include thread #include mutex #include atomic #include condition_variable #include functional #include cassert namespace bitlog{ #define BUFFER_DEFAULT_SIZE (1*1024*1024) #define BUFFER_INCREMENT_SIZE (1*1024*1024) #define BUFFER_THRESHOLD_SIZE (10*1024*1024) class Buffer {public:Buffer(): _reader_idx(0), _writer_idx(0), _v(BUFFER_DEFAULT_SIZE){}bool empty() { return _reader_idx _writer_idx; }size_t readAbleSize() { return _writer_idx - _reader_idx; }size_t writeAbleSize() { return _v.size() - _writer_idx; }void reset() { _reader_idx _writer_idx 0; }void swap(Buffer buf) {_v.swap(buf._v);std::swap(_reader_idx, buf._reader_idx);std::swap(_writer_idx, buf._writer_idx);}void push(const char *data, size_t len) { assert(len writeAbleSize());ensureEnoughSpace(len);std::copy(data, datalen, _v[_writer_idx]);_writer_idx len;}const char*begin() { return _v[_reader_idx]; }void pop(size_t len) { _reader_idx len; assert(_reader_idx _writer_idx);}protected:void ensureEnoughSpace(size_t len) {if (len writeAbleSize()) return;/*每次增⼤1M⼤⼩*/size_t new_capacity;if (_v.size() BUFFER_THRESHOLD_SIZE)new_capacity _v.size() * 2 len;elsenew_capacity _v.size() BUFFER_INCREMENT_SIZE len;_v.resize(new_capacity);}private:size_t _reader_idx;size_t _writer_idx;std::vectorchar _v; }; } #ifndef __M_LOOP_H__ #define __M_LOOP_H__ #include util.hpp #include vector #include thread #include mutex #include atomic #include condition_variable #include functional #include buffer.hpp namespace bitlog{class AsyncLooper {public:using Functor std::functionvoid(Buffer buffer);using ptr std::shared_ptrAsyncLooper;AsyncLooper(const Functor cb): _running(true), _looper_callback(cb),_thread(std::thread(AsyncLooper::worker_loop, this)) {}~AsyncLooper() { stop(); }void stop(){ _running false; _pop_cond.notify_all();_thread.join();}void push(const std::string msg){if (_running false) return;{std::unique_lockstd::mutex lock(_mutex);_push_cond.wait(lock, []{ return _tasks_push.writeAbleSize() msg.size(); });_tasks_push.push(msg.c_str(), msg.size());}_pop_cond.notify_all();}private:void worker_loop(){while(1){{std::unique_lockstd::mutex lock(_mutex);if (_running false _tasks_push.empty()) { return; }_pop_cond.wait(lock,[]{ return !_tasks_push.empty()||!_running; });_tasks_push.swap(_tasks_pop);}_push_cond.notify_all();_looper_callback(_tasks_pop);_tasks_pop.reset();}return;}private:Functor _looper_callback;private:std::mutex _mutex;std::atomicbool _running;std::condition_variable _push_cond;std::condition_variable _pop_cond;Buffer _tasks_push;Buffer _tasks_pop;std::thread _thread;}; } #endif 7.⽇志宏全局接⼝设计代理模式 提供全局的⽇志器获取接⼝。 使⽤代理模式通过全局函数或宏函数来代理Logger类的log、debug、info、warn、error、fatal等接 ⼝以便于控制源码⽂件名称和⾏号的输出控制简化⽤⼾操作。 当仅需标准输出⽇志的时候可以通过主⽇志器来打印⽇志。 且操作时只需要通过宏函数直接进⾏输出 即可。 #ifndef __M_BIT_H__ #define __M_BIT_H__ #include logger.hpp namespace bitlog {Logger::ptr getLogger(const std::string name) {return loggerManager::getInstance().getLogger(name);}Logger::ptr rootLogger() {return loggerManager::getInstance().rootLogger();}#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define info(fmt, ...) info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define warn(fmt, ...) warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define LOG_DEBUG(logger, fmt, ...) (logger)-debug(fmt, ##__VA_ARGS__)#define LOG_INFO(logger, fmt, ...) (logger)-info(fmt, ##__VA_ARGS__)#define LOG_WARN(logger, fmt, ...) (logger)-warn(fmt, ##__VA_ARGS__)#define LOG_ERROR(logger, fmt, ...) (logger)-error(fmt, ##__VA_ARGS__)#define LOG_FATAL(logger, fmt, ...) (logger)-fatal(fmt, ##__VA_ARGS__)#define LOGD(fmt, ...) LOG_DEBUG(bitlog::rootLogger(), fmt, ##__VA_ARGS__)#define LOGI(fmt, ...) LOG_INFO(bitlog::rootLogger(), fmt, ##__VA_ARGS__)#define LOGW(fmt, ...) LOG_WARN(bitlog::rootLogger(), fmt, ##__VA_ARGS__)#define LOGE(fmt, ...) LOG_ERROR(bitlog::rootLogger(), fmt, ##__VA_ARGS__)#define LOGF(fmt, ...) LOG_FATAL(bitlog::rootLogger(), fmt, ##__VA_ARGS__) }#endif 8.性能测试 下⾯对⽇志系统做⼀个性能测试测试⼀下平均每秒能打印多少条⽇志消息到⽂件。 主要的测试⽅法是每秒能打印⽇志数 打印⽇志条数 / 总的打印⽇志消耗时间 主要测试要素同步/异步 单线程/多线程 • 100w条指定⻓度的⽇志输出所耗时间 • 每秒可以输出多少条⽇志 • 每秒可以输出多少MB⽇志 测试环境 • CPUAMD Ryzen 7 5800H with Radeon Graphics 3.20 GHz • RAM16G DDR4 3200 • ROM512G-SSD • OSubuntu-22.04TLS虚拟机2CPU核⼼/4G内存 #ifndef __M_BENCH_H__ #define __M_BENCH_H__ #include bitlog.h #include chrono namespace bitlog { void bench(const std::string loger_name, size_t thread_num, size_t msglen, size_t msg_count) {Logger::ptr lp getLogger(loger_name);if (lp.get() nullptr) return;std::string msg(msglen, 1);size_t msg_count_per_thread msg_count / thread_num;std::vectordouble cost_time(thread_num);std::vectorstd::thread threads;std::cout 输⼊线程数量: thread_num std::endl;std::cout 输出⽇志数量: msg_count std::endl;std::cout 输出⽇志⼤⼩: msglen * msg_count / 1024 KB std::endl;for (int i 0; i thread_num; i) {threads.emplace_back([, i](){auto start std::chrono::high_resolution_clock::now();for(size_t j 0; j msg_count_per_thread; j) {lp-fatal(%s, msg.c_str());}auto end std::chrono::high_resolution_clock::now();auto coststd::chrono::duration_caststd::chrono::durationdouble (end-start);cost_time[i] cost.count();auto avg msg_count_per_thread / cost_time[i];std::cout 线程 i 耗时: cost.count() s;std::cout 平均 (size_t)avg /s\n;});}for(auto thr : threads) {thr.join();}double max_cost 0;for (auto cost : cost_time) max_cost max_cost cost ? cost : max_cost;std::cout 总消耗时间: max_cost std::endl;std::cout 平均每秒输出: (size_t)(msg_count / max_cost) std::endl; } } #endif #include bitlog.h #include bench.h #include unistd.h void sync_bench_thread_log(size_t thread_count, size_t msg_count, size_t msglen) {static int num 1;std::string logger_name sync_bench_logger std::to_string(num);LOGI(************************************************);LOGI(同步⽇志测试: %d threads, %d messages, thread_count, msg_count);bitlog::GlobalLoggerBuilder::ptr lbp(new bitlog::GlobalLoggerBuilder);lbp-buildLoggerName(logger_name);lbp-buildFormatter(%m%n);lbp-buildSinkbitlog::FileSink(./logs/sync.log);lbp-buildLoggerType(bitlog::Logger::Type::LOGGER_SYNC);lbp-build(); bitlog::bench(logger_name, thread_count, msglen, msg_count);LOGI(************************************************); } void async_bench_thread_log(size_t thread_count, size_t msg_count, size_t msglen) {static int num 1;std::string logger_name async_bench_logger std::to_string(num);LOGI(************************************************);LOGI(异步⽇志测试: %d threads, %d messages, thread_count, msg_count);bitlog::GlobalLoggerBuilder::ptr lbp(new bitlog::GlobalLoggerBuilder);lbp-buildLoggerName(logger_name);lbp-buildFormatter(%m);lbp-buildSinkbitlog::FileSink(./logs/async.log);lbp-buildLoggerType(bitlog::Logger::Type::LOGGER_ASYNC);lbp-build(); bitlog::bench(logger_name, thread_count, msglen, msg_count);LOGI(************************************************); } void bench_test() {// 同步写⽇志sync_bench_thread_log(1, 1000000, 100);sync_bench_thread_log(5, 1000000, 100);/*异步⽇志输出为了避免因为等待落地影响时间所以⽇志数量降低为⼩于缓冲区⼤⼩进⾏测试*/async_bench_thread_log(1, 100000, 100);async_bench_thread_log(5, 100000, 100); } int main(int argc, char *argv[]) {bench_test();return 0; } 在单线程情况下异步效率看起来还没有同步⾼这个我们得了解现在的IO操作在⽤⼾态都会有缓冲区进⾏缓冲区因此我们当前测试⽤例看起来的同步其实⼤多时候也是在操作内存只有在缓冲区 满了才会涉及到阻塞写磁盘操作⽽异步单线程效率看起来低也有⼀个很重要的原因就是单线程同步操作中不存在锁冲突⽽单线程异步⽇志操作存在⼤量的锁冲突因此性能也会有⼀定的降低。 但是我们也要看到限制同步⽇志效率的最⼤原因是磁盘性能打⽇志的线程多少并⽆明显区别线 程多了反⽽会降低因为增加了磁盘的读写争抢⽽对于异步⽇志的限制并⾮磁盘的性能⽽是cpu的处理性能打⽇志并不会因为落地⽽阻塞因此在多线程打⽇志的情况下性能有了显著的提⾼。 9.扩展 • 丰富sink类型 ◦ ⽀持按⼩时按天滚动⽂件 ◦ ⽀持将log通过⽹络传输落地到⽇志服务器(tcp/udp) ◦ ⽀持在控制台通过⽇志等级渲染不同颜⾊输出⽅便定位 ◦ ⽀持落地⽇志到数据库 ◦ ⽀持配置服务器地址将⽇志落地到远程服务器 • 实现⽇志服务器负责存储⽇志并提供检索、分析、展⽰等功能 10.参考资料 https://www.imangodoc.com/174918.html https://blog.csdn.net/w1014074794/article/details/125074038 https://zhuanlan.zhihu.com/p/472569975 https://zhuanlan.zhihu.com/p/460476053 https://gitee.com/davidditao/DDlog https://www.cnblogs.com/ailumiyana/p/9519614.html https://gitee.com/lqk1949/plog/ https://www.cnblogs.com/horacle/p/15494358.html https://blog.csdn.net/qq_29220369/article/details/127314390
http://www.zqtcl.cn/news/885925/

相关文章:

  • 网站注册需要多少钱wordpress缓存失败
  • 西安h5响应式网站施工企业安全生产管理规范最新版
  • 电商平台网站建设如何安装网站模版
  • wordpress攻击跳转seo营销软件
  • 广东中山市做网站python做的网站如何部署
  • VPS做镜像网站wordpress 安装七牛
  • 雄安做网站优化的公司小程序开发公司哪里强
  • 做的网站没有注册国家建设部网站倪虹
  • 中英文网站怎么实现做网站有名的公司
  • 先网站开发后软件开发显示网站运行时间代码
  • 品牌网站制作流程图百度网页版入口页
  • 哪些人需要做网站网站开发工程师 招聘
  • 东莞网站建设多长时间如何将网址提交到一些权重比较高的网站
  • 阳江网站seo公司wordpress建站博客
  • 我想做京东网站淘宝怎么做的wordpress淘宝联盟转链
  • 虚拟钱包对接网站开发视频教程营销型网站建设要懂代码吗
  • 莱州教育网站一站式网站搭建
  • 开发网站开票名称是什么捕鱼游戏网站开发商
  • 我国中小企业网站建设怎样办自己的网站
  • 如何推广自己网站链接通化北京网站建设
  • 小型的游戏网站怎么做WordPress设置作者信息
  • 网站建设师要求关键词优化排名易下拉排名
  • 网站建设步骤及推广方法做网站的公司叫什么
  • 怎么建立自己网站 asp网站做视频流量赚钱
  • 全屏网站宽度域名服务器怎么设置
  • 网站图片切换js代码金融公司网站方案
  • 企业网站开发步骤开源软件开发
  • 建设项目环境影响登记表备案系统网站签署网站建设协议新闻
  • 有的网站在浏览器打不开怎么办最近中国新闻热点大事件
  • 网站模板组件随州网站建设有哪些