日志系统模块基础、C语言实现一个日志模块、zlog日志模块基础_c日志记录

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

下面我演示一下在我的Ubuntu云服务器上的使用方法。

  1. 按照上面的要求新建代码文件和目录
    root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# tree
    .
    ├── log.c
    ├── log.conf
    ├── log.h
    ├── temp
    └── test.c

  2. 编译
    root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# gcc log.c test.c -o out

  3. 运行
    root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# ./out

  4. 可打开temp目录下的日志文件看看
    root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# tree
    .
    ├── log.c
    ├── log.conf
    ├── log.h
    ├── out
    ├── temp
    │ └── 2021-12-13.log
    └── test.c

  5. temp目录下的2021-12-13.log文件既是我们写入的日志文件
    文件中的内容如下:
    [INFO] [2021-12-13 21:18:25] Hello World!
    [DEBUG] [2021-12-13 21:18:25] H.e.l.l.o W.o.r.l.d!
    [WARN] [2021-12-13 21:18:25] H e l l o W o r l d!
    [ERROR] [2021-12-13 21:18:25] Hallo World!
    [INFO] [2021-12-13 21:18:59] Hello World!
    可以看到记录了日志等级,日期时间,打印信息。

可以看到这个日志系统还是很小巧易用的,适合与在有文件系统的类Unix系统中。

2.C语言日志系统2

下面这个日志系统是打印出来的:C语言日志分级设计

/****************************************************************
***Author: lishuangliang ***
***Email: lishuangliang@outlook.com ***
***Date: 2018-09-24 ***
***Festivsl: Mid-autumn ***
*****************************************************************/
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdbool.h>
#include <sys/un.h>
#include <sys/param.h>
#include <time.h>

//通过宏来控制是否打开日志输出
#ifdef DISABLE_DEBUG
#define real_debug_level 0
#else
#define real_debug_level debug_level
#endif

//定义日志输出级别
#define FATALEER (1<<0)
#define ERROR (1<<1)
#define WRAN (1<<2)
#define INFO (1<<3)
#define DEBUG (1<<4)

#define WriteLog(level,mesg) log_mesg_printf(__FILE__, __LINE__, __func__, level, mesg)
#define WriteLog2(level,format, arg…) log_mesg_printf2( __FILE__,__FUNCTION__, __LINE__, level, format, ##arg)

int debug_level = 0;

struct dbg {
int level;
const char *mesg;
};

static struct dbg debug_level_table[] = {
{FATALEER, “Config The Log Level as FATALEER”},
{ERROR, “Config The Log Level as ERROR”},
{WRAN, “Config The Log Level as WRAN”},
{INFO, “Config The Log Level as INFO”},
{DEBUG, “Config The Log Level as DEBUG”}
};

void print_debug_usage(void)
{
struct dbg *p;

fprintf(stderr,
" To calculate the debug level, logically ‘or’\n"
" some of the following values together to get a debug level:\n");
for (p = debug_level_table;
p <
debug_level_table +
(sizeof (debug_level_table) / sizeof (struct dbg)); p++) {
fprintf(stderr, “\t%d:\t%s\n”, p->level, p->mesg);
}
fprintf(stderr, “\n”);
}

void parse_debug(char *foo)
{
int i;
struct dbg *p;

if (!foo)
return;
fprintf(stderr, “Before parse_debug, debug_level is: %d\n”,
debug_level);

i = atoi(foo);
if(i == -1)
{
/* error */
fprintf(stderr, “Invalid level specified.\n”);
exit(0);
}
for (p = debug_level_table;p < debug_level_table +(sizeof (debug_level_table) / sizeof (struct dbg)); p++)
{
if (i > 0) {
if (i & p->level) {
fprintf(stderr, “Enabling %s debug level.\n”,p->mesg);
debug_level |= p->level;
}
}
}
fprintf(stderr, “After parse_debug, debug_level is: %d\n”,
debug_level);
}

char *get_commonlog_time(void)
{
char *p;
char sys_time[64];
time_t tnow = time(NULL);
struct tm *ptm = localtime(&tnow);

memset(sys_time, 0 ,sizeof(sys_time));
sprintf(sys_time, “%04d-%02d-%02d %02d:%02d:%02d”,ptm->tm_year+1900 ,ptm->tm_mon+1 ,ptm->tm_mday ,ptm->tm_hour ,ptm->tm_min ,ptm->tm_sec);
//return (char *)sys_time;
p = sys_time;
return p;
}

