MULE 0.0.1
Multi-Use Library Essentials
Loading...
Searching...
No Matches
log.h
1#pragma once
2/**
3 * @defgroup mod_log Log
4 * @brief Levelled messages for people reading a terminal or a log file.
5 *
6 * - Each call writes one line, `LEVEL file:line: message`, with the level
7 * coloured when the output supports it.
8 * - Calls below the runtime level, set with log_set_level(), return before
9 * evaluating their arguments. Calls below #LOG_LEVEL_MIN compile to
10 * nothing.
11 * - Each line goes out in one `fwrite`, so lines from different threads do
12 * not interleave. Set the level and output before starting threads.
13 * - log_fatal() and log_assert() write, flush and abort, whatever the level.
14 *
15 * @snippet{trimleft} test_log.c log
16 *
17 * ## Colour
18 *
19 * log_color_enabled() follows the conventions other command-line tools use:
20 * - `NO_COLOR` set to anything non-empty turns colour off. It wins over
21 * everything else (https://no-color.org).
22 * - `CLICOLOR_FORCE` or `FORCE_COLOR` set to anything but empty or `0` turns
23 * colour on, even when the output is a pipe or a file.
24 * - Otherwise colour is on when the stream is a terminal and `TERM` is set
25 * and is not `dumb`.
26 *
27 * @snippet{trimleft} test_log.c color
28 * @{
29 */
30
31#include "base/base.h"
32
33#include <stdio.h>
34
35/** @name Levels and output
36 * What gets written, and where.
37 * @{ */
38
39/** How much a message matters, from chattiest to most severe. */
40typedef enum LogLevel {
41 LOG_TRACE, /**< Step-by-step detail, usually off. */
42 LOG_DEBUG, /**< Detail for whoever is debugging. */
43 LOG_INFO, /**< Normal progress worth recording. The default level. */
44 LOG_WARN, /**< Something odd that the program survived. */
45 LOG_ERROR, /**< An operation failed. */
46 LOG_FATAL, /**< The program cannot continue. */
47} LogLevel;
48
49#ifndef LOG_LEVEL_MIN
50/** Calls below this level compile to nothing. Define it before the first
51 * include of log.h, for example `-DLOG_LEVEL_MIN=LOG_INFO`. */
52#define LOG_LEVEL_MIN LOG_TRACE
53#endif
54
55/** Drop messages below @p level from now on. */
56void log_set_level(LogLevel level);
57/** Write messages to @p f from now on. Null means stderr, the default. */
58void log_set_output(FILE *f);
59
60/** @} */
61
62/** @name Logging
63 * Each macro takes a printf format and its arguments, which the compiler
64 * checks.
65 * @{ */
66
67/** Log a printf-style message at LOG_TRACE. */
68#define log_trace(...) LOG_AT_(LOG_TRACE, __VA_ARGS__)
69/** Log a printf-style message at LOG_DEBUG. */
70#define log_debug(...) LOG_AT_(LOG_DEBUG, __VA_ARGS__)
71/** Log a printf-style message at LOG_INFO. */
72#define log_info(...) LOG_AT_(LOG_INFO, __VA_ARGS__)
73/** Log a printf-style message at LOG_WARN. */
74#define log_warn(...) LOG_AT_(LOG_WARN, __VA_ARGS__)
75/** Log a printf-style message at LOG_ERROR. */
76#define log_error(...) LOG_AT_(LOG_ERROR, __VA_ARGS__)
77/** Log a printf-style message at LOG_FATAL, flush, and abort. */
78#define log_fatal(...) log_fatal_(__FILE__, __LINE__, __VA_ARGS__)
79/** Abort through log_fatal() when @p cond is false. Unlike assert(), it stays
80 * in release builds. */
81#define log_assert(cond) \
82 ((cond) ? (void)0 \
83 : log_fatal_(__FILE__, __LINE__, "assertion failed: %s", #cond))
84
85/** @} */
86
87/** @name Colour
88 * Decide whether to colour output, and the escapes to do it with.
89 * @{ */
90
91/** True when colour escapes written to @p f will render as colour. Reads the
92 * environment on every call, so call it once and keep the answer. */
93MULE_NODISCARD bool log_color_enabled(FILE *f);
94
95/* ANSI escape sequences. Macros, so they join string literals:
96 * `LOG_GREEN "ok" LOG_RESET`. Print them only when log_color_enabled(). */
97#define LOG_RED "\x1b[31m" /**< Red text. */
98#define LOG_GREEN "\x1b[32m" /**< Green text. */
99#define LOG_YELLOW "\x1b[33m" /**< Yellow text. */
100#define LOG_RESET "\x1b[0m" /**< Back to the terminal's default style. */
101
102/** @} */
103
104/** @cond */
105extern LogLevel log_level_;
106
107#define LOG_AT_(level, ...) \
108 ((level) >= LOG_LEVEL_MIN && (level) >= log_level_ \
109 ? log_write_((level), __FILE__, __LINE__, __VA_ARGS__) \
110 : (void)0)
111
112void log_write_(LogLevel level, const char *file, int line, const char *fmt,
113 ...) MULE_PRINTF(4, 5);
114MULE_NORETURN void log_fatal_(const char *file, int line, const char *fmt, ...)
115 MULE_PRINTF(3, 4);
116/** @endcond */
117
118/** @} */
void log_set_output(FILE *f)
Write messages to f from now on.
Definition log.c:26
void log_set_level(LogLevel level)
Drop messages below level from now on.
Definition log.c:22
bool log_color_enabled(FILE *f)
True when colour escapes written to f will render as colour.
Definition log.c:37
LogLevel
How much a message matters, from chattiest to most severe.
Definition log.h:40
@ LOG_ERROR
An operation failed.
Definition log.h:45
@ LOG_TRACE
Step-by-step detail, usually off.
Definition log.h:41
@ LOG_INFO
Normal progress worth recording.
Definition log.h:43
@ LOG_DEBUG
Detail for whoever is debugging.
Definition log.h:42
@ LOG_FATAL
The program cannot continue.
Definition log.h:46
@ LOG_WARN
Something odd that the program survived.
Definition log.h:44