我的开发组长曾经说过这么一段话“一个优秀的程序员不在于他写代码有多快,也不在于他能不能实现这个模块的功能,要实现业务实现功能谁不会啊,重要的是他的解决能力,也就说当程序出现错误时你能不能够快速定位到错误并解决它。”

 
是的,我也非常赞同,代码不可能完美,也可能有千奇百怪的bug,尤其是新手,犯的错误就更多了,所以,我们写程序时应有自己的一套debug手段,有一套自己的LOG的方法,一旦程序发生错误,我们不需要再往程序中加繁琐的打印就可以定位到错误位置,这样才能加快自己的开发速度。
 
那怎么才算有一套高效的debug手段呢?我的想法是这样:
  • 进程内开一个独立线程用于Debug&Info Center,在这里我们可以看到所有的打印信息,便于追踪各程序动向
  • 标准化一套日志/打印的手段,不要再使用简陋的printf,在适当的位置加适当的log
今天我们先来完成自定义一套属于我们LOG的规定。
 
一、信息类别与等级
我们知道,程序内可能出现各种异常,有的异常很严重,一不注意就发生coredump;有的异常只是可能对程序的运行产生影响,但不至于挂掉;有的异常是隐蔽的,虽说现在没对系统产生明显影响,但是不加处理也终究是个隐患。如果我们对这些异常都使用printf语句的话,我们就区分不了哪些异常重要哪些不重要了,所以我们首先得给信息分类别评等级(severity)。我的划分是这样的:
  1. fatal           致命错误
  2. alarm         需要立即纠正的错误
  3. error          需要关注的错误
  4. warning      警告,可能存在某种差错
  5. info            一般提示信息
  6. debug        调试信息

代码定义可以这么写:

#define FATAL      1
#define ALARM 2
#define ERROR 3
#define WARN 4
#define INFO 5
#define DEBUG 6
二、log的设计
根据我们上面规定的打印等级,我们很容易设计出相应的debug log,闲话少说,先上代码:
#define MY_LOG(level, fmt, args...)  do{  \
if(BIT_ON(debug_flag,level)){ \
printf("[%s]:", __FUNCTION__); \
printf(fmt, ##args); \
} \
}while()

当然我们还需要定义一套设置debug level的方法,我的思路是这样的:

  • 采用bit-map思想,定义一个unsigned int的数,这个数的而每一位表示一个级别,比如一个unsigned int的数就可以表示32个级别
  • 定义相应的函数/宏定义,去设置相应的位

根据以上想法,可以设置出下面一系列的操作:

#define PRESENT_BIT32(x)        (((uint32)((uint32)1<<(x))))
#define BIT_ON32(m, b) (((m) & PRESENT_BIT32(b)) != 0)
#define SET_BIT32(m, b) ((m) |= PRESENT_BIT32(b))
#define CLEAR_BIT32(m, b) ((m) &= ~PRESENT_BIT32(b))

解释:

  • PRESENT_BIT32(x)    :对应level的位的位置
  • BIT_ON32(m, b)      :判定某一位是否为1
  • SET_BIT32(m, b)     :设置指定位为1
  • CLEAR_BIT32(m, b)   :将指定为设置为0

上面我们使用了宏定义来定义出带级别的LOG,那该怎么使用这些LOG呢?使用方法如下:

if(pthread_create(&thread2_id, NULL, (void*)msg_sender2, NULL))
{
MY_LOG(FATAL,"create handler thread fail!\n");
return -;
} MY_LOG(DEBUG,"I have recieved a message!\n");
MY_LOG(DEBUG,"msgtype:%d msg_src:%d dst:%d\n\n",msg->hdr.msg_type,msg->hdr.msg_src,msg->hdr.msg_dst);
 
 
当然使用前需要先打开相应的log开关,比如我想看debug的log,可以这么做:
SET_BIT(debug_flag, DEBUG);
 
这样子我们就将系统的debug级别定义为DEBUG了。
 
再说一个打印的小技巧:给你的打印上颜色!
 
