root/lib/common/output_log.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. log_subprocess_output
  2. log_free_priv
  3. log_init
  4. log_finish
  5. log_reset
  6. log_version
  7. G_GNUC_PRINTF
  8. log_output_xml
  9. G_GNUC_PRINTF
  10. G_GNUC_PRINTF
  11. log_end_list
  12. G_GNUC_PRINTF
  13. G_GNUC_PRINTF
  14. log_is_quiet
  15. log_spacer
  16. log_progress
  17. log_prompt
  18. pcmk__mk_log_output
  19. pcmk__output_get_log_level
  20. pcmk__output_set_log_level
  21. pcmk__output_set_log_filter

   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>
  11 #include <crm/common/cmdline_internal.h>
  12 
  13 #include <ctype.h>
  14 #include <stdarg.h>
  15 #include <stdint.h>
  16 #include <stdlib.h>
  17 #include <stdio.h>
  18 
  19 GOptionEntry pcmk__log_output_entries[] = {
  20     { NULL }
  21 };
  22 
  23 typedef 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;
  31 } private_data_t;
  32 
  33 /*!
  34  * \internal
  35  * \brief Log a message using output object's log level and filters
  36  *
  37  * \param[in] priv    Output object's private_data_t
  38  * \param[in] fmt     printf(3)-style format string
  39  * \param[in] args... Format string arguments
  40  */
  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 
  48 /*!
  49  * \internal
  50  * \brief Log a message using an explicit log level and output object's filters
  51  *
  52  * \param[in] priv    Output object's private_data_t
  53  * \param[in] level   Log level
  54  * \param[in] fmt     printf(3)-style format string
  55  * \param[in] ap      Variadic arguments
  56  */
  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 
  64 static void
  65 log_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
  66                       const char *proc_stdout, const char *proc_stderr) {
  67     /* This function intentionally left blank */
  68 }
  69 
  70 static void
  71 log_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  85 static bool
  86 log_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
 109 static void
 110 log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
 111     /* This function intentionally left blank */
 112 }
 113 
 114 static void
 115 log_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 125 static void
 126 log_version(pcmk__output_t *out, bool extended) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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",
 134                PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
 135     } else {
 136         logger(priv, "Pacemaker " PACEMAKER_VERSION);
 137         logger(priv, "Written by Andrew Beekhof and "
 138                      "the Pacemaker project contributors");
 139     }
 140 }
 141 
 142 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 static void
 144 log_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 
 160 static void
 161 log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 174 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 static void
 176 log_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 
 201 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 202 static void
 203 log_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 
 245 static void
 246 log_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 261 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 262 static int
 263 log_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 
 281 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 static int
 283 log_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 
 298 static bool
 299 log_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 300     return false;
 301 }
 302 
 303 static void
 304 log_spacer(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 305     /* This function intentionally left blank */
 306 }
 307 
 308 static void
 309 log_progress(pcmk__output_t *out, bool end) {
     /* [previous][next][first][last][top][bottom][index][help] */
 310     /* This function intentionally left blank */
 311 }
 312 
 313 static void
 314 log_prompt(const char *prompt, bool echo, char **dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
 315     /* This function intentionally left blank */
 316 }
 317 
 318 pcmk__output_t *
 319 pcmk__mk_log_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 334     retval->register_message = pcmk__register_message;
 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 
 356 uint8_t
 357 pcmk__output_get_log_level(const pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 368 void
 369 pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 379 /*!
 380  * \internal
 381  * \brief Set the file, function, line, and tags used to filter log output
 382  *
 383  * \param[in,out] out       Logger output object
 384  * \param[in]     file      File name to filter with (or NULL for default)
 385  * \param[in]     function  Function name to filter with (or NULL for default)
 386  * \param[in]     line      Line number to filter with (or 0 for default)
 387  * \param[in]     tags      Tags to filter with (or 0 for none)
 388  *
 389  * \note Custom filters should generally be used only in short areas of a single
 390  *       function. When done, callers should call this function again with
 391  *       NULL/0 arguments to reset the filters.
 392  */
 393 void
 394 pcmk__output_set_log_filter(pcmk__output_t *out, const char *file,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */