Coverage Report

Created: 2026-04-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/include/crm/common/logging.h
Line
Count
Source
1
/*
2
 * Copyright 2004-2025 the Pacemaker project contributors
3
 *
4
 * The version control history for this file may have further details.
5
 *
6
 * This source code is licensed under the GNU General Public License version 2
7
 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8
 */
9
10
#ifndef PCMK__CRM_COMMON_LOGGING__H
11
#define PCMK__CRM_COMMON_LOGGING__H
12
13
#include <stdio.h>
14
#include <stdint.h>             // uint8_t, uint32_t
15
#include <glib.h>
16
#include <qb/qblog.h>           // LOG_TRACE, qb_*
17
#include <libxml/tree.h>
18
19
#include <crm/common/results.h>     // crm_abort
20
21
#ifdef __cplusplus
22
extern "C" {
23
#endif
24
25
/**
26
 * \file
27
 * \brief Wrappers for and extensions to libqb logging
28
 * \ingroup core
29
 */
30
31
32
/* Define custom log priorities.
33
 *
34
 * syslog(3) uses int for priorities, but libqb's struct qb_log_callsite uses
35
 * uint8_t, so make sure they fit in the latter.
36
 */
37
38
// Print message to stdout instead of logging it
39
#ifndef LOG_STDOUT
40
0
#define LOG_STDOUT  254
41
#endif
42
43
// Don't send message anywhere
44
#ifndef LOG_NEVER
45
0
#define LOG_NEVER   255
46
#endif
47
48
// @COMPAT Make internal when we can break API backward compatibility
49
//! \deprecated Do not use
50
extern unsigned int crm_log_level;
51
52
// @COMPAT Make internal when we can break API backward compatibility
53
//! \deprecated Do not use
54
extern unsigned int crm_trace_nonlog;
55
56
void crm_enable_blackbox(int nsig);
57
void crm_disable_blackbox(int nsig);
58
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite);
59
60
void crm_update_callsites(void);
61
62
void crm_log_deinit(void);
63
64
/*!
65
 * \brief Initializes the logging system and defaults to the least verbose output level
66
 *
67
 * \param[in] entity  If not NULL, will be used as the identity for logging purposes
68
 * \param[in] argc    The number of command line parameters
69
 * \param[in] argv    The command line parameter values
70
 */
71
void crm_log_preinit(const char *entity, int argc, char *const *argv);
72
gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon,
73
                      gboolean to_stderr, int argc, char **argv, gboolean quiet);
74
75
void crm_log_args(int argc, char **argv);
76
void crm_log_output_fn(const char *file, const char *function, int line, int level,
77
                       const char *prefix, const char *output);
78
79
// Log a block of text line by line
80
#define crm_log_output(level, prefix, output)   \
81
    crm_log_output_fn(__FILE__, __func__, __LINE__, level, prefix, output)
82
83
void crm_bump_log_level(int argc, char **argv);
84
85
void crm_enable_stderr(int enable);
86
87
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags);
88
89
// NOTE: sbd (as of at least 1.5.2) uses this
90
/* returns the old value */
91
unsigned int set_crm_log_level(unsigned int level);
92
93
unsigned int get_crm_log_level(void);
94
95
void pcmk_log_xml_as(const char *file, const char *function, uint32_t line,
96
                     uint32_t tags, uint8_t level, const char *text,
97
                     const xmlNode *xml);
98
99
/*!
100
 * \internal
101
 * \brief Clip log_level to \p uint8_t range
102
 *
103
 * \param[in] level  Log level to clip
104
 *
105
 * \return 0 if \p level is less than 0, \p UINT8_MAX if \p level is greater
106
 *         than \p UINT8_MAX, or \p level otherwise
107
 */
108
/* @COMPAT: Make this function internal at a compatibility break. It's used in
109
 * public macros for now.
110
 */
