summaryrefslogtreecommitdiffstats
path: root/include/crm/common/logging.h
blob: eea4cec9aa743f41a1d4500ba0a021e940d9897f (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
/*
 * Copyright 2004-2023 the Pacemaker project contributors
 *
 * The version control history for this file may have further details.
 *
 * This source code is licensed under the GNU General Public License version 2
 * or later (GPLv2+) WITHOUT ANY WARRANTY.
 */

#ifndef PCMK__CRM_COMMON_LOGGING__H
#  define PCMK__CRM_COMMON_LOGGING__H

#  include <stdio.h>
#  include <stdint.h>           // uint8_t, uint32_t
#  include <glib.h>
#  include <qb/qblog.h>
#  include <libxml/tree.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * \file
 * \brief Wrappers for and extensions to libqb logging
 * \ingroup core
 */


/* Define custom log priorities.
 *
 * syslog(3) uses int for priorities, but libqb's struct qb_log_callsite uses
 * uint8_t, so make sure they fit in the latter.
 */

// Define something even less desired than debug
#  ifndef LOG_TRACE
#    define LOG_TRACE   (LOG_DEBUG+1)
#  endif

// Print message to stdout instead of logging it
#  ifndef LOG_STDOUT
#    define LOG_STDOUT  254
#  endif

// Don't send message anywhere
#  ifndef LOG_NEVER
#    define LOG_NEVER   255
#  endif

/* "Extended information" logging support */
#ifdef QB_XS
#  define CRM_XS QB_XS
#  define crm_extended_logging(t, e) qb_log_ctl((t), QB_LOG_CONF_EXTENDED, (e))
#else
#  define CRM_XS "|"

/* A caller might want to check the return value, so we can't define this as a
 * no-op, and we can't simply define it to be 0 because gcc will then complain
 * when the value isn't checked.
 */
static inline int
crm_extended_logging(int t, int e)
{
    return 0;
}
#endif

extern unsigned int crm_log_level;
extern unsigned int crm_trace_nonlog;

/*! \deprecated Pacemaker library functions set this when a configuration
 *              error is found, which turns on extra messages at the end of
 *              processing. It should not be used directly and will be removed
 *              from the public C API in a future release.
 */
extern gboolean crm_config_error;

/*! \deprecated Pacemaker library functions set this when a configuration
 *              warning is found, which turns on extra messages at the end of
 *              processing. It should not be used directly and will be removed
 *              from the public C API in a future release.
 */
extern gboolean crm_config_warning;

void crm_enable_blackbox(int nsig);
void crm_disable_blackbox(int nsig);
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite);

void crm_update_callsites(void);

void crm_log_deinit(void);

/*!
 * \brief Initializes the logging system and defaults to the least verbose output level
 *
 * \param[in] entity  If not NULL, will be used as the identity for logging purposes
 * \param[in] argc    The number of command line parameters
 * \param[in] argv    The command line parameter values
 */
void crm_log_preinit(const char *entity, int argc, char *const *argv);
gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon,
                      gboolean to_stderr, int argc, char **argv, gboolean quiet);

void crm_log_args(int argc, char **argv);
void crm_log_output_fn(const char *file, const char *function, int line, int level,
                       const char *prefix, const char *output);

// Log a block of text line by line
#define crm_log_output(level, prefix, output)   \
    crm_log_output_fn(__FILE__, __func__, __LINE__, level, prefix, output)

void crm_bump_log_level(int argc, char **argv);

void crm_enable_stderr(int enable);

gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags);

/* returns the old value */
unsigned int set_crm_log_level(unsigned int level);

unsigned int get_crm_log_level(void);

void pcmk_log_xml_as(const char *file, const char *function, uint32_t line,
                     uint32_t tags, uint8_t level, const char *text,
                     const xmlNode *xml);

/*
 * Throughout the macros below, note the leading, pre-comma, space in the
 * various ' , ##args' occurrences to aid portability across versions of 'gcc'.
 * https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros
 */
#if defined(__clang__)
#    define CRM_TRACE_INIT_DATA(name)
#  else
#    include <assert.h> // required by QB_LOG_INIT_DATA() macro
#    define CRM_TRACE_INIT_DATA(name) QB_LOG_INIT_DATA(name)
#endif

/*!
 * \internal
 * \brief Clip log_level to \p uint8_t range
 *
 * \param[in] level  Log level to clip
 *
 * \return 0 if \p level is less than 0, \p UINT8_MAX if \p level is greater
 *         than \p UINT8_MAX, or \p level otherwise
 */
/* @COMPAT: Make this function internal at a compatibility break. It's used in
 * public macros for now.
 */
static inline uint8_t
pcmk__clip_log_level(int level)
{
    if (level <= 0) {
        return 0;
    }
    if (level >= UINT8_MAX) {
        return UINT8_MAX;
    }
    return level;
}

/* Using "switch" instead of "if" in these macro definitions keeps
 * static analysis from complaining about constant evaluations
 */

/*!
 * \brief Log a message
 *
 * \param[in] level  Priority at which to log the message
 * \param[in] fmt    printf-style format string literal for message
 * \param[in] args   Any arguments needed by format string
 */
