写一个Windows上的守护进程(4)日志其余

这次把和日志相关的其他东西一并说了。

一、vaformat

C++日志接口通常有两种形式:流输入形式,printf形式。

我采用printf形式,因为流输入不好控制格式。

printf形式要求日志接口支持不定长参数,我没有直接在日志实现类里边支持不定长参数,而是只接受一个字符串参数,可以参见第一篇。

为什么呢?

如果要成为不定长参数,就是这样

bool log_string(const LOG_LEVEL level, const char* file, const int line, const char *s, ...);

那么在每一个log_xxx的变体里就都要写_vsnprintf_s那一套代码了,而且是完全一样的(我不知道__VA_ARGS__宏是否可以传递),这显然是不好的做法。

我把不定长参数的处理放在了宏定义里,类似:

#define ErrorLog(s, ...) _Log(LOG_ERROR, __FILE__, __LINE__, vaformat(MAX_LOG_BUFFER, s, __VA_ARGS__))

vaformat就是处理不定长参数的:

std::string vaformat(const size_t max_size, const char* msg, ...);
std::wstring vaformat(const size_t max_size, const wchar_t* wmsg, ...);

因为并不知道格式化后有多长,所以要指定最大长度,如果格式化后的长度大于最大长度,则截断。vaformat里还有一个小技巧,当指定的max_size小于1024的时候,使用栈空间,否则申请堆内存,这是从std::string的实现中学来的——SSO短字符串优化。

可以看下vaformat的实现,两个版本的代码基本一样,这样当然是不好的,但是我不知道怎样把他们合并起来,这是一个todo。类似的问题下面还有。

二、CLastErrorFormat

这个东西是用来解决第一篇里提到的记录LastErrorCode的问题的。

它的主要功能就是把error code转换成文本描述,合适的构造也可以省去GetLastError的调用:

class CLastErrorFormat : public boost::noncopyable
{
public:CLastErrorFormat(): m_code(GetLastError()){}CLastErrorFormat(const DWORD code): m_code(code){}~CLastErrorFormat(){}public:const DWORD code() const{return m_code;}const std::string& str(){//...
    }const std::wstring& wstr(){//...
    }private://...
};

日志实现类里对应的接口:

bool log_last_error(const LOG_LEVEL level, const char* file, const int line, CLastErrorFormat& e, const std::string& prefix);

接受一个CLastErrorFormat的引用,然后在记录日志的时候,把error code和其对应的描述也记录下来:xxx, error code: 999, error msg: yyy

最终的日志接口是有两个版本的:一个接受一个CLastErrorFormat参数;另一个省去,在函数内部自己构造。

三、str_encode

我不可能在日志文件里一会记宽字符串,一会记窄字符串,那就没法看了,又考虑到日志文件的大小,我最终决定,按照窄字符串SystemCurrentCodePage(在简体中文版的Windows上,就是GB2312)编码记录日志,所以对于宽字符串我还要转换成窄字符串。

Windows提供了两个API来做编码转换:MultiByteToWideChar和WideCharToMultiByte,而这两个API总是要两次调用才能安全的转换。我将其稍稍封装了一下,做成了两个函数:

std::wstring multistr2widestr(const unsigned int from_code_page, const std::string& s);
std::string widestr2multistr(const unsigned int to_code_page, const std::wstring& ws, const char *default_char = NULL);

注:SystemCurrentCodePage的代码页编码就是CP_ACP。

这样在记宽字符串的时候总是会慢一些,所以我代码中,能用窄字符串的地方我都用窄字符串了。

四、any_lexical_cast

代码中总是免不了要做类型转换,特别是把数字转换成字符串,为了简单一点,我使用了boost的lexical_cast,虽然大家都说这货效率低,因为使用了C++的流,但是我坚持“先正确,再优化”的原则,还是使用了它。

然而,这个东西使用起来有两处不便:

1. 转换失败的时候会抛异常

2. 把bool转换成string的时候是0或1,不是true或false

为了解决这两个问题,我又做了一下封装:

1. 转换失败的时候,填充为默认值。调用者必须提供默认值

2. 特化对于bool和string之间的转换

这就是any_lexical_cast:

template<typename Target, typename Source>
Target any_lexical_cast(const Source& src, const Target& fail_value)
{Target value = fail_value;try{value = boost::lexical_cast<Target>(src);}catch (boost::bad_lexical_cast&){value = fail_value;}return value;
}template<>
bool any_lexical_cast<bool, std::string>(const std::string& src, const bool& fail_value);template<>
bool any_lexical_cast<bool, std::wstring>(const std::wstring& src, const bool& fail_value);template<>
std::string any_lexical_cast<std::string, bool>(const bool& src, const std::string&);template<>
std::wstring any_lexical_cast<std::wstring, bool>(const bool& src, const std::wstring&);

具体实现请参看源码。

五、CSelfPath

日志初始化接口通常需要提供一个路径参数,以指定日志存放路径。我为其增加了一个默认路径:当传递空字符串时,将日志文件放在应用程序所在路径的log目录下,若log目录不存在,则先创建。

获取应用程序所在路径本可以放在日志模块内部,但考虑到别的地方可能也会用到,而且应用程序一旦启动,路径就不会变,所以就做成了一个单例类CSelfPath。

CSelfPath仅在构造函数中调用GetModuleFileNameA一次获取路径并分割成目录、文件名等等部分。

六、CLoggerImpl与Logger

日志的实现类里边有好多东西我都不想给调用者看到,典型如private的成员;还有日志实现类的接口并不易用。所以我在日志实现类和调用者之间又引入了一个间接层Logger,它的主要作用就是隐藏日志实现类和使接口更“亲民”。当然除了这个我还给了它一些别的功能:控制日志输出级别。Logger并不是一个类。

