summaryrefslogtreecommitdiff
path: root/src/shl_llog.h
blob: 5c0ea241a5b172f97c478fe7e13fa3a8c5ea4892 (plain)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
 * Library Log/Debug Interface
 * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
 * Dedicated to the Public Domain
 */

/*
 * Library Log/Debug Interface
 * Libraries should always avoid producing side-effects. This includes writing
 * log-messages of any kind. However, you often don't want to disable debugging
 * entirely, therefore, the core objects often contain a pointer to a function
 * which performs logging. If that pointer is NULL (default), logging is
 * disabled.
 *
 * This header should never be installed into the system! This is _no_ public
 * header. Instead, copy it into your application if you want and use it there.
 * Your public library API should include something like this:
 *
 *   typedef void (*MYPREFIX_log_t) (void *data,
 *                                   const char *file,
 *                                   int line,
 *                                   const char *func,
 *                                   const char *subs,
 *                                   unsigned int sev,
 *                                   const char *format,
 *                                   va_list args);
 *
 * And then the user can supply such a function when creating a new context
 * object of your library or simply supply NULL. Internally, you have a field of
 * type "MYPREFIX_log_t llog" in your main structure. If you pass this to the
 * convenience helpers like llog_dbg(), llog_warn() etc. it will automatically
 * use the "llog" field to print the message. If it is NULL, nothing is done.
 *
 * The arguments of the log-function are defined as:
 *   data: User-supplied data field that is passed straight through.
 *   file: Zero terminated string of the file-name where the log-message
 *         occurred. Can be NULL.
 *   line: Line number of @file where the message occurred. Set to 0 or smaller
 *         if not available.
 *   func: Function name where the log-message occurred. Can be NULL.
 *   subs: Subsystem where the message occurred (zero terminated). Can be NULL.
 *   sev: Severity of log-message. An integer between 0 and 7 as defined below.
 *        These are identical to the linux-kernel severities so there is no need
 *        to include these in your public API. Every app can define them
 *        themselves, if they need it.
 *   format: Format string. Must not be NULL.
 *   args: Argument array
 *
 * The user should also be able to optionally provide a data field which is
 * always passed unmodified as first parameter to the log-function. This allows
 * to add context to the logger.
 */

#ifndef SHL_LLOG_H_INCLUDED
#define SHL_LLOG_H_INCLUDED

#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>

enum llog_severity {
	LLOG_FATAL = 0,
	LLOG_ALERT = 1,
	LLOG_CRITICAL = 2,
	LLOG_ERROR = 3,
	LLOG_WARNING = 4,
	LLOG_NOTICE = 5,
	LLOG_INFO = 6,
	LLOG_DEBUG = 7,
	LLOG_SEV_NUM,
};

typedef void (*llog_submit_t) (void *data,
			       const char *file,
			       int line,
			       const char *func,
			       const char *subs,
			       unsigned int sev,
			       const char *format,
			       va_list args);

static inline __attribute__((format(printf, 8, 9)))
void llog_format(llog_submit_t llog,
		 void *data,
		 const char *file,
		 int line,
		 const char *func,
		 const char *subs,
		 unsigned int sev,
		 const char *format,
		 ...)
{
	va_list list;

	if (llog) {
		va_start(list, format);
		llog(data, file, line, func, subs, sev, format, list);
		va_end(list);
	}
}

#ifndef LLOG_SUBSYSTEM
static const char *LLOG_SUBSYSTEM __attribute__((__unused__));
#endif

#define LLOG_DEFAULT __FILE__, __LINE__, __func__, LLOG_SUBSYSTEM

#define llog_printf(obj, sev, format, ...) \
	llog_format((obj)->llog, \
		    (obj)->llog_data, \
		    LLOG_DEFAULT, \
		    (sev), \
		    (format), \
		    ##__VA_ARGS__)
#define llog_dprintf(obj, data, sev, format, ...) \
	llog_format((obj), \
		    (data), \
		    LLOG_DEFAULT, \
		    (sev), \
		    (format), \
		    ##__VA_ARGS__)

static inline __attribute__((format(printf, 4, 5)))
void llog_dummyf(llog_submit_t llog, void *data, unsigned int sev,
		 const char *format, ...)
{
}

/*
 * Helpers
 * They pick-up all the default values and submit the message to the
 * llog-subsystem. The llog_debug() function produces zero-code if
 * BUILD_ENABLE_DEBUG is not defined. Therefore, it can be heavily used for
 * debugging and will not have any side-effects.
 */

#ifdef BUILD_ENABLE_DEBUG
	#define llog_ddebug(obj, data, format, ...) \
		llog_dprintf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__)
	#define llog_debug(obj, format, ...) \
		llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__)
