寫程式常常需要有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> /************************************************* * TYPE *************************************************/ enum log_type { /* 這裡我先設定有三個類別,分別為AT,CORE與DB,之後請自行新增刪減修改 */ LOG_TYPE_AT, LOG_TYPE_CORE, LOG_TYPE_DB, LOG_TYPE_MAX }; /***** internal function for MACRO used *****/ void _log_type_toggle(enum log_type type); void _log_type_clr(enum log_type type); void _log_type_set(enum log_type type); /***** public function to enable/disable log on certain 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); /************************************************* * Format *************************************************/ enum log_fmt { LOG_FMT_FUNC_AND_LINE, LOG_FMT_TID, ///< thread ID. LOG_FMT_LOG_TYPE, LOG_FMT_LOG_LEVEL, LOG_FMT_MAX, }; /***** internal function for MACRO used *****/ 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); /***** public function to enable/disable certain format type *****/ #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) /************************************************* * Level *************************************************/ /***** internal function for MACRO used *****/ int _log_type_level_is(enum log_type type, int *level); int _log_type_level_set(enum log_type type, int level); /***** public function to set debug level. the level is leveraged from syslog *****/ #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) /***** public function *****/ /* 請呼叫下面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