1自定义日志调试
1-1引入
#include <stdio.h>
int fact(int n){
int i,f=1;
for( i=1; i<=n; i++){
f += i;
}
return f;
}
int main(){
printf( "4!=%d\n", fact(4) );
return 0;
}
输出结果为:4!=11
与预期的结果不同,因此我们要对程序进行调试
我们在调试程序时,输出调试信息(又称为”打桩”或者”插桩”)是一种普遍、有效的方法。
我们输出的信息通常包括行号、函数名、程序变量等。
但是当我们调试结束之后,那些调试语句会影响程序输出时的美观和清晰,因此很多情况下我们都要手动把那些用来打印调试信息的语句删除或者注释掉。在大的项目中,这样做是很麻烦的。因此,我们要想办法来简化这些工作。
1-2传统手工日志:printf
也就是直接输出调试信息。
在上面的程序的函数fact中插入一条输出信息:
#include <stdio.h>
#include <stdio.h>
int fact(int n){
int i,f=1;
for( i=1; i<=n; i++){
f += i;
printf("i=%d ; f=%d\n", i, f);
}
return f;
}
输出的信息如下:
由此我们看出来是 f+=i这句话错了,应该改为 f*=i
改正之后再测试一下,就没啥问题了。
优点:简单,适合新手,通俗易懂
缺点:调试完之后要删除或注释掉调试的语句很麻烦,尤其是当项目庞大,调试增多时;无法方便地打印文件信息和行信息,并且不利于生成日志文件
1-3预处理封装printf
在每个调试语句用#ifdef
包含,代码如下:
#include <stdio.h>
#define DEBUG
int fact(int n){
int i,f=1;
for( i=1; i<=n; i++){
f += i;
#ifdef DEBUG
printf("i=%d ; f=%d\n", i, f);
#endif
}
return f;
}
int main(){
printf( "4!=%d\n", fact(4) );
return 0;
}
如果DEBUG这个宏有定义,那么就执行这个#ifdef里面的语句。如果这个宏没有定义,那就不执行这里面的语句。因此,当我们调试结束之后,只需要把最前面定义的#define DEBUG删掉就可以了,而不需要把每条打印语句全都删掉,这样就方便了许多。
并且,gcc为我们提供了一个编译选项,因此用不着#define DEBUG这条定义语句。只要在gcc编译的时候加上-DDEBUG,注意有两个D。它就会自动帮我们定义这个DEBUG 。省略#define DEBUG
情况下。如下:
加了-DDEBUG:
不加-DDEBUG:
优点:方便简单不复杂
缺点:每条调试语句都被预处理指令包含,造成代码膨胀;无法方便地打印文件信息和行信息,并且不利于生成日志文件
1-4自定义调试日志打印函数
1-4-1简易函数
我们可以用自己的函数来实现更加智能的日志打印的函数,在前面添加代码如下:
#include<stdio.h>
#ifdef DEBUG
#include <stdarg.h>
logd(const char *format, ...) {
va_list argPtr;
int count;
va_start(argPtr, format); /* 获取可变参数列表 */
fflush(stdout); /* 强制刷新输出缓冲区 */
count = vfprintf(stderr, format, argPtr); /* 将信息输出到标准出错流设备 */
va_end(argPtr); /* 可变参数列表结束 */
}
#else
int logd(const char *format, ...) {
}
#endif
int fact(int n) {
int i,f=1;
for( i=1; i<=n; i++) {
f += i;
logd("i=%d ; f=%d\n", i, f);
}
return f;
}
int main() {
printf( "4!=%d\n", fact(4) );
return 0;
}
这样就可以通过资格自定义的打印函数来打印调试日志。仍然是在gcc编译时添加-DDEBUG来控制是否打印日志。如下:
1-4-2添加打印信息
但是,这样写,好像还缺点什么。如果能在打印的时候加上一些信息就好了,比如文件信息,函数信息,以及行的信息。因此,这里就用到了C语言库中的 FILE,LINE,FUNCTION 这三个宏定义,分别代表的就是当前的文件名file,行数line,函数名function。我们使用宏定义的方法把这三个加进去,再将logd函数的参数增加这三个,就可以打印这些信息了。代码如下:
#include<stdio.h>
#ifdef DEBUG
#include <stdarg.h>
logd(const char* filename, int lines, const char* functions,const char*format, ...) {
printf("调试日志 --- 在文件: %s 中, 在第 %d 行, 在函数 %s中 ---",filename,lines,functions);
va_list argPtr;
int count;
va_start(argPtr, format); /* 获取可变参数列表 */
fflush(stdout); /* 强制刷新输出缓冲区 */
count = vfprintf(stderr, format, argPtr); /* 将信息输出到标准出错流设备 */
va_end(argPtr); /* 可变参数列表结束 */
}
#else
int logd(const char *format, ...) {
}
#endif
#define LOGD(FORMAT,...) logd(__FILE__,__LINE__,__FUNCTION__,FORMAT,##__VA_ARGS__)
int fact(int n) {
int i,f=1;
for( i=1; i<=n; i++) {
f += i;
LOGD("i=%d ; f=%d\n", i, f);
}
return f;
}
int main() {
printf( "4!=%d\n", fact(4) );
return 0;
}
仍然是用gcc中的-DDEBUG控制是否打印日志。打印如下:
优点:日志信息清晰明了
缺点:无法将日志保存成文件
1-4-3写到日志文件中去
因此,我们再进行微调,把打印的信息写入到一个当前目录下名为 “log.txt” 的文件当中
修改代码如下:
#include <stdio.h>
#ifdef DEBUG
#include <stdarg.h>
int logd(const char* filename, int lines, const char* functions,const char*format, ...) {
FILE*pFile = fopen("log.txt", "a+");
fprintf(pFile,"调试日志 --- 在文件: %s 中, 在第 %d 行, 在函数 %s中 ---",filename,lines,functions);
va_list argPtr;
int count;
va_start(argPtr, format); /* 获取可变参数列表 */
fflush(stdout); /* 强制刷新输出缓冲区 */
count = vfprintf(pFile, format, argPtr); /* 将信息输出到标准出错流设备 */
va_end(argPtr); /* 可变参数列表结束 */
}
#else
int logd(const char *format, ...) {
}
#endif
#define LOGD(FORMAT,...) logd(__FILE__,__LINE__,__FUNCTION__,FORMAT,##__VA_ARGS__)
int fact(int n) {
int i,f=1;
for( i=1; i<=n; i++) {
f += i;
LOGD("i=%d ; f=%d\n", i, f);
}
return f;
}
int main() {
printf( "4!=%d\n", fact(4) );
return 0;
}
效果如下:
打开log.txt看如下:
这样自定义的调试日志函数就完成了,只需要一句:LOGD(“xxx”,xxx,xxx,……); 就可以打印日志到文件中了
2,第三方日志库的使用
2-1一个小的第三方日志函数库
2-1-1安装
首先把两个文件复制过来:
然后在自己写的c文件中引入这两个,用include,像这样:
#include "log.h"
#include "log.c"
然后,就可以使用这样的语句打印日志:
LOG_PRINT(“xxx”,xxx,……);
例如刚才的fact.c这个求阶乘的文件:
#include <stdio.h>
#include "log.h"
#include "log.c"
int fact(int n) {
int i,f=1;
for( i=1; i<=n; i++) {
f += i;
LOG_PRINT("i=%d ; f=%d\n", i, f);
}
return f;
}
int main() {
printf( "4!=%d\n", fact(4) );
return 0;
}
就是这样使用LOG_PRINT();
就可以打印出很多的信息
参考:
[1] C语言 日志输出 测试运行时间(Windows、Linux平台)https://blog.csdn.net/u014644466/article/details/80311948
[2] C语言输出DEBUG调试信息的方法https://blog.csdn.net/zh1204190329/article/details/78594461
[3] C语言中几种输出调试信息的方法https://blog.csdn.net/thinkerABC/article/details/615378?depth_1-
以上内容转载于https://blog.csdn.net/weixin_42534168/article/details/115699275
关于zlog库的快速使用教程
tao@ubuntu:~/study/test$ git clone https://github.com/HardySimpson/zlog.git
正克隆到 'zlog'...
remote: Enumerating objects: 3264, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3264 (delta 0), reused 0 (delta 0), pack-reused 3261
接收对象中: 100% (3264/3264), 3.16 MiB | 4.76 MiB/s, 完成.
处理 delta 中: 100% (2329/2329), 完成.
切换到下载的zlog目录
tao@ubuntu:~/study/test$ ls
zlog
tao@ubuntu:~/study/test$ cd zlog/
tao@ubuntu:~/study/test/zlog$ ls
Changelog COPYING doc INSTALL makefile README.md src test TODO tools
安装到系统
$ make
$ sudo make install
使用方法:代码+配置文件
因为打印可以配置格式,索引zlog库的使用就需要一个zlogname.conf的配置文件,在配置文件里面可以指定打印格式,等级,等等。。。
myzlog.conf配置文件介绍
由于配置文件格式比较丰富,条件也很多,所以,我在这里只提供一个最简单,最实用的配置文件可直接使用,想要详细了解可在最下面配置文件格式详细介绍。
myzlog.conf文件是将打印日志输出到mytest.log文件中,可在mytest.conf文件查看日志。
打印等级
#默认打印等级: FATAL > ERROR > WARN > NOTICE > INFO > DEBUG
常用myzlog.conf配置文件内容
[global]
strict init = true
buffer min = 1024
buffer max = 2MB
rotate lock file = /tmp/myzlog.lock
default format = "%d.%us [%-6V] [%p:%F:%L] - %m%n"
file perms = 600
#默认打印等级: FATAL > ERROR > WARN > NOTICE > INFO > DEBUG
#只有当打印等级大于等于DEBUG时才会打印输出
#定义的myrule_class将会在函数dzlog_init("myzlog.conf", "myrule_class")中使用
[rules]
myrule_class.DEBUG "mytest.log",10kb * 3 ~ "mytest.txt.#r"; #输出到mytest.log文件
# my_zlog.DEBUG >stdout; #打印输出到终端
测试代码
#include <stdio.h>
#include "zlog.h"
int main(void)
{
int ret;
int d=10;
char *s="world";
ret = dzlog_init("myzlog.conf", "myrule_class"); //指定配置文件路径及类型名 初始化zlog
if (ret)
{
printf("init failed\n");
return -1;
}
//打印等级数值越低,等级越高
dzlog_debug("debug"); //5
dzlog_info("info, %d %s",d,s); //4
dzlog_notice("notice"); //3
dzlog_warn("warn"); //2
dzlog_error("error"); //1
dzlog_fatal("fatal"); //0
zlog_fini(); //释放zlog
return 0;
}
编译
gcc main.c -lzlog