111
static inline uint8_t
112
pcmk__clip_log_level(int level)
113
22.3k
{
114
22.3k
    if (level <= 0) {
115
0
        return 0;
116
0
    }
117
22.3k
    if (level >= UINT8_MAX) {
118
0
        return UINT8_MAX;
119
0
    }
120
22.3k
    return level;
121
22.3k
}
Unexecuted instantiation: scores_fuzzer.c:pcmk__clip_log_level
Unexecuted instantiation: results.c:pcmk__clip_log_level
Unexecuted instantiation: scores.c:pcmk__clip_log_level
strings.c:pcmk__clip_log_level
Line
Count
Source
113
1.00k
{
114
1.00k
    if (level <= 0) {
115
0
        return 0;
116
0
    }
117
1.00k
    if (level >= UINT8_MAX) {
118
0
        return UINT8_MAX;
119
0
    }
120
1.00k
    return level;
121
1.00k
}
Unexecuted instantiation: utils.c:pcmk__clip_log_level
iso8601.c:pcmk__clip_log_level
Line
Count
Source
113
21.3k
{
114
21.3k
    if (level <= 0) {
115
0
        return 0;
116
0
    }
117
21.3k
    if (level >= UINT8_MAX) {
118
0
        return UINT8_MAX;
119
0
    }
120
21.3k
    return level;
121
21.3k
}
logging.c:pcmk__clip_log_level
Line
Count
Source
113
1
{
114
1
    if (level <= 0) {
115
0
        return 0;
116
0
    }
117
1
    if (level >= UINT8_MAX) {
118
0
        return UINT8_MAX;
119
0
    }
120
1
    return level;
121
1
}
Unexecuted instantiation: mainloop.c:pcmk__clip_log_level
Unexecuted instantiation: options.c:pcmk__clip_log_level
Unexecuted instantiation: output.c:pcmk__clip_log_level
Unexecuted instantiation: output_log.c:pcmk__clip_log_level
Unexecuted instantiation: output_text.c:pcmk__clip_log_level
Unexecuted instantiation: output_xml.c:pcmk__clip_log_level
Unexecuted instantiation: patchset_display.c:pcmk__clip_log_level
Unexecuted instantiation: schemas.c:pcmk__clip_log_level
Unexecuted instantiation: xml.c:pcmk__clip_log_level
Unexecuted instantiation: xml_attr.c:pcmk__clip_log_level
Unexecuted instantiation: xml_comment.c:pcmk__clip_log_level
Unexecuted instantiation: xml_display.c:pcmk__clip_log_level
Unexecuted instantiation: xml_element.c:pcmk__clip_log_level
Unexecuted instantiation: xml_idref.c:pcmk__clip_log_level
Unexecuted instantiation: xml_io.c:pcmk__clip_log_level
Unexecuted instantiation: xpath.c:pcmk__clip_log_level
Unexecuted instantiation: acl.c:pcmk__clip_log_level
Unexecuted instantiation: actions.c:pcmk__clip_log_level
Unexecuted instantiation: agents.c:pcmk__clip_log_level
Unexecuted instantiation: cmdline.c:pcmk__clip_log_level
Unexecuted instantiation: digest.c:pcmk__clip_log_level
Unexecuted instantiation: health.c:pcmk__clip_log_level
Unexecuted instantiation: io.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_client.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_common.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_controld.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_pacemakerd.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_schedulerd.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_server.c:pcmk__clip_log_level
Unexecuted instantiation: messages.c:pcmk__clip_log_level
Unexecuted instantiation: nodes.c:pcmk__clip_log_level
Unexecuted instantiation: nvpair.c:pcmk__clip_log_level
Unexecuted instantiation: options_display.c:pcmk__clip_log_level
Unexecuted instantiation: patchset.c:pcmk__clip_log_level
Unexecuted instantiation: procfs.c:pcmk__clip_log_level
Unexecuted instantiation: rules.c:pcmk__clip_log_level
Unexecuted instantiation: servers.c:pcmk__clip_log_level
Unexecuted instantiation: cib.c:pcmk__clip_log_level
Unexecuted instantiation: ipc_attrd.c:pcmk__clip_log_level
Unexecuted instantiation: pid.c:pcmk__clip_log_level
Unexecuted instantiation: attrs.c:pcmk__clip_log_level
Unexecuted instantiation: strings_fuzzer.c:pcmk__clip_log_level
Unexecuted instantiation: cib_file_fuzzer.c:pcmk__clip_log_level
Unexecuted instantiation: cib_client.c:pcmk__clip_log_level
Unexecuted instantiation: cib_file.c:pcmk__clip_log_level
Unexecuted instantiation: cib_native.c:pcmk__clip_log_level
Unexecuted instantiation: cib_ops.c:pcmk__clip_log_level
Unexecuted instantiation: cib_remote.c:pcmk__clip_log_level
Unexecuted instantiation: cib_utils.c:pcmk__clip_log_level
Unexecuted instantiation: remote.c:pcmk__clip_log_level
Unexecuted instantiation: tls.c:pcmk__clip_log_level
Unexecuted instantiation: watchdog.c:pcmk__clip_log_level
Unexecuted instantiation: iso8601_fuzzer.c:pcmk__clip_log_level
122
123
/* Using "switch" instead of "if" in these macro definitions keeps
124
 * static analysis from complaining about constant evaluations
125
 */
