pacemaker 2.1.7-2.1.7
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
output_log.c
Go to the documentation of this file.
1/*
2 * Copyright 2019-2023 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 Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
12
13#include <ctype.h>
14#include <stdarg.h>
15#include <stdint.h>
16#include <stdlib.h>
17#include <stdio.h>
18
19GOptionEntry pcmk__log_output_entries[] = {
20 { NULL }
21};
22
23typedef struct private_data_s {
24 /* gathered in log_begin_list */
25 GQueue/*<char*>*/ *prefixes;
26 uint8_t log_level;
27 const char *function;
28 const char *file;
29 uint32_t line;
30 uint32_t tags;
32
41#define logger(priv, fmt, args...) do { \
42 qb_log_from_external_source(pcmk__s((priv)->function, __func__), \
43 pcmk__s((priv)->file, __FILE__), fmt, (priv)->log_level, \
44 (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
45 ##args); \
46 } while (0);
47
57#define logger_va(priv, level, fmt, ap) do { \
58 qb_log_from_external_source_va(pcmk__s((priv)->function, __func__), \
59 pcmk__s((priv)->file, __FILE__), fmt, level, \
60 (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
61 ap); \
62 } while (0);
63
64static void
65log_subprocess_output(pcmk__output_t *out, int exit_status,
66 const char *proc_stdout, const char *proc_stderr) {
67 /* This function intentionally left blank */
68}
69
70static void
71log_free_priv(pcmk__output_t *out) {
72 private_data_t *priv = NULL;
73
74 if (out == NULL || out->priv == NULL) {
75 return;
76 }
77
78 priv = out->priv;
79
80 g_queue_free(priv->prefixes);
81 free(priv);
82 out->priv = NULL;
83}
84
85static bool
86log_init(pcmk__output_t *out) {
87 private_data_t *priv = NULL;
88
89 CRM_ASSERT(out != NULL);
90
91 /* If log_init was previously called on this output struct, just return. */
92 if (out->priv != NULL) {
93 return true;
94 }
95
96 out->priv = calloc(1, sizeof(private_data_t));
97 if (out->priv == NULL) {
98 return false;
99 }
100
101 priv = out->priv;
102
103 priv->prefixes = g_queue_new();
104 priv->log_level = LOG_INFO;
105
106 return true;
107}
108
109static void
110log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
111 /* This function intentionally left blank */
112}
113
114static void
115log_reset(pcmk__output_t *out) {
116 CRM_ASSERT(out != NULL);
117
118 out->dest = freopen(NULL, "w", out->dest);
119 CRM_ASSERT(out->dest != NULL);
120
121 log_free_priv(out);
122 log_init(out);
123}
124
125static void
126log_version(pcmk__output_t *out, bool extended) {
127 private_data_t *priv = NULL;
128
129 CRM_ASSERT(out != NULL && out->priv != NULL);
130 priv = out->priv;
131
132 if (extended) {
133 logger(priv, "Pacemaker %s (Build: %s): %s",
135 } else {
136 logger(priv, "Pacemaker " PACEMAKER_VERSION);
137 logger(priv, "Written by Andrew Beekhof and "
138 "the Pacemaker project contributors");
139 }
140}
141
142G_GNUC_PRINTF(2, 3)
143static void
144log_err(pcmk__output_t *out, const char *format, ...)
145{
146 va_list ap;
147 private_data_t *priv = NULL;
148
149 CRM_ASSERT((out != NULL) && (out->priv != NULL));
150 priv = out->priv;
151
152 /* Error output does not get indented, to separate it from other
153 * potentially indented list output.
154 */
155 va_start(ap, format);
156 logger_va(priv, LOG_ERR, format, ap);
157 va_end(ap);
158}
159
160static void
161log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
162 xmlNodePtr node = NULL;
163 private_data_t *priv = NULL;
164
165 CRM_ASSERT(out != NULL && out->priv != NULL);
166 priv = out->priv;
167
168 node = create_xml_node(NULL, name);
169 xmlNodeSetContent(node, (pcmkXmlStr) buf);
170 do_crm_log_xml(priv->log_level, name, node);
171 free(node);
172}
173
174G_GNUC_PRINTF(4, 5)
175static void
176log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
177 const char *format, ...) {
178 int len = 0;
179 va_list ap;
180 char* buffer = NULL;
181 private_data_t *priv = NULL;
182
183 CRM_ASSERT(out != NULL && out->priv != NULL);
184 priv = out->priv;
185
186 va_start(ap, format);
187 len = vasprintf(&buffer, format, ap);
188 CRM_ASSERT(len >= 0);
189 va_end(ap);
190
191 /* Don't skip empty prefixes,
192 * otherwise there will be mismatch
193 * in the log_end_list */
194 if(strcmp(buffer, "") == 0) {
195 /* nothing */
196 }
197
198 g_queue_push_tail(priv->prefixes, buffer);
199}
200
201G_GNUC_PRINTF(3, 4)
202static void
203log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
204 int len = 0;
205 va_list ap;
206 private_data_t *priv = NULL;
207 char prefix[LINE_MAX] = { 0 };
208 int offset = 0;
209 char* buffer = NULL;
210
211 CRM_ASSERT(out != NULL && out->priv != NULL);
212 priv = out->priv;
213
214 for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
215 if (strcmp(prefix, "") != 0) {
216 offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
217 } else {
218 offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
219 }
220 }
221
222 va_start(ap, format);
223 len = vasprintf(&buffer, format, ap);
224 CRM_ASSERT(len >= 0);
225 va_end(ap);
226
227 if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
228 if ((name != NULL) && (strcmp(name, "") != 0)) {
229 if (strcmp(prefix, "") != 0) {
230 logger(priv, "%s: %s: %s", prefix, name, buffer);
231 } else {
232 logger(priv, "%s: %s", name, buffer);
233 }
234 } else {
235 if (strcmp(prefix, "") != 0) {
236 logger(priv, "%s: %s", prefix, buffer);
237 } else {
238 logger(priv, "%s", buffer);
239 }
240 }
241 }
242 free(buffer);
243}
244
245static void
246log_end_list(pcmk__output_t *out) {
247 private_data_t *priv = NULL;
248
249 CRM_ASSERT(out != NULL && out->priv != NULL);
250 priv = out->priv;
251
252 if (priv->prefixes == NULL) {
253 return;
254 }
255 CRM_ASSERT(priv->prefixes->tail != NULL);
256
257 free((char *)priv->prefixes->tail->data);
258 g_queue_pop_tail(priv->prefixes);
259}
260
261G_GNUC_PRINTF(2, 3)
262static int
263log_info(pcmk__output_t *out, const char *format, ...)
264{
265 va_list ap;
266 private_data_t *priv = NULL;
267
268 CRM_ASSERT(out != NULL && out->priv != NULL);
269 priv = out->priv;
270
271 /* Informational output does not get indented, to separate it from other
272 * potentially indented list output.
273 */
274 va_start(ap, format);
275 logger_va(priv, priv->log_level, format, ap);
276 va_end(ap);
277
278 return pcmk_rc_ok;
279}
280
281G_GNUC_PRINTF(2, 3)
282static int
283log_transient(pcmk__output_t *out, const char *format, ...)
284{
285 va_list ap;
286 private_data_t *priv = NULL;
287
288 CRM_ASSERT(out != NULL && out->priv != NULL);
289 priv = out->priv;
290
291 va_start(ap, format);
292 logger_va(priv, QB_MAX(priv->log_level, LOG_DEBUG), format, ap);
293 va_end(ap);
294
295 return pcmk_rc_ok;
296}
297
298static bool
299log_is_quiet(pcmk__output_t *out) {
300 return false;
301}
302
303static void
304log_spacer(pcmk__output_t *out) {
305 /* This function intentionally left blank */
306}
307
308static void
309log_progress(pcmk__output_t *out, bool end) {
310 /* This function intentionally left blank */
311}
312
313static void
314log_prompt(const char *prompt, bool echo, char **dest) {
315 /* This function intentionally left blank */
316}
317
320 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
321
322 if (retval == NULL) {
323 return NULL;
324 }
325
326 retval->fmt_name = "log";
327 retval->request = pcmk__quote_cmdline(argv);
328
329 retval->init = log_init;
330 retval->free_priv = log_free_priv;
331 retval->finish = log_finish;
332 retval->reset = log_reset;
333
335 retval->message = pcmk__call_message;
336
337 retval->subprocess_output = log_subprocess_output;
338 retval->version = log_version;
339 retval->info = log_info;
340 retval->transient = log_transient;
341 retval->err = log_err;
342 retval->output_xml = log_output_xml;
343
344 retval->begin_list = log_begin_list;
345 retval->list_item = log_list_item;
346 retval->end_list = log_end_list;
347
348 retval->is_quiet = log_is_quiet;
349 retval->spacer = log_spacer;
350 retval->progress = log_progress;
351 retval->prompt = log_prompt;
352
353 return retval;
354}
355
356uint8_t
358{
359 private_data_t *priv = NULL;
360
361 CRM_ASSERT((out != NULL) && (out->priv != NULL));
362 CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return 0);
363
364 priv = out->priv;
365 return priv->log_level;
366}
367
368void
370 private_data_t *priv = NULL;
371
372 CRM_ASSERT(out != NULL && out->priv != NULL);
373 CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return);
374
375 priv = out->priv;
376 priv->log_level = log_level;
377}
378
393void
395 const char *function, uint32_t line, uint32_t tags)
396{
397 private_data_t *priv = NULL;
398
399 CRM_ASSERT((out != NULL) && (out->priv != NULL));
400 CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return);
401
402 priv = out->priv;
403 priv->file = file;
404 priv->function = function;
405 priv->line = line;
406 priv->tags = tags;
407}
const char * name
Definition cib.c:26
gchar * pcmk__quote_cmdline(gchar **argv)
Definition cmdline.c:163
#define PACEMAKER_VERSION
Definition config.h:514
#define CRM_FEATURES
Definition config.h:33
#define BUILD_VERSION
Definition config.h:8
#define do_crm_log_xml(level, text, xml)
Log XML line-by-line in a formatted fashion.
Definition logging.h:261
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
struct private_data_s private_data_t
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition output.c:166
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition output.c:188
#define logger(priv, fmt, args...)
Definition output_log.c:41
GOptionEntry pcmk__log_output_entries[]
Definition output_log.c:19
void pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
Definition output_log.c:369
pcmk__output_t * pcmk__mk_log_output(char **argv)
Definition output_log.c:319
struct private_data_s private_data_t
#define logger_va(priv, level, fmt, ap)
Definition output_log.c:57
void pcmk__output_set_log_filter(pcmk__output_t *out, const char *file, const char *function, uint32_t line, uint32_t tags)
Definition output_log.c:394
uint8_t pcmk__output_get_log_level(const pcmk__output_t *out)
Definition output_log.c:357
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:154
enum crm_exit_e crm_exit_t
@ pcmk__str_none
This structure contains everything that makes up a single output formatter.
void(* end_list)(pcmk__output_t *out)
void(* version)(pcmk__output_t *out, bool extended)
int(* message)(pcmk__output_t *out, const char *message_id,...)
bool(* is_quiet)(pcmk__output_t *out)
const char * fmt_name
The name of this output formatter.
FILE * dest
Where output should be written.
void(* register_message)(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
int(*) int(* transient)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
void(* prompt)(const char *prompt, bool echo, char **dest)
int(*) int(*) void(*) void(* output_xml)(pcmk__output_t *out, const char *name, const char *buf)
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
bool(* init)(pcmk__output_t *out)
void * priv
Implementation-specific private data.
void(* spacer)(pcmk__output_t *out)
void(* progress)(pcmk__output_t *out, bool end)
void(* reset)(pcmk__output_t *out)
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void(* free_priv)(pcmk__output_t *out)
gchar * request
A copy of the request that generated this output.
int(*) int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
const xmlChar * pcmkXmlStr
Definition xml.h:50
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:638