LCOV - code coverage report
Current view: top level - src/common - logging.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 92 130 70.8 %
Date: 2024-04-19 15:12:22 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * Logging framework for frontend programs
       3             :  *
       4             :  * Copyright (c) 2018-2024, PostgreSQL Global Development Group
       5             :  *
       6             :  * src/common/logging.c
       7             :  *
       8             :  *-------------------------------------------------------------------------
       9             :  */
      10             : 
      11             : #ifndef FRONTEND
      12             : #error "This file is not expected to be compiled for backend code"
      13             : #endif
      14             : 
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include <unistd.h>
      18             : 
      19             : #include "common/logging.h"
      20             : 
      21             : enum pg_log_level __pg_log_level;
      22             : 
      23             : static const char *progname;
      24             : static int  log_flags;
      25             : 
      26             : static void (*log_pre_callback) (void);
      27             : static void (*log_locus_callback) (const char **, uint64 *);
      28             : 
      29             : static const char *sgr_error = NULL;
      30             : static const char *sgr_warning = NULL;
      31             : static const char *sgr_note = NULL;
      32             : static const char *sgr_locus = NULL;
      33             : 
      34             : #define SGR_ERROR_DEFAULT "01;31"
      35             : #define SGR_WARNING_DEFAULT "01;35"
      36             : #define SGR_NOTE_DEFAULT "01;36"
      37             : #define SGR_LOCUS_DEFAULT "01"
      38             : 
      39             : #define ANSI_ESCAPE_FMT "\x1b[%sm"
      40             : #define ANSI_ESCAPE_RESET "\x1b[0m"
      41             : 
      42             : #ifdef WIN32
      43             : 
      44             : #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
      45             : #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
      46             : #endif
      47             : 
      48             : /*
      49             :  * Attempt to enable VT100 sequence processing for colorization on Windows.
      50             :  * If current environment is not VT100-compatible or if this mode could not
      51             :  * be enabled, return false.
      52             :  */
      53             : static bool
      54             : enable_vt_processing(void)
      55             : {
      56             :     /* Check stderr */
      57             :     HANDLE      hOut = GetStdHandle(STD_ERROR_HANDLE);
      58             :     DWORD       dwMode = 0;
      59             : 
      60             :     if (hOut == INVALID_HANDLE_VALUE)
      61             :         return false;
      62             : 
      63             :     /*
      64             :      * Look for the current console settings and check if VT100 is already
      65             :      * enabled.
      66             :      */
      67             :     if (!GetConsoleMode(hOut, &dwMode))
      68             :         return false;
      69             :     if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
      70             :         return true;
      71             : 
      72             :     dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
      73             :     if (!SetConsoleMode(hOut, dwMode))
      74             :         return false;
      75             :     return true;
      76             : }
      77             : #endif                          /* WIN32 */
      78             : 
      79             : /*
      80             :  * This should be called before any output happens.
      81             :  */
      82             : void
      83       21592 : pg_logging_init(const char *argv0)
      84             : {
      85       21592 :     const char *pg_color_env = getenv("PG_COLOR");
      86       21592 :     bool        log_color = false;
      87       21592 :     bool        color_terminal = isatty(fileno(stderr));
      88             : 
      89             : #ifdef WIN32
      90             : 
      91             :     /*
      92             :      * On Windows, check if environment is VT100-compatible if using a
      93             :      * terminal.
      94             :      */
      95             :     if (color_terminal)
      96             :         color_terminal = enable_vt_processing();
      97             : #endif
      98             : 
      99             :     /* usually the default, but not on Windows */
     100       21592 :     setvbuf(stderr, NULL, _IONBF, 0);
     101             : 
     102       21592 :     progname = get_progname(argv0);
     103       21592 :     __pg_log_level = PG_LOG_INFO;
     104             : 
     105       21592 :     if (pg_color_env)
     106             :     {
     107           0 :         if (strcmp(pg_color_env, "always") == 0 ||
     108           0 :             (strcmp(pg_color_env, "auto") == 0 && color_terminal))
     109           0 :             log_color = true;
     110             :     }
     111             : 
     112       21592 :     if (log_color)
     113             :     {
     114           0 :         const char *pg_colors_env = getenv("PG_COLORS");
     115             : 
     116           0 :         if (pg_colors_env)
     117             :         {
     118           0 :             char       *colors = strdup(pg_colors_env);
     119             : 
     120           0 :             if (colors)
     121             :             {
     122           0 :                 for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":"))
     123             :                 {
     124           0 :                     char       *e = strchr(token, '=');
     125             : 
     126           0 :                     if (e)
     127             :                     {
     128             :                         char       *name;
     129             :                         char       *value;
     130             : 
     131           0 :                         *e = '\0';
     132           0 :                         name = token;
     133           0 :                         value = e + 1;
     134             : 
     135           0 :                         if (strcmp(name, "error") == 0)
     136           0 :                             sgr_error = strdup(value);
     137           0 :                         if (strcmp(name, "warning") == 0)
     138           0 :                             sgr_warning = strdup(value);
     139           0 :                         if (strcmp(name, "note") == 0)
     140           0 :                             sgr_note = strdup(value);
     141           0 :                         if (strcmp(name, "locus") == 0)
     142           0 :                             sgr_locus = strdup(value);
     143             :                     }
     144             :                 }
     145             : 
     146           0 :                 free(colors);
     147             :             }
     148             :         }
     149             :         else
     150             :         {
     151           0 :             sgr_error = SGR_ERROR_DEFAULT;
     152           0 :             sgr_warning = SGR_WARNING_DEFAULT;
     153           0 :             sgr_note = SGR_NOTE_DEFAULT;
     154           0 :             sgr_locus = SGR_LOCUS_DEFAULT;
     155             :         }
     156             :     }
     157       21592 : }
     158             : 
     159             : /*
     160             :  * Change the logging flags.
     161             :  */
     162             : void
     163       28418 : pg_logging_config(int new_flags)
     164             : {
     165       28418 :     log_flags = new_flags;
     166       28418 : }
     167             : 
     168             : /*
     169             :  * pg_logging_init sets the default log level to INFO.  Programs that prefer
     170             :  * a different default should use this to set it, immediately afterward.
     171             :  */
     172             : void
     173         746 : pg_logging_set_level(enum pg_log_level new_level)
     174             : {
     175         746 :     __pg_log_level = new_level;
     176         746 : }
     177             : 
     178             : /*
     179             :  * Command line switches such as --verbose should invoke this.
     180             :  */
     181             : void
     182         132 : pg_logging_increase_verbosity(void)
     183             : {
     184             :     /*
     185             :      * The enum values are chosen such that we have to decrease __pg_log_level
     186             :      * in order to become more verbose.
     187             :      */
     188         132 :     if (__pg_log_level > PG_LOG_NOTSET + 1)
     189         132 :         __pg_log_level--;
     190         132 : }
     191             : 
     192             : void
     193       14606 : pg_logging_set_pre_callback(void (*cb) (void))
     194             : {
     195       14606 :     log_pre_callback = cb;
     196       14606 : }
     197             : 
     198             : void
     199       14606 : pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
     200             : {
     201       14606 :     log_locus_callback = cb;
     202       14606 : }
     203             : 
     204             : void
     205      174564 : pg_log_generic(enum pg_log_level level, enum pg_log_part part,
     206             :                const char *pg_restrict fmt,...)
     207             : {
     208             :     va_list     ap;
     209             : 
     210      174564 :     va_start(ap, fmt);
     211      174564 :     pg_log_generic_v(level, part, fmt, ap);
     212      174564 :     va_end(ap);
     213      174564 : }
     214             : 
     215             : void
     216      174674 : pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
     217             :                  const char *pg_restrict fmt, va_list ap)
     218             : {
     219      174674 :     int         save_errno = errno;
     220      174674 :     const char *filename = NULL;
     221      174674 :     uint64      lineno = 0;
     222             :     va_list     ap2;
     223             :     size_t      required_len;
     224             :     char       *buf;
     225             : 
     226             :     Assert(progname);
     227             :     Assert(level);
     228             :     Assert(fmt);
     229             :     Assert(fmt[strlen(fmt) - 1] != '\n');
     230             : 
     231             :     /* Do nothing if log level is too low. */
     232      174674 :     if (level < __pg_log_level)
     233       69104 :         return;
     234             : 
     235             :     /*
     236             :      * Flush stdout before output to stderr, to ensure sync even when stdout
     237             :      * is buffered.
     238             :      */
     239      105570 :     fflush(stdout);
     240             : 
     241      105570 :     if (log_pre_callback)
     242       60628 :         log_pre_callback();
     243             : 
     244      105570 :     if (log_locus_callback)
     245       60628 :         log_locus_callback(&filename, &lineno);
     246             : 
     247      105570 :     fmt = _(fmt);
     248             : 
     249      105570 :     if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
     250             :     {
     251       45998 :         if (sgr_locus)
     252           0 :             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
     253       45998 :         if (!(log_flags & PG_LOG_FLAG_TERSE))
     254       45998 :             fprintf(stderr, "%s:", progname);
     255       45998 :         if (filename)
     256             :         {
     257         806 :             fprintf(stderr, "%s:", filename);
     258         806 :             if (lineno > 0)
     259         806 :                 fprintf(stderr, UINT64_FORMAT ":", lineno);
     260             :         }
     261       45998 :         fprintf(stderr, " ");
     262       45998 :         if (sgr_locus)
     263           0 :             fprintf(stderr, ANSI_ESCAPE_RESET);
     264             :     }
     265             : 
     266      105570 :     if (!(log_flags & PG_LOG_FLAG_TERSE))
     267             :     {
     268       45998 :         switch (part)
     269             :         {
     270       45702 :             case PG_LOG_PRIMARY:
     271             :                 switch (level)
     272             :                 {
     273        1544 :                     case PG_LOG_ERROR:
     274        1544 :                         if (sgr_error)
     275           0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
     276        1544 :                         fprintf(stderr, _("error: "));
     277        1544 :                         if (sgr_error)
     278           0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     279        1544 :                         break;
     280         176 :                     case PG_LOG_WARNING:
     281         176 :                         if (sgr_warning)
     282           0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
     283         176 :                         fprintf(stderr, _("warning: "));
     284         176 :                         if (sgr_warning)
     285           0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     286         176 :                         break;
     287       43982 :                     default:
     288       43982 :                         break;
     289             :                 }
     290       45702 :                 break;
     291          30 :             case PG_LOG_DETAIL:
     292          30 :                 if (sgr_note)
     293           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     294          30 :                 fprintf(stderr, _("detail: "));
     295          30 :                 if (sgr_note)
     296           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     297          30 :                 break;
     298         266 :             case PG_LOG_HINT:
     299         266 :                 if (sgr_note)
     300           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     301         266 :                 fprintf(stderr, _("hint: "));
     302         266 :                 if (sgr_note)
     303           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     304         266 :                 break;
     305             :         }
     306       59572 :     }
     307             : 
     308      105570 :     errno = save_errno;
     309             : 
     310      105570 :     va_copy(ap2, ap);
     311      105570 :     required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
     312      105570 :     va_end(ap2);
     313             : 
     314      105570 :     buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
     315             : 
     316      105570 :     errno = save_errno;         /* malloc might change errno */
     317             : 
     318      105570 :     if (!buf)
     319             :     {
     320             :         /* memory trouble, just print what we can and get out of here */
     321           0 :         vfprintf(stderr, fmt, ap);
     322           0 :         return;
     323             :     }
     324             : 
     325      105570 :     vsnprintf(buf, required_len, fmt, ap);
     326             : 
     327             :     /* strip one newline, for PQerrorMessage() */
     328      105570 :     if (required_len >= 2 && buf[required_len - 2] == '\n')
     329       59622 :         buf[required_len - 2] = '\0';
     330             : 
     331      105570 :     fprintf(stderr, "%s\n", buf);
     332             : 
     333      105570 :     free(buf);
     334             : }

Generated by: LCOV version 1.14