void log_mesg_printf(const char *file, int line, const char *func,int level, const char *mesg)
{
if(real_debug_level & level)
{
int errno_save = errno;
fprintf(stderr, "%s%s:%d (%s) - ", get_commonlog_time(), file, line, func);
errno = errno_save;
perror(mesg);
errno = errno_save;
}
}

void log_mesg_printf2(const char *file,const char *func,const int line, int level, char *fmt,…)
{
if(real_debug_level & level)
{
char msg_buf[20*1024];
va_list ap;
va_start(ap,fmt);
sprintf(msg_buf,“[%s %s:%s:%d] “,get_commonlog_time(),file,func,line);
vsprintf(msg_buf+strlen(msg_buf),fmt,ap);
fprintf(stderr,”%s\n”,msg_buf);
va_end(ap);
}
}

int main(int argc, char* argv[])
{

#ifdef DISABLE_DEBUG
print_debug_usage();
parse_debug(argv[1]);//解析日志打印输出级别
#endif
//不使用可变参数解析样例
WriteLog(DEBUG,“I want to DEBUG”);
WriteLog(INFO,“I want to INFO”);
WriteLog(WRAN,“I want to WARN”);
WriteLog(ERROR,“I want to ERROR”);
WriteLog(FATALEER,“I want to FATALEER”);
//使用可变参数解析样例
WriteLog2(DEBUG,“I want to %s which level is %d”,“DEBUG”,DEBUG);
WriteLog2(INFO,“I want to %s which level is %d”,“INFO”,INFO);
WriteLog2(WRAN,“I want to %s which level is %d”,“WRAN”,WRAN);
WriteLog2(ERROR,“I want to %s which level is %d”,“ERROR”,ERROR);
WriteLog2(FATALEER,“I want to %s which level is %d”,“FATALEER”,FATALEER);

return 0;
}

3.zlog日志系统
Chapter 1 zlog是什么?

zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库。

事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的轮子,但没办法通过配置改变日志的格式或者输出文件。syslog是个系统级别的轮子,不过速度慢,而且功能比较单调。

所以我写了zlog。

zlog在效率、功能、安全性上大大超过了log4c,并且是用c写成的,具有比较好的通用性。

zlog有这些特性:

  • syslog分类模型,比log4j模型更加直接了当
  • 日志格式定制,类似于log4j的pattern layout
  • 多种输出,包括动态文件、静态文件、stdout、stderr、syslog、用户自定义输出函数
  • 运行时手动、自动刷新配置文件(同时保证安全)
  • 高性能,在我的笔记本上达到25万条日志每秒, 大概是syslog(3)配合rsyslogd的1000倍速度
  • 用户自定义等级
  • 多线程和多进程环境下保证安全转档
  • 精确到微秒
  • 简单调用包装dzlog(一个程序默认只用一个分类)
  • MDC,线程键-值对的表,可以扩展用户自定义的字段
  • 自诊断,可以在运行时输出zlog自己的日志和配置状态
  • 不依赖其他库,只要是个POSIX系统就成(当然还要一个C99兼容的vsnprintf)

相关链接:

主页:http://hardysimpson.github.com/zlog/

下载:https://github.com/HardySimpson/zlog/releases

其实之前我已经写过一篇博客来讲解zlog的简单使用:【C语言开源库】C语言开源库zlog的使用

Chapter 2 zlog不是什么?

zlog的目标是成为一个简而精的日志函数库,不会直接支持网络输出或者写入数据库,不会直接支持日志内容的过滤和解析。

原因很明显,日志库是被应用程序调用的,所有花在日志库上的时间都是应用程序运行时间的一部分,而上面说的这些操作都很费时间,会拖慢应用程序的速度。这些事儿应该在别的进程或者别的机器上做。

如果你需要这些特性,我建议使用rsyslog、zLogFabric、Logstash,这些日志搜集、过滤、存储软件,当然这是单独的进程,不是应用程序的一部分。

目前zlog已经支持7.4,可以自己实现一个输出函数,自由的把日志输出到其他进程或者其他机器。而把日志的分类匹配、日志格式成型的工作交给zlog。

