summaryrefslogtreecommitdiff
path: root/include/nslog/nslog.h
blob: 91135279e5d0f0825772e544abfa623c23e1e16e (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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*
 * Copyright 2017 Daniel Silverstone <dsilvers@netsurf-browser.org>
 *
 * This file is part of libnslog.
 *
 * Licensed under the MIT License,
 *		  http://www.opensource.org/licenses/mit-license.php
 */

/**
 * \file
 * NetSurf Logging
 */

#ifndef NSLOG_NSLOG_H_
#define NSLOG_NSLOG_H_

#include <stdarg.h>

/**
 * Log levels
 *
 * When logging, you can set the 'log level' which can be used as a basic way
 * to filter the logging which occurs.  In addition, the logging level can be
 * used to control which logging is compiled in.  As such, release builds can
 * be made where DEBUG and DEEPDEBUG are compiled out.
 *
 * If logging is set at INFO then everything above INFO will be logged,
 * including warnings, errors, etc.
 */
typedef enum {
	NSLOG_LEVEL_DEEPDEBUG = 0,
	NSLOG_LEVEL_DEBUG = 1,
	NSLOG_LEVEL_VERBOSE = 2,
	NSLOG_LEVEL_INFO = 3,
	NSLOG_LEVEL_WARNING = 4,
	NSLOG_LEVEL_ERROR = 5,
	NSLOG_LEVEL_CRITICAL = 6,
} nslog_level;

/**
 * Convert a logging level to a string.
 *
 * The returned string is owned by the nslog library (static) and should
 * not be freed.
 *
 * \param level The level for which you want the 'canonical' name.
 */
const char *nslog_level_name(nslog_level level);

#define NSLOG_LEVEL_DD		NSLOG_LEVEL_DEEPDEBUG
#define NSLOG_LEVEL_DBG		NSLOG_LEVEL_DEBUG
#define NSLOG_LEVEL_CHAT	NSLOG_LEVEL_VERBOSE
#define NSLOG_LEVEL_WARN	NSLOG_LEVEL_WARNING
#define NSLOG_LEVEL_ERR		NSLOG_LEVEL_ERROR
#define NSLOG_LEVEL_CRIT	NSLOG_LEVEL_CRITICAL

#ifndef NSLOG_COMPILED_MIN_LEVEL
/**
 * The minimum log level to be compiled in.
 *
 * When compiling a library or application which uses nslog, you can set the
 * minimum level to be compiled in.  By setting this, you can reduce the
 * size of the binary and potentially improve the performance of hotspots
 * which contain logging instructions.
 */
#define NSLOG_COMPILED_MIN_LEVEL NSLOG_LEVEL_DEBUG
#endif

/**
 * Logging category
 *
 * This structure is used internally by nslog to manage categories for logging.
 * Categories are declared by the \ref NSLOG_DECLARE_CATEGORY and defined by
 * either \ref NSLOG_DEFINE_CATEGORY and \ref NSLOG_DEFINE_SUBCATEGORY.
 *
 * It is not recommended that clients of nslog look inside the category structs
 * excepting the name (and namelen) values.  In addition you should only trust
 * those values when handling a log callback.
 */
typedef struct nslog_category_s {
	const char *cat_name; /**< The category leaf name (static) */
	const char *description; /**< The category description (static) */
	struct nslog_category_s *parent; /**< The category's parent (static) */
	char *name; /**< The fully qualified category name (owned by nslog) */
	int namelen; /**< The length of the category name */
	struct nslog_category_s *next; /**< Link to next category (internal) */
} nslog_category_t;

/**
 * Log entry context
 *
 * When logging, nslog will create an entry context and pass it to the log
 * callback.  This context tells you everything about the log entry being
 * created (except the message) and is ephemeral (if you need the content
 * beyond the scope of the log callback, copy it).
 */
typedef struct nslog_entry_context_s {
	nslog_category_t *category; /**< The category under which the log entry is being made */
	nslog_level level; /**< The level at which the log entry is being made */
	const char *filename; /**< The source filename where the log entry is */
	int filenamelen; /**< The source filename name's length */
	const char *funcname; /**< The source function where the log entry is */
	int funcnamelen; /**< The source function name's length */
	int lineno; /**< The line number at which the log entry is */
} nslog_entry_context_t;

/**
 * Declare a category
 *
 * This macro is used to declare a category.  Use it in headers to allow more
 * than one source file to use the same category.
 *
 * \param catname The category leaf name (as a bareword)
 */
#define NSLOG_DECLARE_CATEGORY(catname)				\
	extern nslog_category_t __nslog_category_##catname

/**
 * Define a category
 *
 * This macro is used to define the storage for a category.  Use it in one of
 * your C source files to allow the category to have some storage space.
 *
 * \param catname The category name (as a bareword)
 * \param description The category description (as a static string)
 */
#define NSLOG_DEFINE_CATEGORY(catname, description)	\
	nslog_category_t __nslog_category_##catname = { \
		#catname,				\
		description,				\
		NULL,					\
		NULL,					\
		0,					\
		NULL,					\
	}