#  define do_crm_log(level, fmt, args...) do {                              \
        uint8_t _level = pcmk__clip_log_level(level);                       \
                                                                            \
        switch (_level) {                                                   \
            case LOG_STDOUT:                                                \
                printf(fmt "\n" , ##args);                                  \
                break;                                                      \
            case LOG_NEVER:                                                 \
                break;                                                      \
            default:                                                        \
                qb_log_from_external_source(__func__, __FILE__, fmt,        \
                                            _level, __LINE__, 0 , ##args);  \
                break;                                                      \
        }                                                                   \
    } while (0)

/*!
 * \brief Log a message that is likely to be filtered out
 *
 * \param[in] level  Priority at which to log the message
 * \param[in] fmt    printf-style format string for message
 * \param[in] args   Any arguments needed by format string
 *
 * \note This does nothing when level is \p LOG_STDOUT.
 */
#  define do_crm_log_unlikely(level, fmt, args...) do {                     \
        uint8_t _level = pcmk__clip_log_level(level);                       \
                                                                            \
        switch (_level) {                                                   \
            case LOG_STDOUT: case LOG_NEVER:                                \
                break;                                                      \
            default: {                                                      \
                static struct qb_log_callsite *trace_cs = NULL;             \
                if (trace_cs == NULL) {                                     \
                    trace_cs = qb_log_callsite_get(__func__, __FILE__, fmt, \
                                                   _level, __LINE__, 0);    \
                }                                                           \
                if (crm_is_callsite_active(trace_cs, _level, 0)) {          \
                    qb_log_from_external_source(__func__, __FILE__, fmt,    \
                                                _level, __LINE__, 0 ,       \
                                                ##args);                    \
                }                                                           \
            }                                                               \
            break;                                                          \
        }                                                                   \
    } while (0)

#  define CRM_LOG_ASSERT(expr) do {					\
        if (!(expr)) {                                                  \
            static struct qb_log_callsite *core_cs = NULL;              \
            if(core_cs == NULL) {                                       \
                core_cs = qb_log_callsite_get(__func__, __FILE__,       \
                                              "log-assert", LOG_TRACE,  \
                                              __LINE__, 0);             \
            }                                                           \
            crm_abort(__FILE__, __func__, __LINE__, #expr,              \
                      core_cs?core_cs->targets:FALSE, TRUE);            \
        }                                                               \
    } while(0)

/* 'failure_action' MUST NOT be 'continue' as it will apply to the
 * macro's do-while loop
 */
#  define CRM_CHECK(expr, failure_action) do {				            \
        if (!(expr)) {                                                  \
            static struct qb_log_callsite *core_cs = NULL;              \
            if (core_cs == NULL) {                                      \
                core_cs = qb_log_callsite_get(__func__, __FILE__,       \
                                              "check-assert",           \
                                              LOG_TRACE, __LINE__, 0);  \
            }                                                           \
	        crm_abort(__FILE__, __func__, __LINE__, #expr,	            \
		        (core_cs? core_cs->targets: FALSE), TRUE);              \
	        failure_action;						                        \
	    }								                                \
    } while(0)

/*!
 * \brief Log XML line-by-line in a formatted fashion
 *
 * \param[in] level  Priority at which to log the messages
 * \param[in] text   Prefix for each line
 * \param[in] xml    XML to log
 *
 * \note This does nothing when \p level is \p LOG_STDOUT.
 */
#  define do_crm_log_xml(level, text, xml) do {                         \
        uint8_t _level = pcmk__clip_log_level(level);                   \
        static struct qb_log_callsite *xml_cs = NULL;                   \
                                                                        \
        switch (_level) {                                               \
            case LOG_STDOUT:                                            \
            case LOG_NEVER:                                             \
                break;                                                  \
            default:                                                    \
                if (xml_cs == NULL) {                                   \
                    xml_cs = qb_log_callsite_get(__func__, __FILE__,    \
                                                 "xml-blob", _level,    \
                                                 __LINE__, 0);          \
                }                                                       \
                if (crm_is_callsite_active(xml_cs, _level, 0)) {        \
                    pcmk_log_xml_as(__FILE__, __func__, __LINE__, 0,    \
                                    _level, text, (xml));               \
                }                                                       \
                break;                                                  \
        }                                                               \
    } while(0)

/*!
 * \brief Log a message as if it came from a different code location
 *
 * \param[in] level     Priority at which to log the message
 * \param[in] file      Source file name to use instead of __FILE__
 * \param[in] function  Source function name to use instead of __func__
 * \param[in] line      Source line number to use instead of __line__
 * \param[in] fmt       printf-style format string literal for message
 * \param[in] args      Any arguments needed by format string
 */
