linux c日志系统设计,Linux C简单日志打印代码示例

背景

项目代码的打印函数,有的用printf,有的用std::cout,风格不统一,也不方便查看,因此需要编写一个统一的函数接口。

需求及实现时间戳

该打印函数需要有时间戳,精确到毫秒。这样能直观观察程序运行时间。获取时间使用localtime函数,毫秒的获取使用gettimeofday函数。获取时间戳函数get_timestamp没有使用静态局部变量,使用4线程测试,时间戳没有错误情况发生。

打印等级

函数需要有打印等级,目前分为ERROR、WARNING、INFO、DEBUG几种,等级越高,数字越小。在使用时需要根据实际情况,出错的情况使用ERROR。为代码简洁起见,使用gcc扩展语法。

代码如下:

1

2

3

4

5

6static const char* s_loginfo[] = {

[ERROR] = "ERROR",

[WARN] = "WARN",

[INFO] = "INFO",

[DEBUG] = "DEBUG",

};

这样通过s_loginfo数组即可获取等级对应的字符串。注意,这种语法只能在C语言中使用,C++代码编译报错。

附加信息

打印时,要附带文件名、行号信息(当然,也可打印函数名称),此举在于方便定位代码。由于Makefile可能不与实现文件在同一目录,因此,文件名可能会带有路径信息,需要用strrchr查找“/”,达到保留文件名的目的。详细参考代码。

检查参数

代码实现需要严谨,传入参数需要严格检查。这里再次使用gcc扩展语法:__attribute__((format(printf,N,M)));这样的好处是,能避免参数不一致。比如字符串使用%d打印时,会提示编译警告。

扩展(未完事宜)打印等级控制没有很好实现,只是在代码中固定定义一个宏,理论上应该在Makefile中定义,这样无须修改代码。另外,也可以通过配置文件的形式,在执行程序时手动配置等级。

没有添加syslog,因为考虑到docker部署,也考虑到调试权限问题,所以未添加。

处理不同的等级,如ERROR等级的,需要写入EEPROM或flash指定分区文件,以方便长久保存。

完整代码

头文件声明如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24#ifndef LOG_H_

#define LOG_H_

#ifdef __cplusplus

extern "C" {

#endif

enum LogLevel

{

ERROR = 1,

WARN = 2,

INFO = 3,

DEBUG = 4,

};

void mylog1(const char* filename, int line, enum LogLevel level, const char* fmt, ...) __attribute__((format(printf,4,5)));

#define mylog(level, format, ...) mylog1(__FILE__, __LINE__, level, format, ## __VA_ARGS__)

#ifdef __cplusplus

};

#endif

#endif

实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74/**

日志打印示例。

使用:

mylog(DEBUG, "This is debug info\n");

结果:

[2018-07-22 23:37:27:172] [DEBUG] [main.cpp:5] This is debug info

默认打印当前时间(精确到毫秒)、文件名称、行号。

*/

#include

#include

#include

#include

#include

#include "log.h"

#ifndef LOGLEVEL

#define LOGLEVEL DEBUG

#endif

// 使用了GNU C扩展语法,只在gcc(C语言)生效,

// g++的c++版本编译不通过

static const char* s_loginfo[] = {

[ERROR] = "ERROR",

[WARN] = "WARN",

[INFO] = "INFO",

[DEBUG] = "DEBUG",

};

static void get_timestamp(char *buffer)

{

time_t t;

struct tm *p;

struct timeval tv;

int len;

int millsec;

t = time(NULL);

p = localtime(&t);

gettimeofday(&tv, NULL);

millsec = (int)(tv.tv_usec / 1000);

/* 时间格式:[2011-11-15 12:47:34:888] */

len = snprintf(buffer, 32, "[%04d-%02d-%02d %02d:%02d:%02d:%03d] ",

p->tm_year+1900, p->tm_mon+1,

p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, millsec);

buffer[len] = '\0';

}

void mylog1(const char* filename, int line, enum LogLevel level, const char* fmt, ...)

{

if(level > LOGLEVEL)

return;

va_list arg_list;

char buf[1024];

memset(buf, 0, 1024);

va_start(arg_list, fmt);

vsnprintf(buf, 1024, fmt, arg_list);

char time[32] = {0};

// 去掉*可能*存在的目录路径,只保留文件名

const char* tmp = strrchr(filename, '/');

if (!tmp) tmp = filename;

else tmp++;

get_timestamp(time);

printf("%s[%s] [%s:%d] %s\n", time, s_loginfo[level], tmp, line, buf);

va_end(arg_list);

}

注:本文所示代码可随意使用。代码不是最终应用到实际项目版本。

李迟 XX

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值