目录
一、描述
二、实现效果
三、使用案例
四、内存检测
一、描述
最近实现一个 WS 服务器,内部需要一个日志打印记录服务器程序的运行过程,故自己实现了一个轻量级的 logger,主要包含如下特征:
-
可输出 debug、info、warn、error、fatal 级别日志
-
每行日志包含时间戳、进程号、线程号、文件名、函数、行数、日志级别等重要信息
-
可按日志文件大小自动拆分日志文件(以 MB 为单位)
-
可限制日志文件数量(自动覆盖旧的日志文件)
-
日志文件命名可设置固定开头,以时间戳进行区分
-
线程安全,可多线程读写
-
通过宏参数控制日志行为
具体的代码已上传至 gitcode和github,链接如下:
项目首页 - GitCode
AtaoistPriest/c_logger: A Lightweight Logger Implemented with C (github.com)
二、实现效果
主要打印时间戳、进程号、线程号、文件名、函数、行数、日志级别和错误描述等重要信息。
May 21 2024 23:45:01 [1661][1663] [./test.c print_log:19] [ERROR] this an error thread 2 54246
May 21 2024 23:45:01 [1661][1663] [./test.c print_log:20] [ERROR] this an error thread 2 54246
May 21 2024 23:45:01 [1661][1663] [./test.c print_log:13] [INFO] this an info thread 2 54247
May 21 2024 23:45:01 [1661][1663] [./test.c print_log:14] [INFO] this an info thread 2 54247
May 21 2024 23:45:01 [1661][1663] [./test.c print_log:16] [WARN] this an warn thread 2 54247
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:13] [INFO] this an info thread 1 59045
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:14] [INFO] this an info thread 1 59045
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:16] [WARN] this an warn thread 1 59045
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:17] [WARN] this an warn thread 1 59045
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:19] [ERROR] this an error thread 1 59045
May 21 2024 23:45:01 [1661][1662] [./test.c print_log:20] [ERROR] this an error thread 1 59045
三、使用案例
#include <unistd.h>
#include <pthread.h>
#include "./logger.h"
void *print_log(void *arg)
{
char *str = (char *)arg;
for ( int i = 0; i < 100000; i++ )
{
logger_level_printf(LOGGER_DEBUG_LEVEL, "this an debug %s %d", str, 525 + i);
logger_level_printf(LOGGER_INFO_LEVEL, "this an info %s %d", str, 525 + i);
logger_level_printf(LOGGER_WARN_LEVEL, "this an warn %s %d", str, 525 + i);
logger_level_printf(LOGGER_ERROR_LEVEL, "this an error %s %d", str, 525 + i);
if ( i % 10000 == 0 )
{
sleep(1);
}
}
return NULL;
}
int main(void)
{
logger_init("./log");
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, print_log, "thread 1");
pthread_create(&tid2, NULL, print_log, "thread 2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
logger_destroy();
return 0;
}
四、内存检测
使用 valgrind 对该日志器进行了内存泄漏检测,检测结果如下,没有任何问题。
==14863== Memcheck, a memory error detector
==14863== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==14863== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==14863== Command: ./test
==14863== Parent PID: 10266
==14863==
==14863==
==14863== HEAP SUMMARY:
==14863== in use at exit: 0 bytes in 0 blocks
==14863== total heap usage: 27 allocs, 27 frees, 368,920 bytes allocated
==14863==
==14863== All heap blocks were freed -- no leaks are possible
==14863==
==14863== For lists of detected and suppressed errors, rerun with: -s
==14863== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
valgrind是一个开源的,检测内存泄漏的工具,通常在linux下使用,除此之外,他还能检测内存管理错误,线程bug等错误。valgrind相当于一个沙盒,指定的程序会在他模拟的cpu和内核中去运行。
粗浅的来讲,valgrind由两部分构成,一部分用来模拟cpu和内核,被称为framework(框架),一部分是他用来检测各种错误信息的插件工具。其中的插件包括:
memcheck:是valgrind最重要的工具之一,是一个重量级的内存检查器,它可以帮我们检测是否有使用未初始化的内存,以及内存泄漏,访问越界之类的问题。
callgrind:可以用来统计函数的互相调用情况。可以用来统计某个函数被调用了几次,以及他们的调用关系。
cachegrind:cache分析器,可以精准的指出cache的丢失与命中。还可以统计cache命中与丢失的次数,让我们了解我们程序堆栈的使用情况。
helgrind:主要用来检测线程资源的竞争问题,比如 POSIX pthreads API 的误用。由锁排序导致的死锁。以及数据的竞争问题等。
massif:堆栈分析器。它可以统计堆和栈使用的内存大小是多少。
extension:可以利用core的特性自己编辑的内存调试工具。
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --log-file=check.log ./host_monitor
在程序执行结束后,valgrind 将打印检测结果。
valgrind 将内存泄漏分为 4 类:并且默认情况下,只会打印明确泄漏 和可能泄漏,如果需要打印 间接泄漏,需要加上选项 --show-reachable=yes.
a. 明确泄漏(definitely lost):表示某段内存还没有被释放,但是已经没有指针指向它了,所以明确了这里一定发生了内存释放。
b. 间接泄漏(indirectly lost):某一段内存没有被释放,指向它的指针仍然存在,但是,存放这个指针的内存已经被释放了。
c. 可能泄漏(possibly lost):指针存在,但并不指向内存头地址,而是指向内存内部的位置。
d. 仍可访达(still reachable):指针一直存在并指向首地址,直至程序退出时内存还没被释放。例如指针 ptr=malloc() 了一段内存,直至程序结束,ptr仍然指向malloc分配的内存的首地址,并且该内存仍然没有没释放。