目前我的想法是实现一个zlog-redis客户端,用自定义输出功能,把日志存储到本机或者远程的redis服务器内,然后用其他进程(也使用zlog库)来把日志写到文件里面,不知大家以为这个想法如何?欢迎和我联系探讨。

Chapter 3 Hello World
3.1 编译和安装zlog

下载[zlog-latest-stable.tar.gz](file://https://github.com/HardySimpson/zlog/archive/latest-stable.tar.gz)

$ tar -zxvf zlog-latest-stable.tar.gz
$ cd zlog-latest-stable/

$ make

$ sudo make install

or

$ sudo make PREFIX=/usr/local/ install

PREFIX指明了安装的路径,安转完之后为了让你的程序能找到zlog动态库

$ sudo vi /etc/ld.so.conf
/usr/local/lib

$ sudo ldconfig

在你的程序运行之前,保证libzlog.so在系统的动态链接库加载器可以找到的目录下。上面的命令适用于linux,别的系统自己想办法。

  • 除了一般的make以外,还可以

$ make 32bit # 32bit version on 64bit machine, libc6-dev-i386 is needed
$ make noopt # without gcc optimization

$ make doc # lyx and hevea is needed

$ make test # test code, which is also good example for zlog

  • makefile是用GNU make的格式写的,所以在你的平台上需要预装gnu make和gcc。或者,手工修改一个自己平台的makefile也行。
3.2 应用程序调用和链接zlog

应用程序使用zlog很简单,只要在C文件里面加一行。

#include "zlog.h"

链接zlog需要pthread库,命令是:

$ cc -c -o app.o app.c -I/usr/local/include

-I[where zlog.h is put]

$ cc -o app app.o -L/usr/local/lib -lzlog -lpthread

-L[where libzlog.so is put]

3.3 Hello World 代码

这些代码在$(top_builddir)/test/test_hello.c, test_hello.conf

  1. 写一个C文件:

#include <stdio.h>

#include “zlog.h”

int main(int argc, char** argv)

{

int rc;
zlog_category_t *c;

rc = zlog_init(“test_hello.conf”);

if (rc) {

printf(“init failed\n”);
return -1;

}

c = zlog_get_category(“my_cat”);

if (!c) {

printf(“get cat fail\n”);

zlog_fini();
return -2;

}

zlog_info(c, “hello, zlog”);

zlog_fini();

return 0;

}

  1. 写一个配置文件,放在和test_hello.c同样的目录下:

[formats]

simple = “%m%n”

[rules]

my_cat.DEBUG >stdout; simple

  1. 编译、然后运行!

$ cc -c -o test_hello.o test_hello.c -I/usr/local/include
$ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog

$ ./test_hello

hello, zlog

3.4 更简单的Hello World

这个例子在$(top_builddir)/test/test_default.c, test_default.conf. 源代码是:

#include <stdio.h>
#include “zlog.h”

int main(int argc, char** argv)

{

int rc;
rc = dzlog_init(“test_default.conf”, “my_cat”);

if (rc) {

printf(“init failed\n”);
return -1;

}

dzlog_info(“hello, zlog”);

zlog_fini();

return 0;

}

配置文件是test_default.conf,和test_hello.conf一模一样,最后执行程序的输出也一样。区别在于这里用了dzlog API,内含一个默认的zlog_category_t。详见6.5

Chapter 4 Syslog 模型
4.1 分类(Category)、规则(Rule)和格式(Format)

zlog有3个重要的概念:分类(Category)、规则(Rule)和格式(Format)。

分类(Category)用于区分不同的输入。代码中的分类变量的名字是一个字符串,在一个程序里面可以通过获取不同的分类名的category用来后面输出不同分类的日志,用于不同的目的。

格式(Format)是用来描述输出日志的格式,比如是否有带有时间戳,是否包含文件位置信息等,上面的例子里面的格式simple就是简单的用户输入的信息+换行符。

规则(Rule)则是把分类、级别、输出文件、格式组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出。

所以,当程序执行下面的语句的时候

zlog_category_t *c;
c = zlog_get_category(“my_cat”);

zlog_info(c, “hello, zlog”);

zlog会找到c的名字是"my_cat",对应的配置文件中的规则是

[rules]
my_cat.DEBUG >stdout; simple

然后库会检查,目前这条日志的级别是否符合规则中的级别来决定是否输出。因为INFO>=DEBUG,所以这条日志会被输出。并且根据这条规则,会被输出到stdout(标准输出) ,输出的格式是simple,在配置文件中定义是

[formats]
simple = “%m%n”

最后在屏幕上打印

hello, zlog

这就是整个过程。用户要做就是写自己的信息。日志往哪里输出,以什么格式输出,都是库和配置文件来完成的。

4.2 syslog模型和log4j模型的区别

好,那么目前这个模型和syslog有什么关系呢?至今为止,这个模型还是比较像log4j。log4j的模型里面有logger, appender和layout。区别在于,在log4j里面,代码中的logger和配置中的logger是一一对应的,并且一个logger有唯一的级别。一对一关系是log4j, log4cxx, log4cpp, log4cplus, log4net的唯一选择。

但这种模型是不灵活的,他们发明了过滤器(filters)来弥补,但这只能把事情弄得更加混乱。所以让我们把目光转回syslog的模型,这是一个设计的很简易正确的模型。

继续上一节的例子,如果在zlog的配置文件中有这么2行规则:

[rules]
my_cat.DEBUG >stdout; simple

my_cat.INFO >stdout;

然后,一行代码会产生两行输出:

hello, zlog
2012-05-29 10:41:36 INFO [11288:test_hello.c:41] hello, zlog

现在一个代码中的分类对应配置文件中的两条规则。log4j的用户可能会说:"这很好,但是只要在log4j里面放两个appender也能做的一样。"所以继续看下一个例子:

[rules]
my_cat.WARN “/var/log/aa.log”

my_cat.DEBUG “/var/log/bb.log”

代码是:

zlog_info(c, “info, zlog”);
zlog_debug(c, “debug, zlog”);

最后,在aa.log中只有一条日志

2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog

但在bb.log里面有两条

2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog
2012-05-29 10:41:36 DEBUG [11288:test_hello.c:42] debug, zlog

从这个例子能看出来区别。log4j无法轻易的做到这一点。在zlog里面,一个分类可以对应多个规则,每个规则有自己的级别、输出和格式。这就让用户能按照需求过滤、多渠道输出自己的所有日志。

Chapter 5 配置文件

大部分的zlog的行为取决于配置文件:把日志打到哪里去,用什么格式,怎么转档。配置文件是zlog的黑话,我尽量把这个黑话设计的简单明了。这是个配置文件例子:

comments

[global]

strict init = true

buffer min = 1024

buffer max = 2MB

rotate lock file = /tmp/zlog.lock

default format = “%d.%us %-6V (%c:%F:%L) - %m%n”

file perms = 600

[levels]

TRACE = 10

CRIT = 130, LOG_CRIT

[formats]

simple = “%m%n”

normal = “%d %m%n”

[rules]

default.* >stdout; simple

*.* “%12.2E(HOME)/log/%c.log”, 1MB*12; simple

my_.INFO >stderr;

my_cat.!ERROR “/var/log/aa.log”

my_dog.=DEBUG >syslog, LOG_LOCAL0; simple

my_mice.* $user_define;

有关单位:当设置内存大小或者大数字时,可以设置1k 5GB 4M这样的单位:

1k => 1000 bytes

1kb => 1024 bytes

1m => 1000000 bytes

1mb => 1024*1024 bytes

1g => 1000000000 bytes

1gb => 1024*1024*1024 byte

单位是大小写不敏感的,所以1GB 1Gb 1gB是等效的。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
og/%c.log", 1MB*12; simple

my_.INFO >stderr;

my_cat.!ERROR “/var/log/aa.log”

my_dog.=DEBUG >syslog, LOG_LOCAL0; simple

my_mice.* $user_define;

有关单位:当设置内存大小或者大数字时,可以设置1k 5GB 4M这样的单位:

1k => 1000 bytes

1kb => 1024 bytes

1m => 1000000 bytes

1mb => 1024*1024 bytes

1g => 1000000000 bytes

1gb => 1024*1024*1024 byte

单位是大小写不敏感的,所以1GB 1Gb 1gB是等效的。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-XTNBRFUI-1713563875654)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值