对于C/C++项目比较大或模块比较多时,日志记录起来特别麻烦。将其整理为通用接口,后续开发比较方便。
头文件 dg_log.h 如下:
/******************************************************************************
Copyright (C), 2017-2018, xxx Co.xxx, Ltd.
******************************************************************************
File Name : dg_log.h
Version : V1.0
Author :
Created :
Description : debug log
1. DG_INIT --- Init
2. DG_SET_MODE --- Set the log mode
3. DG_SET_LEN --- Set the log file size
4. DG_LOG --- Print log
History :
******************************************************************************/
#ifndef _DG_LOG_H_
#define _DG_LOG_H_
#ifdef __cplusplus
extern "C" {
#endif
/* log mode */
enum {
DG_LOG_MODE_STDOUT = 1, // output to screen
DG_LOG_MODE_FILE = 2, // output to file
DG_LOG_MODE_DISABLE = 3, // close log
DG_LOG_MODE_MAX,
};
/********************************************************************/
// The following is the internal use interface
int dg_log_init(const char *name, const char *file);
void dg_log_set_mode(int num, int mode);
int dg_log_get_mode(int num);
void dg_log_set_size(int num, int size);
void dg_log_print(int num, const char *function,
unsigned int line, const char *format, ...);
/********************************************************************/
#define DG_TOSTRING(s) #s
/********************************************************************/
// eg:
// ......
// int log_num;
// log_num = DG_LOG_INIT(test_log_mod, "./test.log");
// ......
// DG_LOG(log_num, "Test debug_log_mod!");
// DG_LOG(log_num, "Test debug_log_mod id = %d", log_num);
// ......
/* init */
#define DG_LOG_INIT(_log_mod, _log_file) ({ \
int _log_num = dg_log_init(DG_TOSTRING(_log_mod), (_log_file)); \
(_log_num); \
})
/* set log mode */
#define DG_SET_MODE(_log_num, _log_mode) do { \
dg_log_set_mode((_log_num), (_log_mode)); \
} while (0)
/* set log file size */
#define DG_SET_SIZE(_log_num, _log_size) do { \
dg_log_set_size((_log_num), (_log_size)); \
} while (0)
/* get log mode */
#define DG_GET_MODE(_log_num) ({ \
int _mode = dg_log_get_mode((_log_num)); \
(_mode); \
})
/* print log */
#define DG_LOG(_log_num, ...) do { \
if (dg_log_get_mode((_log_num)) != DG_LOG_MODE_DISABLE) \
dg_log_print((_log_num), __FUNCTION__, __LINE__, __VA_ARGS__); \
} while (0)
#define DG_LOG0(flag, log_num, fmt) \
if (flag) \
DG_LOG(log_num, fmt)
#define DG_LOG1(flag, log_num, fmt, arg1) \
if (flag) \
DG_LOG(log_num, fmt, arg1)
#define DG_LOG2(flag, log_num, fmt, arg1, arg2) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2)
#define DG_LOG3(flag, log_num, fmt, arg1, arg2, arg3) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3)
#define DG_LOG4(flag, log_num, fmt, arg1, arg2, arg3, arg4) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4)
#define DG_LOG5(flag, log_num, fmt, arg1, arg2, arg3, arg4, arg5) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4, arg5)
#define DG_LOG6(flag, log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6)
#define DG_LOG7(flag, log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
#define DG_LOG8(flag, log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
#define DG_LOG9(flag, log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
if (flag) \
DG_LOG(log_num, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
/********************************************************************/
#ifdef __cplusplus
}
#endif
#endif
程序实现 dg_log.c 如下:
/******************************************************************************
Copyright (C), 2017-2018, xxx Co.xxx, Ltd.
******************************************************************************
File Name : dg_log.c
Version : V1.0
Author :
Created :
Description : debug log
History :
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/queue.h>
#include "dg_log.h"
#define DG_LOG_FILE_PATH 256
#define DG_LOG_ENTRY_MAX 512
struct dg_log_entry {
TAILQ_ENTRY(dg_log_entry) next;
int index;
int mode;
long size;
char name[DG_LOG_FILE_PATH];
char file[DG_LOG_FILE_PATH];
char n_file[DG_LOG_FILE_PATH];
};
TAILQ_HEAD(dg_log_head, dg_log_entry);
struct dg_log_free_entry {
TAILQ_ENTRY(dg_log_free_entry) next;
int index;
};
TAILQ_HEAD(dg_log_free_head, dg_log_free_entry);
/* Log queue header */
static struct dg_log_head g_dg_log_head =
TAILQ_HEAD_INITIALIZER(g_dg_log_head);
static struct dg_log_free_head g_dg_log_free_head =
TAILQ_HEAD_INITIALIZER(g_dg_log_free_head);
/* Total number of log queue nodes */
static struct dg_log_entry g_dg_log_entry[DG_LOG_ENTRY_MAX];
static struct dg_log_free_entry g_dg_log_free_entry[DG_LOG_ENTRY_MAX];
/* Get the head node */
#define DG_LOG_HEAD(_head) \
(_head) = &g_dg_log_head
#define DG_LOG_FREE_HEAD(_f_head) \
(_f_head) = &g_dg_log_free_head
/* Get the node by subscript */
#define DG_LOG_ENTRY_BY_INDEX(_elm, _index) \
(_elm) = &g_dg_log_entry[(_index)]
#define DG_LOG_FREE_ENTRY_BY_INDEX(_var, _index) \
(_var) = &g_dg_log_free_entry[(_index)]
/* Initialize node */
#define DG_LOG_ENTRY_INIT(_elm) do { \
(_elm)->index = -1; \
(_elm)->mode = DG_LOG_MODE_STDOUT; \
(_elm)->size = 50 * 1024 * 1024; \
(_elm)->name[0] = '\0'; \
(_elm)->file[0] = '\0'; \
} while (0)
typedef struct {
volatile int locked; /**< lock status 0 = unlocked, 1 = locked */
} dg_log_spinlock_t;
/* Log lock */
static dg_log_spinlock_t g_dg_log_lock = {
.locked = 0
};
static volatile int dg_log_init_flag = 0;
/*****************************************************************************
Prototype : dg_log_spinlock_lock
Description : Lock
Input : dg_log_spinlock_t *sl
Output : void
Return Value : void
Author :
Date :
*****************************************************************************/
static inline void
dg_log_spinlock_lock(dg_log_spinlock_t *sl)
{
int lock_val = 1;
asm volatile (
"1:\n"
"xchg %[locked], %[lv]\n"
"test %[lv], %[lv]\n"
"jz 3f\n"
"2:\n"
"pause\n"
"cmpl $0, %[locked]\n"
"jnz 2b\n"
"jmp 1b\n"
"3:\n"
: [locked] "=m" (sl->locked), [lv] "=q" (lock_val)
: "[lv]" (lock_val)
: "memory");
}
/*****************************************************************************
Prototype : dg_log_spinlock_unlock
Description : Unlock
Input : dg_log_spinlock_t *sl
Output : None
Return Value : None
Author :
Date :
*****************************************************************************/
static inline void
dg_log_spinlock_unlock(dg_log_spinlock_t *sl)
{
int unlock_val = 0;
asm volatile (
"xchg %[locked], %[ulv]\n"
: [locked] "=m" (sl->locked), [ulv] "=q" (unlock_val)
: "[ulv]" (unlock_val)
: "memory");
}
/*****************************************************************************
Prototype : dg_log_init
Description : Init
Input : const char *name
const char *file
Output : None
Return Value : int
Author :
Date :
*****************************************************************************/
int dg_log_init(const char *name, const char *file)
{
int i;
int num = -1;
char n_file[DG_LOG_FILE_PATH];
struct dg_log_entry *elm;
struct dg_log_head *head;
struct dg_log_free_entry *var;
struct dg_log_free_head *free_head;
if (!name || !file) return -1;
dg_log_spinlock_lock(&g_dg_log_lock);
DG_LOG_HEAD(head);
DG_LOG_FREE_HEAD(free_head);
if (!dg_log_init_flag) {
for (i = 0; i < DG_LOG_ENTRY_MAX; i++) {
DG_LOG_ENTRY_BY_INDEX(elm, i);
DG_LOG_ENTRY_INIT(elm);
DG_LOG_FREE_ENTRY_BY_INDEX(var, i);
var->index = i;
TAILQ_INSERT_TAIL(free_head, var, next);
}
dg_log_init_flag = 1;
}
snprintf(n_file, sizeof(n_file), "%s_%s.log", file, name);
TAILQ_FOREACH(elm, head, next) {
if (!strcmp(elm->n_file, n_file)) {
num = elm->index;
goto unlock;
}
}
var = TAILQ_FIRST(free_head);
if (var) {
DG_LOG_ENTRY_BY_INDEX(elm, var->index);
elm->index = var->index;
snprintf(elm->name, sizeof(elm->name), "%s", name);
snprintf(elm->file, sizeof(elm->file), "%s", file);
snprintf(elm->n_file, sizeof(elm->n_file), "%s", n_file);
TAILQ_INSERT_TAIL(head, elm, next);
TAILQ_REMOVE(free_head, var, next);
var->index = -1;
num = elm->index;
}
unlock:
dg_log_spinlock_unlock(&g_dg_log_lock);
return num;
}
/*****************************************************************************
Prototype : dg_log_set_mode
Description : Set the log mode
Input : int num
int mode
Output : None
Return Value : None
Author :
Date :
*****************************************************************************/
void dg_log_set_mode(int num, int mode)
{
struct dg_log_entry *elm;
struct dg_log_head *head;
if (num < 0) return;
dg_log_spinlock_lock(&g_dg_log_lock);
DG_LOG_HEAD(head);
TAILQ_FOREACH(elm, head, next) {
if (elm->index == num) {
elm->mode = mode;
break;
}
}
dg_log_spinlock_unlock(&g_dg_log_lock);
}
/*****************************************************************************
Prototype : dg_log_get_mode
Description : Get the log mode
Input : int num
Output : None
Return Value : int
Author :
Date :
*****************************************************************************/
int dg_log_get_mode(int num)
{
struct dg_log_entry *elm;
struct dg_log_head *head;
int mode = DG_LOG_MODE_DISABLE;
if (num < 0) return mode;
dg_log_spinlock_lock(&g_dg_log_lock);
DG_LOG_HEAD(head);
TAILQ_FOREACH(elm, head, next) {
if (elm->index == num) {
mode = elm->mode;
break;
}
}
dg_log_spinlock_unlock(&g_dg_log_lock);
return mode;
}
/*****************************************************************************
Prototype : dg_log_set_size
Description : Set the log file size
Input : int num
int mode
Output : None
Return Value : None
Author :
Date :
*****************************************************************************/
void dg_log_set_size(int num, int size)
{
struct dg_log_entry *elm;
struct dg_log_head *head;
if (num < 0) return;
dg_log_spinlock_lock(&g_dg_log_lock);
DG_LOG_HEAD(head);
TAILQ_FOREACH(elm, head, next) {
if (elm->index == num) {
if (size >= 50) {
elm->size = size * 1024 * 1024;
}
break;
}
}
dg_log_spinlock_unlock(&g_dg_log_lock);
}
/*****************************************************************************
Prototype : dg_log_print
Description : Print log
Input : int num
const char *function
unsigned int line
const char *format
...
Output : None
Return Value : None
Author :
Date :
*****************************************************************************/
void dg_log_print(int num, const char *function, unsigned int line,
const char *format, ...)
{
if (num < 0) return;
dg_log_spinlock_lock(&g_dg_log_lock);
long file_size;
FILE *fp = stdout;
struct stat stStat;
struct dg_log_entry *elm;
struct dg_log_head *head;
DG_LOG_HEAD(head);
TAILQ_FOREACH(elm, head, next) {
if (elm->index == num) {
break;
}
}
if (elm == NULL) goto unlock;
if (elm->mode == DG_LOG_MODE_FILE) {
if (stat(elm->n_file, &stStat) != 0) {
if (errno == ENOENT) file_size = 0;
else goto unlock;
} else {
file_size = stStat.st_size;
}
if (file_size > elm->size) {
if ((fp = fopen(elm->n_file, "w")) == NULL) goto unlock;
} else {
if ((fp = fopen(elm->n_file, "a")) == NULL) goto unlock;
}
} else if (elm->mode == DG_LOG_MODE_DISABLE) goto unlock;
int len;
va_list ap;
char buf[128];
char msg[2048];
time_t tm_t;
struct tm *ttm = NULL;
tm_t = time(NULL);
ttm = localtime(&tm_t);
strftime(buf, sizeof(buf), "%Y/%m/%d %X", ttm);
va_start(ap, format);
vsnprintf(msg, sizeof(msg), format, ap);
va_end(ap);
fprintf(fp, "[%s][%s:%d] %s\n", buf, function, line, msg);
fflush(fp);
if (fp != stdout) fclose(fp);
unlock:
dg_log_spinlock_unlock(&g_dg_log_lock);
return;
}
测试代码如下:
#include <stdio.h>
#include "dg_log.h"
int LOG_NUM = 0;
int test1()
{
LOG_NUM = DG_LOG_INIT(test1, "/home/log/lijd.log");
DG_SET_MODE(LOG_NUM, DG_LOG_MODE_FILE);
DG_LOG(LOG_NUM, "test1 code1!");
DG_LOG(LOG_NUM, "test1 code2, num = %d", LOG_NUM);
DG_LOG(LOG_NUM, "test1 code3, str = %s", "test code");
return 0;
}
int test2()
{
LOG_NUM = DG_LOG_INIT(test2, "/home/log/lijd.log");
DG_SET_MODE(LOG_NUM, DG_LOG_MODE_FILE);
DG_LOG(LOG_NUM, "test2 code1!");
DG_LOG(LOG_NUM, "test2 code2, num = %d", LOG_NUM);
DG_LOG(LOG_NUM, "test2 code3, str = %s", "test code");
return 0;
}
int main(int argc, char *argv[])
{
LOG_NUM = DG_LOG_INIT(main, "/home/log/lijd.log");
DG_SET_MODE(LOG_NUM, DG_LOG_MODE_FILE);
DG_LOG(LOG_NUM, "main code1!");
DG_LOG(LOG_NUM, "main code2, num = %d", LOG_NUM);
DG_LOG(LOG_NUM, "main code3, str = %s", "test code");
test1();
test2();
return 0;
}
编译代码: gcc -g -o main main.c dg_log.c
运行结果如下: