寫程式常常需要有log system用於debug,大程式常常會有很多thread或是模組,我們想根據模組分類,並且給予log特定等級,如ERR/INFO/DEBUG等等,也會希望多打印一些資訊,如function name,TPID等等,這裡就是要提供這些功能的sample code,此外,根據
syslog_r for Linux?說的According to the POSIX specification, the syslog function is already thread-safe, and so implemented in Linux. So syslog_r is unnecessary - use syslog if you need a reentrant logger function.
brook-log.h
#ifndef _LOG_H_
#define _LOG_H_
#include <syslog.h>
#include <unistd.h>
#include <sys/syscall.h>
enum log_type {
/* 這裡我先設定有三個類別,分別為AT,CORE與DB,之後請自行新增刪減修改 */
LOG_TYPE_AT,
LOG_TYPE_CORE,
LOG_TYPE_DB,
LOG_TYPE_MAX
};
void _log_type_toggle(enum log_type type);
void _log_type_clr(enum log_type type);
void _log_type_set(enum log_type type);
#define log_type_toggle(type) _log_type_toggle(LOG_TYPE_##type)
#define log_type_clr(type) _log_type_clr(LOG_TYPE_##type)
#define log_type_set(type) _log_type_set(LOG_TYPE_##type)
char *get_log_type_status(void);
enum log_fmt {
LOG_FMT_FUNC_AND_LINE,
LOG_FMT_TID,
LOG_FMT_LOG_TYPE,
LOG_FMT_LOG_LEVEL,
LOG_FMT_MAX,
};
void _log_fmt_toggle(enum log_fmt fmt);
void _log_fmt_set(enum log_fmt fmt);
void _log_fmt_clr(enum log_fmt fmt);
int _log_fmt_is_set(enum log_fmt fmt);
#define log_fmt_toggle(fmt) _log_fmt_toggle(LOG_FMT_##fmt)
#define log_fmt_set(fmt) _log_fmt_set(LOG_FMT_##fmt)
#define log_fmt_clr(fmt) _log_fmt_clr(LOG_FMT_##fmt)
#define log_fmt_is_set(fmt) _log_fmt_is_set(LOG_FMT_##fmt)
int _log_type_level_is(enum log_type type, int *level);
int _log_type_level_set(enum log_type type, int level);
#define log_type_level_is(type, level) _log_type_level_is(LOG_TYPE_##type, level)
#define log_type_level_set(type, level) _log_type_level_set(LOG_TYPE_##type, LOG_##level)
void log_pr(enum log_type type, int level, char const * const fmt, ...);
#define _log_pr(type, level, fmt, ...) \
do { \
unsigned char func[36] = {}, tid[16] = {}, tp[16] = {}, lv[16] = {};\
if (log_fmt_is_set(FUNC_AND_LINE)) { \
snprintf(func, sizeof(func) - 1, "%s(#%d), ", __FUNCTION__, __LINE__); \
} \
\
if (log_fmt_is_set(TID)) { \
snprintf(tid, sizeof(tid) - 1, "TID:%ld, ", syscall(__NR_gettid)); \
} \
\
if (log_fmt_is_set(LOG_TYPE)) { \
snprintf(tp, sizeof(tp) - 1, "%s, ", #type); \
} \
if (log_fmt_is_set(LOG_TYPE)) { \
snprintf(tp, sizeof(lv) - 1, "%s, ", #level); \
} \
log_pr(LOG_TYPE_##type, LOG_##level, "%s%s%s%s"fmt, func, tid, tp, lv, ##__VA_ARGS__); \
} while(0)
/* 請呼叫下面MACRO來印log */
#define pr_emerg(type, fmt, ...) _log_pr(type, EMERG, fmt, ##__VA_ARGS__)
#define pr_alert(type, fmt, ...) _log_pr(type, ALERT, fmt, ##__VA_ARGS__)
#define pr_crit(type, fmt, ...) _log_pr(type, CRIT, fmt, ##__VA_ARGS__)
#define pr_err(type, fmt, ...) _log_pr(type, ERR, fmt, ##__VA_ARGS__)
#define pr_warn(type, fmt, ...) _log_pr(type, WARNING, fmt, ##__VA_ARGS__)
#define pr_notice(type, fmt, ...) _log_pr(type, NOTICE, fmt, ##__VA_ARGS__)
#define pr_info(type, fmt, ...) _log_pr(type, INFO, fmt, ##__VA_ARGS__)
#define pr_dbg(type, fmt, ...) _log_pr(type, DEBUG, fmt, ##__VA_ARGS__)
#endif
brook-log.c
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include "brook-log.h"
#define DEFAULT_LOG_TYPE ((1 << LOG_TYPE_AT) | \
(1 << LOG_TYPE_CORE) | \
(1 << LOG_TYPE_DB))
#define DEFAULT_LOG_FMT ((1 << LOG_FMT_FUNC_AND_LINE) | \
(1 << LOG_FMT_TID) | \
(1 << LOG_FMT_LOG_TYPE))
static unsigned int _log_types = DEFAULT_LOG_TYPE;
static unsigned int _log_fmts = DEFAULT_LOG_FMT;
#define DEFAULT_LOG_OPTION (LOG_CONS | LOG_NDELAY)
#define DEFAULT_LOG_FACILITY LOG_LOCAL0
#define DEFAULT_LOG_LEVEL LOG_INFO
char const * const level_str[] = {
[LOG_EMERG] = "EMERG",
[LOG_ALERT] = "ALERT",
[LOG_CRIT] = "CRIT",
[LOG_ERR] = "ERR",
[LOG_WARNING] = "WARN",
[LOG_NOTICE] = "NOTICE",
[LOG_INFO] = "INFO",
[LOG_DEBUG] = "DEBUG",
};
char const * const level_fmt_str[] = {
[LOG_FMT_FUNC_AND_LINE] = "FUNC_AND_LINE",
[LOG_FMT_TID] = "TID",
[LOG_FMT_LOG_TYPE] = "LOG_TYPE",
};
#define AR_SZ(a) (sizeof(a)/sizeof(a[0]))
#define BIT_SET(_v, _b) \
do { \
(_v) |= (1 << (_b));\
} while(0)
#define BIT_CLR(_v, _b) \
do { \
(_v) &= ~(1 << (_b));\
} while(0)
#define BIT_TOGGLE(_v, _b) \
do { \
(_v) ^= (1 <<(_b)); \
} while(0)
int _log_fmt_is_set(enum log_fmt fmt)
{
if (_log_fmts & (1 << fmt)) {
return 1;
}
return 0;
}
void _log_fmt_set(enum log_fmt fmt)
{
BIT_SET(_log_fmts, fmt);
}
void _log_fmt_clr(enum log_fmt fmt)
{
BIT_CLR(_log_fmts, fmt);
}
void _log_fmt_toggle(enum log_fmt fmt)
{
BIT_TOGGLE(_log_fmts, fmt);
}
struct log_type_str_and_level {
char const * const name;
int level;
} log_type_str_and_level [] = {
/* TYPE有增減,請更新這個ARRAY */
[LOG_TYPE_AT] = { "AT", DEFAULT_LOG_LEVEL},
[LOG_TYPE_CORE] = { "CORE", DEFAULT_LOG_LEVEL},
[LOG_TYPE_DB] = { "DB", DEFAULT_LOG_LEVEL},
[LOG_TYPE_MAX] = { "MAX", DEFAULT_LOG_LEVEL},
};
int _log_type_level_is(enum log_type type, int *level)
{
if (type >= 0 && type < AR_SZ(log_type_str_and_level)) {
*level = log_type_str_and_level[type].level;
return 0;
}
return -1;
}
int _log_type_level_set(enum log_type type, int level)
{
if (type >= 0 && type < AR_SZ(log_type_str_and_level) && level >= 0 && level <= LOG_DEBUG) {
log_type_str_and_level[type].level = level;
return 0;
}
return -1;
}
int log_type_is_set(enum log_type type)
{
if (_log_types & (1 << type)) {
return 1;
}
return 0;
}
void _log_type_set(enum log_type type)
{
BIT_SET(_log_types, type);
}
void _log_type_clr(enum log_type type)
{
BIT_CLR(_log_types, type);
}
void _log_type_toggle(enum log_type type)
{
BIT_TOGGLE(_log_types, type);
}
void log_pr(enum log_type type, int level, char const * const fmt, ...)
{
va_list ap;
if ((_log_types & (1 << type)) && (log_type_str_and_level[type].level >= level)) {
va_start(ap, fmt);
vsyslog(LOG_MAKEPRI(LOG_LOCAL0, level), fmt, ap);
va_end(ap);
}
}
char *get_log_type_status_str(void)
{
int i = 0, re_sz;
static char buf[1024], *p;
p = buf;
re_sz = sizeof(buf);
p += snprintf(p, re_sz, "%2s %-16s %-10s %-12s\r\n", "id", "type", "status", "level");
for (i = 0; i < AR_SZ(log_type_str_and_level); i++) {
re_sz = sizeof(buf) - (p - buf);
if (re_sz < 1) {
break;
}
p += snprintf(p, re_sz, "%2d %-16s %-10s %-12s\r\n", i, log_type_str_and_level[i].name,
log_type_is_set((enum log_type)i) ? "enabled" : "disabled",
level_str[log_type_str_and_level[i].level]);
}
return buf;
}
char *get_log_fmt_status_str(void)
{
int i = 0, re_sz;
static char buf[256], *p;
p = buf;
re_sz = sizeof(buf);
p += snprintf(p, re_sz, "%2s %-16s %-12s\r\n", "id", "fmt", "status");
for (i = 0; i < AR_SZ(level_fmt_str); i++) {
re_sz = sizeof(buf) - (p - buf);
if (re_sz < 1) {
break;
}
p += snprintf(p, re_sz, "%2d %-16s %-12s\r\n", i, level_fmt_str[i],
_log_fmt_is_set((enum log_fmt)i) ? "enabled" : "disabled");
}
return buf;
}
void init_brook_log(void)
{
openlog(NULL, DEFAULT_LOG_OPTION, DEFAULT_LOG_FACILITY);
}
void exit_brook_log(void)
{
closelog();
}
void init_brook_log() __attribute__ ((constructor (101)));
void exit_brook_log() __attribute__ ((destructor (101)));
main.c
#include <stdio.h>
#include "brook-log.h"
int main(int argc, char *argv[])
{
pr_info(AT, "AT=%d", 0);
log_type_clr(AT);
pr_info(AT, "AT=%d", 1);
log_type_set(AT);
pr_info(AT, "AT=%d", 2);
log_fmt_toggle(FUNC_AND_LINE);
pr_info(AT, "AT=%d", 3);
log_fmt_clr(TID);
pr_info(AT, "AT=%d", 4);
log_type_level_set(AT, ERR);
pr_info(AT, "AT=%d", 5);
return 0;
}
結果
brook@vista:~/sample/syslog$ gcc main.c brook-log.c -o brook-log
brook@vista:~/sample/syslog$ brook-log
brook@vista:~/sample/syslog$ tail /var/log/local.log
Nov 26 09:51:26 vista brook-log: main(#6), TID:21765, INFO, AT=0
Nov 26 09:51:26 vista brook-log: main(#12), TID:21765, INFO, AT=2
Nov 26 09:51:26 vista brook-log: TID:21765, INFO, AT=3
Nov 26 09:51:26 vista brook-log: INFO, AT=4