0%

开源LOG库-1[C语言]

源码链接:https://github.com/rxi/log.c.git

log.c

A simple logging library implemented in C99

screenshot

Usage

log.c and log.h should be dropped
into an existing project and compiled along with it. The library provides 6
function-like macros for logging:

Log.c和log.h应该放到一个现有的项目中,并与它一起编译。该库为日志记录提供了6个类似函数的宏:

1
2
3
4
5
6
log_trace(const char *fmt, ...);
log_debug(const char *fmt, ...);
log_info(const char *fmt, ...);
log_warn(const char *fmt, ...);
log_error(const char *fmt, ...);
log_fatal(const char *fmt, ...);

Each function takes a printf format string followed by additional arguments:

每个函数接受一个printf格式字符串,后跟附加参数:

1
log_trace("Hello %s", "world")

Resulting in a line with the given format printed to stderr:

结果格式化输出到stderr:

1
20:18:26 TRACE src/main.c:11: Hello world

log_set_quiet(bool enable)

Quiet-mode can be enabled by passing true to the log_set_quiet() function.
While this mode is enabled the library will not output anything to stderr, but
will continue to write to files and callbacks if any are set.

可以通过向log_set_quiet()函数传递true来启用安静模式。当启用此模式时,库不会向stderr输出任何内容,但会继续写入文件和回调(如果设置了任何回调)。

log_set_level(int level)

The current logging level can be set by using the log_set_level() function.
All logs below the given level will not be written to stderr. By default the
level is LOG_TRACE, such that nothing is ignored.

可以使用log_set_level()函数设置当前日志级别。低于给定级别的所有日志都不会写入stderr。默认情况下,级别为LOG_TRACE,因此不会忽略任何内容。

log_add_fp(FILE *fp, int level)

One or more file pointers where the log will be written can be provided to the
library by using the log_add_fp() function. The data written to the file
output is of the following format:

可以使用log_add_fp()函数向库提供一个或多个将写入日志的文件指针。写入文件输出的数据格式如下:

1
2047-03-11 20:18:26 TRACE src/main.c:11: Hello world

Any messages below the given level are ignored. If the library failed to add a
file pointer a value less-than-zero is returned.

任何低于给定级别的消息都将被忽略。如果库未能添加文件指针,则返回一个小于零的值。

log_add_callback(log_LogFn fn, void *udata, int level)

One or more callback functions which are called with the log data can be
provided to the library by using the log_add_callback() function. A callback
function is passed a log_Event structure containing the line number,
filename, fmt string, va printf va_list, level and the given udata.

可以使用log_add_callback()函数向库提供一个或多个与日志数据一起调用的回调函数。回调函数传递一个log_Event结构,其中包含行号,文件名,fmt字符串,va printf va_list,级别和给定的数据。

log_set_lock(log_LockFn fn, void *udata)

If the log will be written to from multiple threads a lock function can be set.
The function is passed the boolean true if the lock should be acquired or
false if the lock should be released and the given udata value.

如果要从多个线程写入日志,可以设置一个锁函数。该函数被传递给布尔值true(如果应该获取锁)或false(如果应该释放锁)和给定的数据值。

const char* log_level_string(int level)

Returns the name of the given log level as a string.

以字符串形式返回给定日志级别的名称。

LOG_USE_COLOR

If the library is compiled with -DLOG_USE_COLOR ANSI color escape codes will
be used when printing.

如果使用-DLOG_USE_COLOR编译库,则在打印时将使用ANSI颜色转义码。

License

This library is free software; you can redistribute it and/or modify it under
the terms of the MIT license. See LICENSE for details.

log.c

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
* Copyright (c) 2020 rxi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

#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();
}

log.h

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
/**
* Copyright (c) 2020 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See `log.c` for details.
*/

#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