File: log.h

package info (click to toggle)
charliecloud 0.43-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,116 kB
  • sloc: python: 6,021; sh: 4,284; ansic: 3,863; makefile: 598
file content (215 lines) | stat: -rw-r--r-- 8,390 bytes parent folder | download
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
/** Copyright © Charliecloud contributors.

    Logging, some of which also exits with error if a value is not as expected.

    This interface provides eight (8) assert-like macros with cryptic
    three-letter names (i.e., optimized for brevity). These verify the
    truth/falseness of their argument, exiting an error message including
    source file and line number if not. They are macros because we want the
    file and line number of the calling location and C has no stack
    introspection.

    The three letters each encode a binary “argument”:

    1. Assert that the argument is true (`T`) or false (`Z` for zero).

       We use `Z` instead of `F` (for false) because this is usually used to
       error-check standard library functions or syscalls that return zero to
       indicate success, e.g. symlink(2), and I thought `F` might confusingly
       suggest failure or false == bad.

    2. Use a specified `printf(3)`-style format string (`f`) or the default
       error message (`_`). Note that the default is very unhelpful for users,
       so use it only for “pure” asserts that you believe are unlikely to be
       violated or reflect a bug that users can’t do anything about anyway.

    3. Include `errno` in the error message (`e`) or not (`_`). Note that if
       `errno` is zero, the message will include confusingly include
       “success”; be careful about whether `errno` is meaningful.

    For example:

      char *d = "/does/not/exist";

      T__ (d != NULL && d[0] != '/');
        -> ch-run[27720]: error: assertion failure: please report this bug (ch-run.c:128)

    This is a good example of a “pure” assertion.

      Z__ (d == NULL || d[0] == '/');
        -> ch-run[27720]: error: assertion failure: please report this bug (ch-run.c:128)

    Typically inadvisable (i.e., don’t use Z?? on expressions) because the
    implicit negation of the expression can be confusing.

      Z_e (chdir(d));
        -> ch-run[27720]: error: assertion failure: please report this bug: No such file or directory (ch-run.c:128 2 ENOENT)

    Typically inadviseable because `chdir(2)` failing is reasonably expected
    and likely due to external causes, so we should explain to the user what
    was being attempted.

      Zf_ (chdir(c), "can't cd: %s", d);
        -> ch-run[27720]: error: can't cd: /does/not/exist (ch-run.c:128)

    Also typically inadviseable because the *reason* for the `chdir(2)`
    failure (as encoded in `errno`) is likely to be important for debugging,
    so we should pass it on to the user.

      Zfe (chdir(d), "can't cd: %s", d);
        -> ch-run[27720]: error: can't cd: /does/not/exist: No such file or  directory (ch-run.c:128 2 ENOENT)

    Best practice for typical error conditions. This explains both what we
    were trying to do and what went wrong.

      errno = 0;

      Z_e (false);
        -> ch-run[27720]: error: assertion failure: please report this bug: Success (ch-run.c:128 0 0)

   Example of a maximally un-helpful error message.

   Note the style of including a space between the macro name and the opening
   parenthesis, to distinguish it from standard function call. */

#define _GNU_SOURCE
#pragma once

#include <errno.h>
#include <stddef.h>
#include <syslog.h>

#include "misc.h"


/** Macros **/

/** Verify that @p x is true (non-zero); otherwise, exit with a generic error
    message. */
#define T__(x)       do { \
                        if (!(x)) \
                           msg_fatal(__FILE__, __LINE__, -1, NULL); \
                     } while (0)

/** Verify that @p x is true (non-zero); otherwise, exit with an error message
    specified by a `printf(3)` format string in the second argument along with
    appropriate additional arguments. */
#define Tf_(x, ...)  do { \
                        if (!(x)) \
                           msg_fatal(__FILE__, __LINE__, -1, __VA_ARGS__); \
                     } while (0)

/** Verify that @p x is true (non-zero); otherwise, exit with a generic error
    message followed by errno and its stringified form. */
#define T_e(x)       do { \
                        if (!(x)) \
                           msg_fatal(__FILE__, __LINE__, errno, NULL); \
                     } while (0);

/** Verify that @p x is true (non-zero); otherwise, exit with an error message
    specified by a `printf(3)` format string in the second argument along with
    appropriate additional arguments, followed by errno and its stringified
    form. */