/**
 * Define a sub-category
 *
 * This macro is used to define the storage for a category which has a parent.
 * Use it in one of your C source files to allow the category to have some
 * storage space.  Sub-categories end up named by their parent name and their
 * name separated by slashes.  This is arbitrary in depth, allowing sub-sub-sub
 * categories etc.
 *
 * \param parentcatname The category name of the parent category (as a bareword)
 * \param catname The category name (as a bareword)
 * \param description The category description (as a static string)
 */
#define NSLOG_DEFINE_SUBCATEGORY(parentcatname, catname, description)	\
	nslog_category_t __nslog_category_##catname = {			\
		#catname,						\
		description,						\
		&__nslog_category_##parentcatname,			\
		NULL,							\
		0,							\
		NULL,							\
	}

/**
 * Log something
 *
 * This is the primary logging macro in nslog.  It needs access to the category
 * which is to be used to log, hence header files and the \ref
 * NSLOG_DECLARE_CATEGORY macro.
 *
 * \param catname The category name (as a bareword)
 * \param level The level at which this is logged (as a bareword such as WARNING)
 * \param logmsg The log message itself (printf format string)
 * \param args The arguments for the log message
 */
#define NSLOG(catname, level, logmsg, args...)				\
	do {								\
		if (NSLOG_LEVEL_##level >= NSLOG_COMPILED_MIN_LEVEL) {	\
			static nslog_entry_context_t _nslog_ctx = {	\
				&__nslog_category_##catname,		\
				NSLOG_LEVEL_##level,			\
				__FILE__,				\
				sizeof(__FILE__) - 1,			\
				__PRETTY_FUNCTION__,			\
				sizeof(__PRETTY_FUNCTION__) - 1,	\
				__LINE__,				\
			};						\
			nslog__log(&_nslog_ctx, logmsg, ##args);	\
		}							\
	} while(0)

/**
 * Internal logging function
 *
 * While clients of nslog will not call this function directly (preferring to
 * use the \ref NSLOG macro, this is the actual function which implements it.
 *
 * \param ctx The log entry context for the log
 * \param pattern The printf message pattern
 * \param ... The arguments for the log entry
 */
void nslog__log(nslog_entry_context_t *ctx,
		const char *pattern,
		...) __attribute__ ((format (printf, 2, 3)));

/**
 * Log error types
 *
 * When nslog is performing actions which can allocate memory or otherwise
 * rely on internal state machines.  Such functions always return an error
 * code and any extra values in pointer-based out-parameters.
 */
typedef enum {
	NSLOG_NO_ERROR = 0, /**< Nothing went wrong.  Have a party. */
	NSLOG_NO_MEMORY = 1, /**< nslog ran out of memory.  Worry, a great deal */
	NSLOG_UNCORKED = 2, /**< nslog is already uncorked, don't rush it */
	NSLOG_PARSE_ERROR = 3, /**< nslog failed to parse the given log filter */
} nslog_error;

/**
 * Callback type for logging
 *
 * In order for a logging client to actually receive the messages which are
 * logged, the client must register a logging callback.  It is recommended that
 * only the highest level client do so (for example NetSurf) but test suites
 * can also register for log messages if you want to use that as part of your
 * testing.
 *
 * \param context The context pointer registered for the callback
 * \param ctx The log entry context
 * \param fmt The log message (printf style format string)
 * \param args The printf arguments for the log entry
 */
typedef void (*nslog_callback)(void *context, nslog_entry_context_t *ctx,
			       const char *fmt, va_list args);

/**
 * Set the render callback for logging
 *
 * In order for the client to receive the log messages, it needs to register
 * a \ref nslog_callback function.
 *
 * \param cb The callback function pointer
 * \param context The context pointer to provide to the callback
 * \return Whether or not this succeeded
 */
nslog_error nslog_set_render_callback(nslog_callback cb, void *context);

/**
 * Uncork the log
 *
 * When nslog starts up, it is corked which means that any messages logged will
 * be saved up ready for when the client calls this function.  Corked messages
 * will be rendered when logged, so don't expect the format strings to be
 * identical between logging before and after uncorking.
 *
 * Any stored log messages will be drained before this function returns.
 *
 * \return Whether or not the uncorking succeeded.
 */
nslog_error nslog_uncork(void);

/**
 * Finalise log categories, release filter handles, etc.
 *
 * Since logging categories can have memory allocated to them at runtime,
 * and the logging filters can have references held inside the library,
 * clients which wish to be 'valgrind clean' may wish to call this to
 * ensure that any memory allocated inside the nslog library is released.
 *
 * This does not remove the active log callback, so logging calls after this
 * returns will still work (though will be unfiltered).  Of course, they will
 * cause memory to be allocated once more.  This function can be called as
 * many times as desired, it is idempotent.
 *
 * If the logging was corked when this was called, pending corked messages
 * will be delivered if necessary before any filters are removed.
 */
void nslog_cleanup(void);

/**
 * Log filter handle
 *
 * nslog allows clients to set a complex filter which can be used to restrict
 * the log messages which make their way through to the log callback.
 *
 * Clients can build log filters up "by hand" or can pass a string
 * representation of a log filter to the internal nslog parser.
 *
 * Log filters are reference counted and filter builders will return
 * a log filter with a reference, and will take automatically reference
 * any filters passed in, so remember to unref them if you're done.
 */
typedef struct nslog_filter_s nslog_filter_t;

/**
 * Create a category based filter
 *
 * The returned filter matches against the fully qualified category name
 * and succeeds if the given category name is either an exact match or
 * is a proper prefix of the category.
 *
 * For example, a filter of 'foo/bar' will match 'foo/bar' 'foo/bar/baz'
 * but not 'foo' or 'foo/barfle'.
 *
 * \param catname The category name to filter on
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_category_new(const char *catname,
				      nslog_filter_t **filter);
/**
 * Create a log level based filter
 *
 * The returned filter matches against the level of the log entry and succeeds
 * if the entry's level is at least the value of the filter.
 *
 * For example, a filter of \ref NSLOG_LEVEL_WARNING will match log entries
 * of the same level or higher (such as \ref NSLOG_LEVEL_ERROR)
 * but not lower levels (such as \ref NSLOG_LEVEL_DEBUG)
 *
 * \param level The log level to filter at
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_level_new(nslog_level level,
				   nslog_filter_t **filter);

/**
 * Create a filename based filter
 *
 * The returned filter matches against the filename and succeeds
 * if the entry's filename leafname matches the value of the filter.
 *
 * For example, a filter of "foo/bar.c" will match log entries
 * for "foo/bar.c" or "baz/foo/bar.c" but not "baz/bar.c".
 *
 * \param filename The filename to filter with
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_filename_new(const char *filename,
				      nslog_filter_t **filter);

/**
 * Create a dirname based filter
 *
 * The returned filter matches against the filename and succeeds
 * if the entry's filename dirname matches the value of the filter.
 *
 * For example, a filter of "foo" will match log entries
 * for "foo/bar.c" or "foo/bar/baz.c" but not "baz/foo.c".
 *
 * \param dirname The directory name to filter with
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_dirname_new(const char *dirname,
				     nslog_filter_t **filter);

/**
 * Create a function-name based filter
 *
 * The returned filter matches against the name of the function
 * where the \ref NSLOG statement exists, and succeeds if the
 * entry's function name exactly matches the value of the filter.
 *
 * For example, a filter of "foo" will match log entries
 * for `foo()` or `foo(int)` but not `foobar()`.
 *
 * \param funcname The function name to filter on
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_funcname_new(const char *funcname,
				      nslog_filter_t **filter);

/**
 * Create a logical 'and' of two filters.
 *
 * The returned filter succeeds if both the passed in filters
 * pass.  It will short-circuit and not evaluate the `right`
 * filter if the `left` filter fails.
 *
 * \param left The first filter to check
 * \param right The second filter to check
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_and_new(nslog_filter_t *left,
				 nslog_filter_t *right,
				 nslog_filter_t **filter);

/**
 * Create a logical 'or' of two filters.
 *
 * The returned filter succeeds if either of the passed in filters
 * pass.  It will short-circuit and not evaluate the `right`
 * filter if the `left` filter succeeds.
 *
 * \param left The first filter to check
 * \param right The second filter to check
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_or_new(nslog_filter_t *left,
				nslog_filter_t *right,
				nslog_filter_t **filter);

/**
 * Create a logical 'xor' of two filters.
 *
 * The returned filter succeeds if one, or the other,
 * but not both the passed in filters pass.
 *
 * \param left The first filter to check
 * \param right The second filter to check
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_xor_new(nslog_filter_t *left,
				 nslog_filter_t *right,
				 nslog_filter_t **filter);

/**
 * Create a logical 'not' of a filters.
 *
 * The returned filter succeeds if the passed in filter
 * fails.
 *
 * \param input The first filter to check
 * \param filter A pointer to a filter to be filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_not_new(nslog_filter_t *input,
				 nslog_filter_t **filter);

/**
 * Take a reference to the passed in filter.
 *
 * This increments the reference count of the given filter
 * and returns it directly to encourage a style of:
 *
 * `myfilter = nslog_filter_ref(incomingfilter)`
 *
 * \param filter A pointer to a filter to be referenced
 * \return Whether or not this succeeds
 */
nslog_filter_t *nslog_filter_ref(nslog_filter_t *filter);

/**
 * Release a reference to the passed in filter.
 *
 * This decrements the reference count of the given filter
 * and returns NULL to encourage the style of:
 *
 * `myfilter = nslog_filter_ref(myfilter)`
 *
 * \param filter A pointer to a filter to be dereferenced
 * \return Whether or not this succeeds
 */
nslog_filter_t *nslog_filter_unref(nslog_filter_t *filter);

/**
 * Set the passed in filter as the active filter
 *
 * This sets the active filter for managing the log.
 * If you don't pass `NULL` in prev, then the currently
 * active filter is returned for you to reuse later.
 *
 * \param filter A pointer to a filter to be set active
 * \param prev A pointer which will be optionally filled out
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_set_active(nslog_filter_t *filter,
				    nslog_filter_t **prev);


/**
 * Render a filter as its canonical textual form.
 *
 * Filters can be written in plain text and this function
 * turns filters into their canonical textual form.
 *
 * The caller owns the returned string and must `free()` it.
 *
 * \param filter A pointer to a filter to be rendered as text
 * \return A string representing the filter
 */
char *nslog_filter_sprintf(nslog_filter_t *filter);

/**
 * Parse a filter's textual form.
 *
 * Filters can be written in plain text and this function
 * turns textual filters into filters which can be used
 * by nslog.
 *
 * The caller owns the reference returned to it and must
 * unreference the filter when done with it.
 *
 * \param input A pointer to filter text to be parsed
 * \param output A pointer to fill out with the parsed filter
 * \return Whether or not this succeeds
 */
nslog_error nslog_filter_from_text(const char *input,
				   nslog_filter_t **output);

#endif /* NSLOG_NSLOG_H_ */