printf("\033[46;31m[%s:%d]\033[0m "#fmt" errno=%d, %m\r\n", __func__, __LINE__, ##args, errno, errno);
 

上面printf时在Linux命令行下打印出带颜色的字体,方便一眼区分不同种类的调试信息,只需要加上一些颜色代码,例如:这里的46代表底色, 31代表字体的颜色。

使用ascii code 是对颜色调用的始末格式如下:

\033[ ; m …… \033[0m
 

后面哪个 ”\033[0m” 是对前面哪个颜色载入的结束,恢复到终端原来的背景色和字体色,可以把后面哪个修改成如下试试:

#define DEBUG_ERR(fmt, args...) printf("\033[46;31m[%s:%d]\033[40;37m "#fmt" errno=%d, %m\r\n", __func__, __LINE__, ##args, errno, errno);

下面列出 ascii code 的颜色值:

字背景颜色范围:40----49                           字颜色:30-----------39

40:黑                                                       30:黑

41:深红                                                    31:红

42:绿                                                       32:绿

43:黄色                                                    33:黄

44:蓝色                                                    34:蓝色

45:紫色                                                    35:紫色

46:深绿                                                    36:深绿

47:白色                                                    37:白色

记忆颜色格式太麻烦了,我们将它搞成宏定义吧,这样以后用起来就方便得多。

#define NONE                 "\e[0m"
#define BLACK "\e[0;30m"
#define L_BLACK "\e[1;30m"
#define RED "\e[0;31m"
#define L_RED "\e[1;31m"
#define GREEN "\e[0;32m"
#define L_GREEN "\e[1;32m"
#define BROWN "\e[0;33m"
#define YELLOW "\e[1;33m"
#define BLUE "\e[0;34m"
#define L_BLUE "\e[1;34m"
#define PURPLE "\e[0;35m"
#define L_PURPLE "\e[1;35m"
#define CYAN "\e[0;36m"
#define L_CYAN "\e[1;36m"
#define GRAY "\e[0;37m"
#define WHITE "\e[1;37m" #define BOLD "\e[1m"
#define UNDERLINE "\e[4m"
#define BLINK "\e[5m"
#define REVERSE "\e[7m"
#define HIDE "\e[8m"
#define CLEAR "\e[2J"
#define CLRLINE "\r\e[K" //or "\e[1K\r" #define DEBUG_ERROR(fmt, args...) do{ \
printf(RED"[%s]:"NONE, __FUNCTION__); \
printf(fmt, ##args); \
}while();
 
效果:
 
所以,我建议将fatal一类致命错误级别的log用高亮颜色标注,一旦有这类错误发生我们也能第一时间察觉。
 

Linux编程之定制带级别的log的更多相关文章

  1. Linux编程之给你的程序开后门

    这里说的"后门"并不是教你做坏事,而是让你做好事,搭建自己的调试工具更好地进行调试开发.我们都知道,当程序发生异常错误时,我们需要定位到错误,有时我们还想,我们在不修改程序的前提下 ...

  2. Linux Redis 安装(带视频)

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 疯狂创客圈 高并 ...

  3. Linux 编程中的API函数和系统调用的关系【转】

    转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...

  4. 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结 转载

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  5. 【转】牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  6. 面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

    基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...

  7. Linux编程return与exit区别

    Linux编程return与exit区别 exit  是用来结束一个程序的执行的,而return只是用来从一个函数中返回. return return 表示从被调函数返回到主调函数继续执行,返回时可附 ...

  8. linux编程学习

    linux编程学习 工具篇 “公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习 C 语言编程常常用到的软件和工具. (一)操作系 ...

  9. 【转】[IT综合面试]牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

    感谢IT面试群 S-北京-陈磊 的整理分享.   基础篇:操作系统.计算机网络.设计模式         提高篇:WIN32.MFC与Linux 算法篇:算法与数据结构           一:操作系 ...

随机推荐

  1. GPUImage 自定义滤镜

    GPUImage 自定义滤镜 GPUImage 是一个基于 GPU 图像和视频处理的开源 iOS 框架.由于使用 GPU 来处理图像和视频,所以速度非常快,它的作者 BradLarson 称在 iPh ...

  2. EasyUI篇の日期控件

    页面代码: <input type="text" id='astartTime' class="easyui-datebox" style="w ...

  3. BZOJ 1024 生日快乐

    Description windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕.现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋 ...

  4. jQuery Asynchronous

    http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html http:// ...

  5. activiti入门3排他网关,并行网管,包含网关,事件网关(转)

    网关用来控制流程的流向 网关可以消费也可以生成token. 网关显示成菱形图形,内部有有一个小图标. 图标表示网关的类型. 基本分支 首先 利用 流程变量  写个带有分支的一个基本流程 流程图: 部署 ...

  6. MPI Maelstrom(Dijkstra)

    http://poj.org/problem?id=1502 刷一道模板题稳定一下心情... Dijkstra求单源最短路,就是输入的时候注意下,是按下三角输入的(无向图),输入字符x表示i与j不通. ...

  7. linux(ubuntu)下分区和格式化sd卡

    我的手机sd卡需要分成两个分区,在windowxp下面死活搞不成.主要的问题是,window只认识sd卡的第一个分区.有人用修改驱动程序,让windows把sd卡认成日立的microdisk,分区和格 ...

  8. 动态规划(二维背包问题):UVAoj 473

     Raucous Rockers  You just inherited the rights to n previously unreleased songs recorded by the pop ...

  9. 综合查询员工和datetime.now和datetime.today区别

    一:综合查询图 二:EmployeeListWindow.cs代码 using System; using System.Collections.Generic; using System.Compo ...

  10. SharePoint Server 2007 Enterprise Key

    正式版 key SN: Tkjcb-3wkhk-2ty2t-qymk2-9xm2y 这个版本也是通过Key来区分是否是测试版还是正式版的 也就是说你输入正式版的Key他就是正式版,输入Enterpri ...