126
127
/*!
128
 * \brief Log a message
129
 *
130
 * \param[in] level  Priority at which to log the message
131
 * \param[in] fmt    printf-style format string literal for message
132
 * \param[in] args   Any arguments needed by format string
133
 */
134
#define do_crm_log(level, fmt, args...) \
135
0
        do_crm_log_alias(level, __FILE__, __func__, __LINE__, fmt, ##args)
136
137
/*!
138
 * \brief Log a message that is likely to be filtered out
139
 *
140
 * \param[in] level  Priority at which to log the message
141
 * \param[in] fmt    printf-style format string for message
142
 * \param[in] args   Any arguments needed by format string
143
 *
144
 * \note This does nothing when level is \p LOG_STDOUT.
145
 */
146
19.4k
#define do_crm_log_unlikely(level, fmt, args...) do {                       \
147
19.4k
        uint8_t _level = pcmk__clip_log_level(level);                       \
148
19.4k
                                                                            \
149
19.4k
        switch (_level) {                                                   \
150
0
            case LOG_STDOUT: case LOG_NEVER:                                \
151
0
                break;                                                      \
152
19.4k
            default: {                                                      \
153
19.4k
                static struct qb_log_callsite *trace_cs = NULL;             \
154
19.4k
                if (trace_cs == NULL) {                                     \
155
19.4k
                    trace_cs = qb_log_callsite_get(__func__, __FILE__, fmt, \
156
19.4k
                                                   _level, __LINE__, 0);    \
157
19.4k
                }                                                           \
158
19.4k
                if (crm_is_callsite_active(trace_cs, _level, 0)) {          \
159
0
                    qb_log_from_external_source(__func__, __FILE__, fmt,    \
160
0
                                                _level, __LINE__, 0,        \
161
0
                                                ##args);                    \
162
0
                }                                                           \
163
19.4k
            }                                                               \
164
19.4k
            break;                                                          \
165
19.4k
        }                                                                   \
166
19.4k
    } while (0)
167
168
0
#define CRM_LOG_ASSERT(expr) do {                                       \
169
0
        if (!(expr)) {                                                  \
170
0
            static struct qb_log_callsite *core_cs = NULL;              \
171
0
            if(core_cs == NULL) {                                       \
172
0
                core_cs = qb_log_callsite_get(__func__, __FILE__,       \
173
0
                                              "log-assert", LOG_TRACE,  \
174
0
                                              __LINE__, 0);             \
175
0
            }                                                           \
176
0
            crm_abort(__FILE__, __func__, __LINE__, #expr,              \
177
0
                      core_cs?core_cs->targets:FALSE, TRUE);            \
178
0
        }                                                               \
179
0
    } while(0)
180
181
// NOTE: sbd (as of at least 1.5.2) uses this
182
/* 'failure_action' MUST NOT be 'continue' as it will apply to the
183
 * macro's do-while loop
184
 */
185
2.70k
#define CRM_CHECK(expr, failure_action) do {                            \
186
8.98k
        if (!(expr)) {                                                  \
187
200
            static struct qb_log_callsite *core_cs = NULL;              \
188
200
            if (core_cs == NULL) {                                      \
189
200
                core_cs = qb_log_callsite_get(__func__, __FILE__,       \
190
200
                                              "check-assert",           \
191
200
                                              LOG_TRACE, __LINE__, 0);  \
192
200
            }                                                           \
193
200
            crm_abort(__FILE__, __func__, __LINE__, #expr,              \
194
200
                (core_cs? core_cs->targets: FALSE), TRUE);              \
195
200
            failure_action;                                             \
196
200
        }                                                               \
197
2.70k
    } while(0)
198
199
/*!
200
 * \brief Log XML line-by-line in a formatted fashion
201
 *
202
 * \param[in] level  Priority at which to log the messages
203
 * \param[in] text   Prefix for each line
204
 * \param[in] xml    XML to log
205
 *
206
 * \note This does nothing when \p level is \p LOG_STDOUT.
207
 */
208
0
#define do_crm_log_xml(level, text, xml) do {                           \
209
0
        uint8_t _level = pcmk__clip_log_level(level);                   \
210
0
        static struct qb_log_callsite *xml_cs = NULL;                   \
