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

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

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

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

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

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

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

正文

int LogWrite(unsigned char loglevel,char *fromat,…);
#endif /* __LOG_H__ */

log.c 文件:

/** log.c **/

#include “log.h”
#define MAXLEVELNUM (3)

LOGSET logsetting;
LOG loging;

const static char LogLevelText[4][10]={“INFO”,“DEBUG”,“WARN”,“ERROR”};

static char * getdate(char *date);

static unsigned char getcode(char *path){
unsigned char code=255;
if(strcmp(“INFO”,path)==0)
code=1;
else if(strcmp(“WARN”,path)==0)
code=3;
else if(strcmp(“ERROR”,path)==0)
code=4;
else if(strcmp(“NONE”,path)==0)
code=0;
else if(strcmp(“DEBUG”,path)==0)
code=2;
return code;
}

static unsigned char ReadConfig(char *path){
char value[512]={0x0};
char data[50]={0x0};

FILE *fpath=fopen(path,“r”);
if(fpath==NULL)
return -1;
fscanf(fpath,“path=%s\n”,value);
getdate(data);
strcat(data,“.log”);
strcat(value,“/”);
strcat(value,data);
if(strcmp(value,logsetting.filepath)!=0)
memcpy(logsetting.filepath,value,strlen(value));
memset(value,0,sizeof(value));

fscanf(fpath,“level=%s\n”,value);
logsetting.loglevel=getcode(value);
fclose(fpath);
return 0;
}
/*
*日志设置信息
* */
static LOGSET *getlogset(){
char path[512]={0x0};
getcwd(path,sizeof(path));
strcat(path,“/log.conf”);
if(access(path,F_OK)==0){
if(ReadConfig(path)!=0){
logsetting.loglevel=INFO;
logsetting.maxfilelen=4096;
}
}else{
logsetting.loglevel=INFO;
logsetting.maxfilelen=4096;
}
return &logsetting;
}

/*
*获取日期
* */
static char * getdate(char *date){
time_t timer=time(NULL);
strftime(date,11,“%Y-%m-%d”,localtime(&timer));
return date;
}

/*
*获取时间
* */
static void settime(){
time_t timer=time(NULL);
strftime(loging.logtime,20,“%Y-%m-%d %H:%M:%S”,localtime(&timer));
}

/*
*不定参打印
* */
static void PrintfLog(char * fromat,va_list args){
int d;
char c,*s;
while(*fromat)
{
switch(*fromat){
case ‘s’:{
s = va_arg(args, char *);
fprintf(loging.logfile,“%s”,s);
break;}
case ‘d’:{
d = va_arg(args, int);
fprintf(loging.logfile,“%d”,d);
break;}
case ‘c’:{
c = (char)va_arg(args, int);
fprintf(loging.logfile,“%c”,c);
break;}
default:{
if(*fromat!=‘%’&&*fromat!=‘\n’)
fprintf(loging.logfile,“%c”,*fromat);
break;}
}
fromat++;
}
fprintf(loging.logfile,“%s”,“\n”);
}

static int initlog(unsigned char loglevel){
char strdate[30]={0x0};
LOGSET *logsetting;
//获取日志配置信息
if((logsetting=getlogset())==NULL){
perror(“Get Log Set Fail!”);
return -1;
}
if((loglevel&(logsetting->loglevel))!=loglevel)
return -1;

memset(&loging,0,sizeof(LOG));
//获取日志时间
settime();
if(strlen(logsetting->filepath)==0){
char *path=getenv(“HOME”);
memcpy(logsetting->filepath,path,strlen(path));

getdate(strdate);
strcat(strdate,“.log”);
strcat(logsetting->filepath,“/”);
strcat(logsetting->filepath,strdate);
}
memcpy(loging.filepath,logsetting->filepath,MAXFILEPATH);
//打开日志文件
if(loging.logfileNULL)
loging.logfile=fopen(loging.filepath,“a+”);
if(loging.logfile
NULL){
perror(“Open Log File Fail!”);
return -1;
}
//写入日志级别,日志时间
fprintf(loging.logfile,"[%s] [%s] ",LogLevelText[loglevel-1],loging.logtime);
return 0;
}

/*
*日志写入
* */
int LogWrite(unsigned char loglevel,char *fromat,…)
{
int rtv = -1;
va_list args;

//[为支持多线程需要加锁] pthread_mutex_lock(&mutex_log); //lock.

do{
//初始化日志
if(initlog(loglevel) != 0)
{
rtv = -1;
break;
}
//打印日志信息
va_start(args,fromat);
PrintfLog(fromat,args);
va_end(args);
//文件刷出
fflush(loging.logfile);
//日志关闭
if(loging.logfile!=NULL)
fclose(loging.logfile);
loging.logfile=NULL;
rtv = 0;
}while(0);

//[为支持多线程需要加锁] pthread_mutex_unlock(&mutex_log); //unlock.

return rtv;
}

test.c 文件:

/** test.c **/

#include “stdio.h”
#include “stdlib.h”
#include “log.h”
int main(int argv,char**argc){
printf(“%s\n”,argc[0]);
LogWrite(INFO,“%s”,“Hello World!”);
LogWrite(DEBUG,“%s”,“H.e.l.l.o W.o.r.l.d!”);
LogWrite(WARN,“%s”,“H e l l o W o r l d!”);
LogWrite(ERROR,“%s”,“Hallo World!”);
return 0;
}

path=./temp
level=ALL

该系统需要在类Unix系统下使用。

下面我演示一下在我的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();

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

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

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

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

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

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

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值