/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 |