211
0
                                                                        \
212
0
        switch (_level) {                                               \
213
0
            case LOG_STDOUT:                                            \
214
0
            case LOG_NEVER:                                             \
215
0
                break;                                                  \
216
0
            default:                                                    \
217
0
                if (xml_cs == NULL) {                                   \
218
0
                    xml_cs = qb_log_callsite_get(__func__, __FILE__,    \
219
0
                                                 "xml-blob", _level,    \
220
0
                                                 __LINE__, 0);          \
221
0
                }                                                       \
222
0
                if (crm_is_callsite_active(xml_cs, _level, 0)) {        \
223
0
                    pcmk_log_xml_as(__FILE__, __func__, __LINE__, 0,    \
224
0
                                    _level, text, (xml));               \
225
0
                }                                                       \
226
0
                break;                                                  \
227
0
        }                                                               \
228
0
    } while(0)
229
230
/*!
231
 * \brief Log a message as if it came from a different code location
232
 *
233
 * \param[in] level     Priority at which to log the message
234
 * \param[in] file      Source file name to use instead of __FILE__
235
 * \param[in] function  Source function name to use instead of __func__
236
 * \param[in] line      Source line number to use instead of __line__
237
 * \param[in] fmt       printf-style format string literal for message
238
 * \param[in] args      Any arguments needed by format string
239
 */
240
2.89k
#define do_crm_log_alias(level, file, function, line, fmt, args...) do {    \
241
2.89k
        uint8_t _level = pcmk__clip_log_level(level);                       \
242
2.89k
                                                                            \
243
2.89k
        switch (_level) {                                                   \
244
0
            case LOG_STDOUT:                                                \
245
0
                printf(fmt "\n", ##args);                                   \
246
0
                break;                                                      \
247
0
            case LOG_NEVER:                                                 \
248
0
                break;                                                      \
249
2.89k
            default:                                                        \
250
2.89k
                qb_log_from_external_source(function, file, fmt, _level,    \
251
2.89k
                                            line, 0, ##args);               \
252
2.89k
                break;                                                      \
253
2.89k
        }                                                                   \
254
2.89k
    } while (0)
255
256
/*!
257
 * \brief Log a message with a tag (for use with PCMK_trace_tags)
258
 *
259
 * \param[in] level  Priority at which to log the message
260
 * \param[in] tag    String to tag message with
261
 * \param[in] fmt    printf-style format string for message
262
 * \param[in] args   Any arguments needed by format string
263
 *
264
 * \note This does nothing when level is LOG_STDOUT.
265
 */
266
#define crm_log_tag(level, tag, fmt, args...) do {                          \
267
        uint8_t _level = pcmk__clip_log_level(level);                       \
268
                                                                            \
269
        switch (_level) {                                                   \
270
            case LOG_STDOUT: case LOG_NEVER:                                \
271
                break;                                                      \
272
            default: {                                                      \
273
                static struct qb_log_callsite *trace_tag_cs = NULL;         \
274
                int converted_tag = g_quark_try_string(tag);                \
275
                if (trace_tag_cs == NULL) {                                 \
276
                    trace_tag_cs = qb_log_callsite_get(__func__, __FILE__,  \
277
                                                       fmt, _level,         \
278
                                                       __LINE__,            \
279
                                                       converted_tag);      \
280
                }                                                           \
281
                if (crm_is_callsite_active(trace_tag_cs, _level,            \
282
                                           converted_tag)) {                \
283
                    qb_log_from_external_source(__func__, __FILE__, fmt,    \
284
                                                _level, __LINE__,           \
285
                                                converted_tag, ##args);     \
286
                }                                                           \
287
            }                                                               \
288
        }                                                                   \
289
    } while (0)
290
291
0
#define crm_log_xml_explicit(xml, text)  do {                   \
292
0
        static struct qb_log_callsite *digest_cs = NULL;        \
293
0
        digest_cs = qb_log_callsite_get(                        \
294
0
            __func__, __FILE__, text, LOG_TRACE, __LINE__,      \
295
0
            crm_trace_nonlog);                                  \
296
0
        if (digest_cs && digest_cs->targets) {                  \
297
0
            do_crm_log_xml(LOG_TRACE,   text, xml);             \
298
0
        }                                                       \
299
0
    } while(0)
300
301
#ifdef __cplusplus
302
}
303
#endif
304
305
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
306
#include <crm/common/logging_compat.h>
307
#endif
308
309
#endif