#else
	#define llog_ddebug(obj, data, format, ...) \
		llog_dummyf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__)
	#define llog_debug(obj, format, ...) \
		llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__)
#endif

#define llog_info(obj, format, ...) \
	llog_printf((obj), LLOG_INFO, (format), ##__VA_ARGS__)
#define llog_dinfo(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_INFO, (format), ##__VA_ARGS__)
#define llog_notice(obj, format, ...) \
	llog_printf((obj), LLOG_NOTICE, (format), ##__VA_ARGS__)
#define llog_dnotice(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_NOTICE, (format), ##__VA_ARGS__)
#define llog_warning(obj, format, ...) \
	llog_printf((obj), LLOG_WARNING, (format), ##__VA_ARGS__)
#define llog_dwarning(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_WARNING, (format), ##__VA_ARGS__)
#define llog_error(obj, format, ...) \
	llog_printf((obj), LLOG_ERROR, (format), ##__VA_ARGS__)
#define llog_derror(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_ERROR, (format), ##__VA_ARGS__)
#define llog_critical(obj, format, ...) \
	llog_printf((obj), LLOG_CRITICAL, (format), ##__VA_ARGS__)
#define llog_dcritical(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_CRITICAL, (format), ##__VA_ARGS__)
#define llog_alert(obj, format, ...) \
	llog_printf((obj), LLOG_ALERT, (format), ##__VA_ARGS__)
#define llog_dalert(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_ALERT, (format), ##__VA_ARGS__)
#define llog_fatal(obj, format, ...) \
	llog_printf((obj), LLOG_FATAL, (format), ##__VA_ARGS__)
#define llog_dfatal(obj, data, format, ...) \
	llog_dprintf((obj), (data), LLOG_FATAL, (format), ##__VA_ARGS__)

#define llog_dbg llog_debug
#define llog_warn llog_warning
#define llog_err llog_error
#define llog_crit llog_critical

/*
 * Default log messages
 * These macros can be used to produce default log messages. You can use them
 * directly in an "return" statement. The "v" variants automatically cast the
 * result to void so it can be used in return statements inside of void
 * functions. The "d" variants use the logging object directly as the parent
 * might not exist, yet.
 *
 * Most of the messages work only if debugging is enabled. This is, because they
 * are used in debug paths and would slow down normal applications.
 */

#define llog_dEINVAL(obj, data) \
	(llog_ddebug((obj), (data), "invalid arguments"), -EINVAL)
#define llog_EINVAL(obj) \
	(llog_dEINVAL((obj)->llog, (obj)->llog_data))
#define llog_vEINVAL(obj) \
	((void)llog_EINVAL(obj))
#define llog_vdEINVAL(obj, data) \
	((void)llog_dEINVAL((obj), (data)))

#define llog_dEFAULT(obj, data) \
	(llog_ddebug((obj), (data), "operation failed"), -EFAULT)
#define llog_EFAULT(obj) \
	(llog_dEFAULT((obj)->llog, (obj)->llog_data))
#define llog_vEFAULT(obj) \
	((void)llog_EFAULT(obj))
#define llog_vdEFAULT(obj, data) \
	((void)llog_dEFAULT((obj), (data)))

#define llog_dENOMEM(obj, data) \
	(llog_ddebug((obj), (data), "memory allocation failed"), -ENOMEM)
#define llog_ENOMEM(obj) \
	(llog_dENOMEM((obj)->llog, (obj)->llog_data))
#define llog_vENOMEM(obj) \
	((void)llog_ENOMEM(obj))
#define llog_vdENOMEM(obj, data) \
	((void)llog_dENOMEM((obj), (data)))

#endif /* SHL_LLOG_H_INCLUDED */