1 /*
2 * Copyright 2015-2024 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_INTERNAL__H
11 #define PCMK__CRM_COMMON_LOGGING_INTERNAL__H
12
13 #include <glib.h>
14
15 #include <crm/common/logging.h>
16 #include <crm/common/output_internal.h>
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21
22 /* Some warnings are too noisy when logged every time a given function is called
23 * (for example, using a deprecated feature). As an alternative, we allow
24 * warnings to be logged once per invocation of the calling program. Each of
25 * those warnings needs a flag defined here.
26 */
27 enum pcmk__warnings {
28 pcmk__wo_blind = (1 << 0),
29 pcmk__wo_record_pending = (1 << 1),
30 pcmk__wo_require_all = (1 << 4),
31 pcmk__wo_order_score = (1 << 5),
32 pcmk__wo_group_order = (1 << 11),
33 pcmk__wo_group_coloc = (1 << 12),
34 pcmk__wo_set_ordering = (1 << 15),
35 pcmk__wo_rdisc_enabled = (1 << 16),
36 pcmk__wo_op_attr_expr = (1 << 19),
37 pcmk__wo_clone_master_max = (1 << 23),
38 pcmk__wo_clone_master_node_max = (1 << 24),
39 pcmk__wo_master_role = (1 << 26),
40 pcmk__wo_slave_role = (1 << 27),
41 };
42
43 /*!
44 * \internal
45 * \brief Log a warning once per invocation of calling program
46 *
47 * \param[in] wo_flag enum pcmk__warnings value for this warning
48 * \param[in] fmt... printf(3)-style format and arguments
49 */
50 #define pcmk__warn_once(wo_flag, fmt...) do { \
51 if (!pcmk_is_set(pcmk__warnings, wo_flag)) { \
52 if (wo_flag == pcmk__wo_blind) { \
53 crm_warn(fmt); \
54 } else { \
55 pcmk__config_warn(fmt); \
56 } \
57 pcmk__warnings = pcmk__set_flags_as(__func__, __LINE__, \
58 LOG_TRACE, \
59 "Warn-once", "logging", \
60 pcmk__warnings, \
61 (wo_flag), #wo_flag); \
62 } \
63 } while (0)
64
65 typedef void (*pcmk__config_error_func) (void *ctx, const char *msg, ...)
66 G_GNUC_PRINTF(2, 3);
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
67 typedef void (*pcmk__config_warning_func) (void *ctx, const char *msg, ...)
68 G_GNUC_PRINTF(2, 3);
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
69
70 extern pcmk__config_error_func pcmk__config_error_handler;
71 extern pcmk__config_warning_func pcmk__config_warning_handler;
72
73 extern void *pcmk__config_error_context;
74 extern void *pcmk__config_warning_context;
75
76 void pcmk__set_config_error_handler(pcmk__config_error_func error_handler, void *error_context);
77 void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler, void *warning_context);
78
79 /* Pacemaker library functions set this when a configuration error is found,
80 * which turns on extra messages at the end of processing.
81 */
82 extern bool pcmk__config_has_error;
83
84 /* Pacemaker library functions set this when a configuration warning is found,
85 * which turns on extra messages at the end of processing.
86 */
87 extern bool pcmk__config_has_warning;
88
89 /*!
90 * \internal
91 * \brief Log an error and make crm_verify return failure status
92 *
93 * \param[in] fmt... printf(3)-style format string and arguments
94 */
95 #define pcmk__config_err(fmt...) do { \
96 pcmk__config_has_error = true; \
97 if (pcmk__config_error_handler == NULL) { \
98 crm_err(fmt); \
99 } else { \
100 pcmk__config_error_handler(pcmk__config_error_context, fmt); \
101 } \
102 } while (0)
103
104 /*!
105 * \internal
106 * \brief Log a warning and make crm_verify return failure status
107 *
108 * \param[in] fmt... printf(3)-style format string and arguments
109 */
110 #define pcmk__config_warn(fmt...) do { \
111 pcmk__config_has_warning = true; \
112 if (pcmk__config_warning_handler == NULL) { \
113 crm_warn(fmt); \
114 } else { \
115 pcmk__config_warning_handler(pcmk__config_warning_context, fmt);\
116 } \
117 } while (0)
118
119 /*!
120 * \internal
121 * \brief Execute code depending on whether trace logging is enabled
122 *
123 * This is similar to \p do_crm_log_unlikely() except instead of logging, it
124 * selects one of two code blocks to execute.
125 *
126 * \param[in] if_action Code block to execute if trace logging is enabled
127 * \param[in] else_action Code block to execute if trace logging is not enabled
128 *
129 * \note Neither \p if_action nor \p else_action can contain a \p break or
130 * \p continue statement.
131 */
132 #define pcmk__if_tracing(if_action, else_action) do { \
133 static struct qb_log_callsite *trace_cs = NULL; \
134 \
135 if (trace_cs == NULL) { \
136 trace_cs = qb_log_callsite_get(__func__, __FILE__, \
137 "if_tracing", LOG_TRACE, \
138 __LINE__, crm_trace_nonlog); \
139 } \
140 if (crm_is_callsite_active(trace_cs, LOG_TRACE, \
141 crm_trace_nonlog)) { \
142 if_action; \
143 } else { \
144 else_action; \
145 } \
146 } while (0)
147
148 /*!
149 * \internal
150 * \brief Log XML changes line-by-line in a formatted fashion
151 *
152 * \param[in] level Priority at which to log the messages
153 * \param[in] xml XML to log
154 *
155 * \note This does nothing when \p level is \c LOG_STDOUT.
156 */
157 #define pcmk__log_xml_changes(level, xml) do { \
158 uint8_t _level = pcmk__clip_log_level(level); \
159 static struct qb_log_callsite *xml_cs = NULL; \
160 \
161 switch (_level) { \
162 case LOG_STDOUT: \
163 case LOG_NEVER: \
164 break; \
165 default: \
166 if (xml_cs == NULL) { \
167 xml_cs = qb_log_callsite_get(__func__, __FILE__, \
168 "xml-changes", _level, \
169 __LINE__, 0); \
170 } \
171 if (crm_is_callsite_active(xml_cs, _level, 0)) { \
172 pcmk__log_xml_changes_as(__FILE__, __func__, __LINE__, \
173 0, _level, xml); \
174 } \
175 break; \
176 } \
177 } while(0)
178
179 /*!
180 * \internal
181 * \brief Log an XML patchset line-by-line in a formatted fashion
182 *
183 * \param[in] level Priority at which to log the messages
184 * \param[in] patchset XML patchset to log
185 *
186 * \note This does nothing when \p level is \c LOG_STDOUT.
187 */
188 #define pcmk__log_xml_patchset(level, patchset) do { \
189 uint8_t _level = pcmk__clip_log_level(level); \
190 static struct qb_log_callsite *xml_cs = NULL; \
191 \
192 switch (_level) { \
193 case LOG_STDOUT: \
194 case LOG_NEVER: \
195 break; \
196 default: \
197 if (xml_cs == NULL) { \
198 xml_cs = qb_log_callsite_get(__func__, __FILE__, \
199 "xml-patchset", _level, \
200 __LINE__, 0); \
201 } \
202 if (crm_is_callsite_active(xml_cs, _level, 0)) { \
203 pcmk__log_xml_patchset_as(__FILE__, __func__, __LINE__, \
204 0, _level, patchset); \
205 } \
206 break; \
207 } \
208 } while(0)
209
210 void pcmk__log_xml_changes_as(const char *file, const char *function,
211 uint32_t line, uint32_t tags, uint8_t level,
212 const xmlNode *xml);
213
214 void pcmk__log_xml_patchset_as(const char *file, const char *function,
215 uint32_t line, uint32_t tags, uint8_t level,
216 const xmlNode *patchset);
217
218 /*!
219 * \internal
220 * \brief Initialize logging for command line tools
221 *
222 * \param[in] name The name of the program
223 * \param[in] verbosity How verbose to be in logging
224 *
225 * \note \p verbosity is not the same as the logging level (LOG_ERR, etc.).
226 */
227 void pcmk__cli_init_logging(const char *name, unsigned int verbosity);
228
229 int pcmk__add_logfile(const char *filename);
230 void pcmk__add_logfiles(gchar **log_files, pcmk__output_t *out);
231
232 void pcmk__free_common_logger(void);
233
234 #ifdef __cplusplus
235 }
236 #endif
237
238 #endif // PCMK__CRM_COMMON_LOGGING_INTERNAL__H