七、Disable 3rd party library warning

我在使用boost关于string的algorithm的时候,发现编译器会大段的警告,这来自boost库中对std::copy的使用,而我明确的知道boost库的这段代码是正确的。这些警告又多又烦人,有没有安全的办法消除这个警告?

肯定有了:

#pragma warning(push)
#pragma warning(disable:4996)
#include <boost/algorithm/string.hpp>
#pragma warning(pop)

上面的代码保存为一个头文件:boost_algorithm_string.h。以后要包含boost/algorithm/string.hpp时,均以boost_algorithm_string.h代替。

源码:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。

2015年11月1日星期日

转载于:https://www.cnblogs.com/mkdym/p/4927324.html

写一个Windows上的守护进程(4)日志其余相关推荐

  1. 写一个Windows上的守护进程(7)捕获异常并生成dump

    写一个Windows上的守护进程(7)捕获异常并生成dump 参考文章: (1)写一个Windows上的守护进程(7)捕获异常并生成dump (2)https://www.cnblogs.com/mk ...

  2. java 线程不足_jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储...

    jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储 我有一个Java应用程序,我从控制台运行,然后控制台执行另一个Java进程. 我想获得该子进程的线程/堆转储. 在Uni ...

  3. python定时开关机的代码_用python写一个windows下的定时关机脚本(推荐)

    由于本人经常使用笔记本共享WiFi,但是又不想笔记本开机一夜(为了低碳环保嘛 ~_~!),所以每次都要用使用DOS命令关机,感觉好麻烦.正好最近在学习Python,于是决定用python写一个定时关机 ...

  4. windows下创建守护进程A和B 互相监视 挂掉拉起

    在windows下创建守护进程A和B ,在其中一个挂掉以后,另一个会把挂掉的拉起来. 下面展示一些 内联代码片. 这里只列出了A的代码,B和A类似. #include<iostream> ...

  5. 一个短小精悍的监控/守护进程

    一个短小精悍的监控/守护进程 Author: 柳大·Poechant(钟超) Email: zhongchao.ustc#gmail.com Blog: Blog.CSDN.net/Poechant ...

  6. windows下的守护进程C++

    1 守护进程 1.1 需求分析 我有三个程序需要不断运行,有可能出现某些未知的原因而宕掉,需要本守护程序来进行守护,发现它运行不管,死掉就重启它,并且服务器开机就启动. 1.2 使用方法 将该程序与需 ...

  7. Windows上的单个进程所能访问的最大内存量是多少?它与系统的最大虚拟内存一样吗?这对于系统设计有什么影响?...

    Windows使用一个系统:虚拟寻址系统.该系统把程序可用的内存地址映射到硬件内存中的实际地址上,这些任务完全有Windows后台管理,其实际结果是32位处理器上的每个进程都可以使用4GB的内存--- ...

  8. javascript从0到0.9手写一个windows计算器

    说实话,最初想用javascript模拟着windows的计算器写一个的时候,感觉也就是10分钟搞定,但写着写着发现,其实并不是那么容易的事,window的这个计算器逻辑挺多的. 而且还想给别人把这个 ...

  9. 用python写一个windows下的定时关机脚本

    由于本人经常使用笔记本共享WiFi,但是又不想笔记本开机一夜(为了低碳环保嘛 ~_~!),所以每次都要用使用DOS命令关机,感觉好麻烦.正好最近在学习python,于是决定用python写一个定时关机 ...

最新文章

  1. php如何同设备连接不上,一个账号同时只能在同一个设备上登陆
  2. 数学笔记--线性代数
  3. boost::graph::isomorphism用法的测试程序
  4. 昇腾AI处理器软件栈--流程编排器(Matrix)
  5. oracle authentication_services,SQLNET.AUTHENTICATION_SERVICES= (NTS) 解释
  6. loadRunner之中文语言包安装
  7. 中标麒麟服务器系统安装教程,安装国产Linux中标麒麟操作系统教程
  8. CCAI 2019 | 俞扬:人工智能的决定权依然在人
  9. MathType如何编辑大三角形符号
  10. 宏批量替换多个word指定文字
  11. 前端性能优化--测试工具
  12. 宁向东认为的沟通分类
  13. dns网络服务器未响应是什么原因(如果各自方法都尝试后无法使用,请尝试重启猫)
  14. 务实java基础之集合总结
  15. Android录音下————AudioRecord源码分析
  16. 实验室信息化管理行业方案
  17. 2022年危险化学品经营单位主要负责人考试题模拟考试题库及答案
  18. 美拍运营技巧都有哪些 如何入门网络媒体行业
  19. 为什么我放弃了有道云笔记,选择了 Obsidian
  20. QMessageBox 换中文 确定和取消按钮

热门文章

  1. nginx main error_page
  2. CUDA TOOlkit Programming Guide 2. Programming Model
  3. 1.6 Dropout 正则化
  4. Linux 安装Eclipse
  5. 泰语7个元音变形_泰语发音规则
  6. python猜名词甲乙丙_用python实现了一下:甲乙两人互猜数字(数理逻辑)
  7. c语言输出教学日历表 节假日突出,[蓝桥杯][算法提高VIP]任意年月日历输出 (C语言代码)...
  8. 华为云优秀伙伴展示--2020-08-12
  9. 开环控制的两轮差速驱动小车_汽车的差速器、差速锁有什么不同?很多车主容易把它俩搞混...
  10. html绘制静态图表,怎样用JavaScript和HTML5 Canvas绘制图表