前言
- c语言没有现成的日志库,如果要记录日志,需要自己封装一个日志库。如果要实现日志级别和参数打印,还是比较麻烦的,正好在github找到了一个c语言开源日志库,可以实现日志级别打印,参数打印,而且还会记录日期和行号,最重要的是代码非常少,只有100多行,可以直接包含在我们自己的工程代码中,不需要任何依赖。
源码地址
- github源码连接
使用介绍
- 直接把工程目录下的log.c和log.h下载下来,包含到工程代码中即可,没有其他依赖。
- 日志级别由低到高,分别为 LOG_TRACE、LOG_DEBUG、LOG_INFO、LOG_WARN、LOG_ERROR、LOG_FATAL
- 如果设置日志级别为LOG_TRACE,则所有级别日志都会打印,如果设置日志级别为LOG_WARN,则只会打印LOG_WARN以及更高级别(即LOG_ERROR和LOG_FATAL)的日志
演示
- 测试代码
-
#include "log.h" #include <stdio.h> int main() { FILE *fp = fopen("log.txt", "a+"); if(fp == NULL){ printf("create log file failed.\n"); return -1; } //设置日志级别(在终端打印) log_set_level(LOG_TRACE); //设置日志级别(在文件中打印) log_add_fp(fp, LOG_INFO); log_trace("start trace."); log_debug("start debug."); log_info("start info."); log_warn("start warn."); log_error("start error."); log_fatal("start fatal"); // 支持参数打印 log_info("number is %d, string is %s", 10010, "helloword"); fclose(fp); }
-
- 演示效果
源码
- 如果访问github有问题,我把源码贴到下面了。
- log.h
-
#ifndef LOG_H #define LOG_H #include <stdio.h> #include <stdarg.h> #include <stdbool.h> #include <time.h> #define LOG_VERSION "0.1.0" typedef struct { va_list ap; const char *fmt; const char *file; struct tm *time; void *udata; int line; int level; } log_Event; typedef void (*log_LogFn)(log_Event *ev); typedef void (*log_LockFn)(bool lock, void *udata); enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) const char* log_level_string(int level); void log_set_lock(log_LockFn fn, void *udata); void log_set_level(int level); void log_set_quiet(bool enable); int log_add_callback(log_LogFn fn, void *udata, int level); int log_add_fp(FILE *fp, int level); void log_log(int level, const char *file, int line, const char *fmt, ...); #endif
-
- log.c
-
#include "log.h" #define MAX_CALLBACKS 32 typedef struct { log_LogFn fn; void *udata; int level; } Callback; static struct { void *udata; log_LockFn lock; int level; bool quiet; Callback callbacks[MAX_CALLBACKS]; } L; static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; #ifdef LOG_USE_COLOR static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; #endif static void stdout_callback(log_Event *ev) { char buf[16]; buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; #ifdef LOG_USE_COLOR fprintf(ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], level_strings[ev->level], ev->file, ev->line); #else fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); #endif vfprintf(ev->udata, ev->fmt, ev->ap); fprintf(ev->udata, "\n"); fflush(ev->udata); } static void file_callback(log_Event *ev) { char buf[64]; buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); vfprintf(ev->udata, ev->fmt, ev->ap); fprintf(ev->udata, "\n"); fflush(ev->udata); } static void lock(void) { if (L.lock) { L.lock(true, L.udata); } } static void unlock(void) { if (L.lock) { L.lock(false, L.udata); } } const char* log_level_string(int level) { return level_strings[level]; } void log_set_lock(log_LockFn fn, void *udata) { L.lock = fn; L.udata = udata; } void log_set_level(int level) { L.level = level; } void log_set_quiet(bool enable) { L.quiet = enable; } int log_add_callback(log_LogFn fn, void *udata, int level) { for (int i = 0; i < MAX_CALLBACKS; i++) { if (!L.callbacks[i].fn) { L.callbacks[i] = (Callback) { fn, udata, level }; return 0; } } return -1; } int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } static void init_event(log_Event *ev, void *udata) { if (!ev->time) { time_t t = time(NULL); ev->time = localtime(&t); } ev->udata = udata; } void log_log(int level, const char *file, int line, const char *fmt, ...) { log_Event ev = { .fmt = fmt, .file = file, .line = line, .level = level, }; lock(); if (!L.quiet && level >= L.level) { init_event(&ev, stderr); va_start(ev.ap, fmt); stdout_callback(&ev); va_end(ev.ap); } for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { Callback *cb = &L.callbacks[i]; if (level >= cb->level) { init_event(&ev, cb->udata); va_start(ev.ap, fmt); cb->fn(&ev); va_end(ev.ap); } } unlock(); }
-
- windows平台编译时有报错,结构体赋值语法不支持,我做了修改。
- 修改后的 log.c
-
#include "log.h" #define MAX_CALLBACKS 32 typedef struct { log_LogFn fn; void *udata; int level; } Callback; static struct { void *udata; log_LockFn lock; int level; bool quiet; Callback callbacks[MAX_CALLBACKS]; } L; static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; #ifdef LOG_USE_COLOR static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; #endif static void stdout_callback(log_Event *ev) { char buf[100] = {0}; time_t timep; time(&timep); struct tm *pt = gmtime(&timep); sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", 1900 + pt->tm_year, 1 + pt->tm_mon, pt->tm_mday, (8 + pt->tm_hour) % 24, pt->tm_min, pt->tm_sec); #ifdef LOG_USE_COLOR fprintf((FILE *)ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], level_strings[ev->level], ev->file, ev->line); #else fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); #endif vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); fprintf((FILE *)ev->udata, "\n"); fflush((FILE *)ev->udata); } static void file_callback(log_Event *ev) { char buf[100] = {0}; time_t timep; time(&timep); struct tm *pt = gmtime(&timep); sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", 1900 + pt->tm_year, 1 + pt->tm_mon, pt->tm_mday, (8 + pt->tm_hour) % 24, pt->tm_min, pt->tm_sec); fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); fprintf((FILE *)ev->udata, "\n"); fflush((FILE *)ev->udata); } static void lock(void) { if (L.lock) { L.lock(true, L.udata); } } static void unlock(void) { if (L.lock) { L.lock(false, L.udata); } } const char *log_level_string(int level) { return level_strings[level]; } void log_set_lock(log_LockFn fn, void *udata) { L.lock = fn; L.udata = udata; } void log_set_level(int level) { L.level = level; } void log_set_quiet(bool enable) { L.quiet = enable; } int log_add_callback(log_LogFn fn, void *udata, int level) { for (int i = 0; i < MAX_CALLBACKS; i++) { if (!L.callbacks[i].fn) { L.callbacks[i].fn = fn; L.callbacks[i].udata = udata; L.callbacks[i].level = level; return 0; } } return -1; } int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } static void init_event(log_Event *ev, void *udata) { if (!ev->time) { time_t t = time(NULL); ev->time = gmtime(&t); } ev->udata = udata; } void log_log(int level, const char *file, int line, const char *fmt, ...) { log_Event ev; ev.fmt = fmt; ev.file = file; ev.line = line; ev.level = level; lock(); if (!L.quiet && level >= L.level) { init_event(&ev, stderr); va_start(ev.ap, fmt); stdout_callback(&ev); va_end(ev.ap); } for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { Callback *cb = &L.callbacks[i]; if (level >= cb->level) { init_event(&ev, cb->udata); va_start(ev.ap, fmt); cb->fn(&ev); va_end(ev.ap); } } unlock(); }
-
文章来源地址https://www.toymoban.com/news/detail-730081.html
文章来源:https://www.toymoban.com/news/detail-730081.html
到了这里,关于轻量级c语言开源日志库log.c介绍 - 实现不同级别和参数化日志打印的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!