#define Tfe(x, ...)  do { \
                        if (!(x)) \
                           msg_fatal(__FILE__, __LINE__, errno, __VA_ARGS__); \
                     } while (0)

/** Verify that @p x is zero (false); otherwise, exit with a generic error
    message. */
#define Z__(x)       do { \
                        if (x) \
                           msg_fatal(__FILE__, __LINE__, -1, NULL); \
                     } while (0)

/** Verify that @p x is zero (false); otherwise, exit with an error message
    specified by a `printf(3)` format string in the second argument along with
    appropriate additional arguments. */
#define Zf_(x, ...)  do { \
                        if (x) \
                           msg_fatal(__FILE__, __LINE__, -1, __VA_ARGS__); \
                     } while (0)

/** Verify that @p x is zero (false); otherwise, exit with a generic error
    message followed by errno and its stringified form. */
#define Z_e(x)       do { \
                        if (x) \
                           msg_fatal(__FILE__, __LINE__, errno, NULL); \
                     } while (0)

/** Verify that @p x is zero (false); otherwise, exit with an error message
    specified by a `printf(3)` format string in the second argument along with
    appropriate additional arguments, followed by errno and its stringified
    form. */
#define Zfe(x, ...)  do { \
                        if (x) \
                           msg_fatal(__FILE__, __LINE__, errno, __VA_ARGS__); \
                     } while (0)

/* Log the current UIDs. */
#define LOG_IDS log_ids(__func__, __LINE__)

/* Use these instead of printf(3), sprintf(3), etc. to explain to the user
   what is going on. Do not use these for output. */
#define FATAL(e, ...) msg_fatal(      __FILE__, __LINE__, e,  __VA_ARGS__)
#define ERROR(e, ...) msg_error(      __FILE__, __LINE__, e,  __VA_ARGS__)
#define WARNING(...)  msg(LL_WARNING, __FILE__, __LINE__, -1, __VA_ARGS__)
#define INFO(...)     msg(LL_INFO,    __FILE__, __LINE__, -1, __VA_ARGS__)
#define VERBOSE(...)  msg(LL_VERBOSE, __FILE__, __LINE__, -1, __VA_ARGS__)
#define DEBUG(...)    msg(LL_DEBUG,   __FILE__, __LINE__, -1, __VA_ARGS__)
#define TRACE(...)    msg(LL_TRACE,   __FILE__, __LINE__, -1, __VA_ARGS__)

/* Syslog facility and level we use. */
#ifdef ENABLE_SYSLOG
#define SYSLOG_PRI (LOG_USER|LOG_INFO)
#endif

/* Size of “warnings” buffer, in bytes. We want this to be big enough that we
   don’t need to worry about running out of room. */
#define WARNINGS_SIZE (4*1024)


/** Types **/

enum log_level { LL_FATAL =   -3,
                 LL_STDERR =  -2,
                 LL_WARNING = -1,
                 LL_INFO =     0,  // minimum number of -v to print the msg
                 LL_VERBOSE =  1,
                 LL_DEBUG =    2,
                 LL_TRACE =    3 };

enum log_color_when { LL_COLOR_NULL = 0,
                      LL_COLOR_AUTO,
                      LL_COLOR_YES,
                      LL_COLOR_NO };

enum log_test { LL_TEST_NONE  = 0,
                LL_TEST_YES   = 1,
                LL_TEST_FATAL = 2 };


/** External variables **/

extern bool abort_fatal;
extern bool log_color_p;
extern enum log_level verbose;
extern char *warnings;
extern size_t warnings_offset;


/** Function prototypes **/

void log_ids(const char *func, int line);
void logging_init(enum log_color_when when, enum log_test test);
void msg(enum log_level level, const char *file, int line, int errno_,
         const char *fmt, ...)
        __attribute__ ((format (printf, 5, 6)));
void msg_error(const char *file, int line, int errno_,
               const char *fmt, ...)
              __attribute__ ((format (printf, 4, 5)));
noreturn msg_fatal(const char *file, int line, int errno_,
                   const char *fmt, ...)
                  __attribute__ ((format (printf, 4, 5)));
void warnings_reprint(void);