如何提升网站速度/站长工具seo综合查询权重
后山coder:C++中级程序员教程 全集目录(必读)
背景介绍
日志库是生产环境的程序生成日志的工具。
生产环境是指脱离了调试环境,程序放到用户电脑上、手机上、硬件设备上运行对应的环境。
调试环境是开发阶段,程序员一边开发代码,一边调试查看自己的代码是否符合自己预期的环境。调试环境通常由IDE提供,比如Visual Studio,IntellJ,eclipse,MyEclipse等。
由于程序放到生产环境运行之后,摆脱了程序员的控制,现场发生了错误如果想解决,基本上是毫无办法。所以只能记录程序内存执行的关键路径,重要逻辑流程以及当时各种关键变量的值,将这些信息实时的写入文件里。这样的文件就叫做程序日志。
程序日志是程序员分析现场问题,解决现场问题最为重要的途径和手段。
一个好用的日志应该符合下面的这些最基本的需求。
需求
1 支持输出日志到文件,控制台;可以指定单独输出到文件,单独输出到控制台,也可以同时输出到文件和控制台。
2 输出日志到文件时,日志文件的文件名以及路径可以指定。
3 支持按级别输出日志;日志的优先级从低到高提供:“调试”,“信息”,“错误”,“致命”四个日志级别;当指定一个输出级别时,大于等于该级别的日志都可以输出。
4 支持日志输出格式定制;日志输出的内容由以下5中元素组成:日志发生的时间、日志级别、日志内容、生成日志的源代码全路径、生成日志的源代码行号。日志格式定制时可以指定这些元素是否显示在日志中,以及它们的位置顺序。
5 日志做到C++跨平台。
6 日志不阻塞当前输出日志的线程(可以先不支持多线程)。
7 提供测试代码。
效果展示
测试代码
#include <iostream>
#include "Log.h"void funLog(void)
{LOG_DEBUG("Hello funLog Debug!");LOG_INFO("Hello funLog Infor!");LOG_ERROR("Hello funLog Error!");LOG_FATAL("Hello funLog Fatal!");
}int main()
{try{LOG_INIT("log.txt", Log::LOG_LEVEL::DEBUG, Log::LOG_TARGET::ALL, "level_datetime_log_source_line");//LOG_INIT("log.txt", Log::LOG_LEVEL::DEBUG, Log::LOG_TARGET::ALL, "level_datetime_log_source");//LOG_INIT("log.txt", Log::LOG_LEVEL::DEBUG, Log::LOG_TARGET::ALL, "level_log_source_datetime");}catch (const std::exception& e){std::cout << e.what() << std::endl;return -1;}funLog();LOG_DEBUG("Hello main Debug!");LOG_INFO("Hello main Infor!");LOG_ERROR("Hello main Error!");LOG_FATAL("Hello main Fatal!");
}
测试输出:控制台

测试输出:文件

设计思路:
1 输出源文件全路径可以使用宏:__FILE__
2 输出源代码行号可以使用宏:__LINE__
3 上面多个需求实现并无顺序,根据自己的理解,由易到难顺序实现即可
4 由于日志初始化影响打印,为了将初始化信息和打印不分散开给用户使用带来记忆负担,考虑用单例实现。为了让程序退出时自己释放单例对象,考虑用静态单例实现日志对象。
5 为了最简化用户使用,考虑用宏定义来包装对日志的调用
#define LOG_DEBUG(str) Log::instance().debug(str, __FILE__, __LINE__)
难点1:
一条日志记录的内容由五部分组成。每一部分可以启用也可以不启用,而且顺序也可以自由调整。
思路:由于用if else枚举所有可能的组合几乎非常困难,所以考虑使用map来解析并存储用户配置的日志各部分,并同时存储日志各部分的优先级。
打印日志的时候,先把和部分内容得到;再按照优先级逐个检查该部分是否需要打印,最后拼接成一条完整的日志记录字符串交给打印模块去输出到目的地。
当前版本百度云下载:实现90%的功能,难点1之前的所有功能
链接:https://pan.baidu.com/s/1EXibuSmjOce4b5yoMqvD7g
提取码:1234
难点2:
日志支持流式操作,比如可以在一条日志里连续输出多个不同类型的变量:
LOG_DEBUG("Hello main Debug!"<<1<<" from int");
思路:只有流对象的输出操作符重载才可以使用 连续输出操作,类似 a<<"str"<<1;
所以,上面的宏定义应该被翻译成首先获取日志对象的流对象,然后用流对象接管连续输出的内容。其余部分不变即可。
关键代码:
#define LOG_DEBUG(str) Log::instance().getOss()<< str; Log::instance().log( __FILE__, __LINE__, Log::LOG_LEVEL::DEBUG)
//Log类的成员增加
std::ostringstream& getOss(void) { return m_oss; }
std::ostringstream m_oss;
宏定义的调整:原来的一条宏命令列变成两条语句执行,第一条语句把要输出的内容写到流对象;第二条语句获取流对象中被写入的内存输出到日志目的地,重置流对象。
与上一版本的区别:


当前版本百度云下载:
链接:https://pan.baidu.com/s/1Zzz4cf3NKzzsTmhGyTgQsw
提取码:1234
难点3:
支持多线程环境下的正常输出日志功能。
思路:解决多线程并发问题的方法总是对临界区增加保护,也就是并发访问控制,让同一时刻只有一个线程可以写入日志。
关键代码:讲要输出的日志内容先用流式输出过去到一个临时的string对象中,然后打印再打印这个string对象。
#define LOG_DEBUG(str1) Log::instance().log((std::ostringstream()<<str1).str(), __FILE__, __LINE__, Log::LOG_LEVEL::DEBUG)

临时的string对象可以保证多线程环境下是安全的,不会受多线程影响,反而是如果把流式对象设置成成员变量会产生并发场景。


下图为单线程环境下运行两个函数:
可以看到先输出 0 2 4 6 8 ,后输出 1 3 5 7 9 。

下图是支持多线程的情况下两个函数作为线程函数执行的情况(并发执行,顺序不再保证):

下图是去掉了锁之后,多线程不再安全,运行出问题的截图(其中一个日志的换行还没打印就被另一条日志插入进来部分日志导致格式乱了):

当前版本百度云下载:
链接:https://pan.baidu.com/s/1W5isTjHI18VwLhB4TEaqoQ
提取码:1234
说明:
1 以上内容就包括了所有的日志基础功能,很实用;
2 代码量包括测试代码一起260行左右;
3 代码规范,简洁明了。代码设计基本符合应届生可以接受的水平。
祝你好运!
后山coder:C++中级程序员教程 全集目录(必读)