#  define do_crm_log_alias(level, file, function, line, fmt, args...) do {  \
        uint8_t _level = pcmk__clip_log_level(level);                       \
                                                                            \
        switch (_level) {                                                   \
            case LOG_STDOUT:                                                \
                printf(fmt "\n" , ##args);                                  \
                break;                                                      \
            case LOG_NEVER:                                                 \
                break;                                                      \
            default:                                                        \
                qb_log_from_external_source(function, file, fmt, _level,    \
                                            line, 0 , ##args);              \
                break;                                                      \
        }                                                                   \
    } while (0)

/*!
 * \brief Send a system error message to both the log and stderr
 *
 * \param[in] level  Priority at which to log the message
 * \param[in] fmt    printf-style format string for message
 * \param[in] args   Any arguments needed by format string
 *
 * \deprecated One of the other logging functions should be used with
 *             pcmk_strerror() instead.
 * \note This is a macro, and \p level may be evaluated more than once.
 * \note Because crm_perror() adds the system error message and error number
 *       onto the end of fmt, that information will become extended information
 *       if CRM_XS is used inside fmt and will not show up in syslog.
 */
#  define crm_perror(level, fmt, args...) do {                              \
        uint8_t _level = pcmk__clip_log_level(level);                       \
                                                                            \
        switch (_level) {                                                   \
            case LOG_NEVER:                                                 \
                break;                                                      \
            default: {                                                      \
                const char *err = strerror(errno);                          \
                if (_level <= crm_log_level) {                              \
                    fprintf(stderr, fmt ": %s (%d)\n" , ##args, err,        \
                            errno);                                         \
                }                                                           \
                /* Pass original level arg since do_crm_log() also declares \
                 * _level                                                   \
                 */                                                         \
                do_crm_log((level), fmt ": %s (%d)" , ##args, err, errno);  \
            }                                                               \
            break;                                                          \
        }                                                                   \
    } while (0)

/*!
 * \brief Log a message with a tag (for use with PCMK_trace_tags)
 *
 * \param[in] level  Priority at which to log the message
 * \param[in] tag    String to tag message with
 * \param[in] fmt    printf-style format string for message
 * \param[in] args   Any arguments needed by format string
 *
 * \note This does nothing when level is LOG_STDOUT.
 */
#  define crm_log_tag(level, tag, fmt, args...)    do {                     \
        uint8_t _level = pcmk__clip_log_level(level);                       \
                                                                            \
        switch (_level) {                                                   \
            case LOG_STDOUT: case LOG_NEVER:                                \
                break;                                                      \
            default: {                                                      \
                static struct qb_log_callsite *trace_tag_cs = NULL;         \
                int converted_tag = g_quark_try_string(tag);                \
                if (trace_tag_cs == NULL) {                                 \
                    trace_tag_cs = qb_log_callsite_get(__func__, __FILE__,  \
                                                       fmt, _level,         \
                                                       __LINE__,            \
                                                       converted_tag);      \
                }                                                           \
                if (crm_is_callsite_active(trace_tag_cs, _level,            \
                                           converted_tag)) {                \
                    qb_log_from_external_source(__func__, __FILE__, fmt,    \
                                                _level, __LINE__,           \
                                                converted_tag , ##args);    \
                }                                                           \
            }                                                               \
        }                                                                   \
    } while (0)

#  define crm_emerg(fmt, args...)   qb_log(LOG_EMERG,       fmt , ##args)
#  define crm_crit(fmt, args...)    qb_logt(LOG_CRIT,    0, fmt , ##args)
#  define crm_err(fmt, args...)     qb_logt(LOG_ERR,     0, fmt , ##args)
#  define crm_warn(fmt, args...)    qb_logt(LOG_WARNING, 0, fmt , ##args)
#  define crm_notice(fmt, args...)  qb_logt(LOG_NOTICE,  0, fmt , ##args)
#  define crm_info(fmt, args...)    qb_logt(LOG_INFO,    0, fmt , ##args)

#  define crm_debug(fmt, args...)   do_crm_log_unlikely(LOG_DEBUG, fmt , ##args)
#  define crm_trace(fmt, args...)   do_crm_log_unlikely(LOG_TRACE, fmt , ##args)

#  define crm_log_xml_crit(xml, text)    do_crm_log_xml(LOG_CRIT,    text, xml)
#  define crm_log_xml_err(xml, text)     do_crm_log_xml(LOG_ERR,     text, xml)
#  define crm_log_xml_warn(xml, text)    do_crm_log_xml(LOG_WARNING, text, xml)
#  define crm_log_xml_notice(xml, text)  do_crm_log_xml(LOG_NOTICE,  text, xml)
#  define crm_log_xml_info(xml, text)    do_crm_log_xml(LOG_INFO,    text, xml)
#  define crm_log_xml_debug(xml, text)   do_crm_log_xml(LOG_DEBUG,   text, xml)
#  define crm_log_xml_trace(xml, text)   do_crm_log_xml(LOG_TRACE,   text, xml)

#  define crm_log_xml_explicit(xml, text)  do {                 \
        static struct qb_log_callsite *digest_cs = NULL;        \
        digest_cs = qb_log_callsite_get(                        \
            __func__, __FILE__, text, LOG_TRACE, __LINE__,      \
            crm_trace_nonlog);                                  \
        if (digest_cs && digest_cs->targets) {                  \
            do_crm_log_xml(LOG_TRACE,   text, xml);             \
        }                                                       \
    } while(0)

#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/common/logging_compat.h>
#endif

#ifdef __cplusplus
}
#endif

#endif