LCOV - code coverage report
Current view: top level - src/common - logging.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 70.2 % 131 92
Test Date: 2026-03-12 06:14:44 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  * Logging framework for frontend programs
       3              :  *
       4              :  * Copyright (c) 2018-2026, 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        14891 : pg_logging_init(const char *argv0)
      84              : {
      85        14891 :     const char *pg_color_env = getenv("PG_COLOR");
      86        14891 :     bool        log_color = false;
      87        14891 :     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        14891 :     setvbuf(stderr, NULL, _IONBF, 0);
     101              : 
     102        14891 :     progname = get_progname(argv0);
     103        14891 :     __pg_log_level = PG_LOG_INFO;
     104              : 
     105        14891 :     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        14891 :     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              :                 char       *token;
     123            0 :                 char       *cp = colors;
     124              : 
     125            0 :                 while ((token = strsep(&cp, ":")))
     126              :                 {
     127            0 :                     char       *e = strchr(token, '=');
     128              : 
     129            0 :                     if (e)
     130              :                     {
     131              :                         char       *name;
     132              :                         char       *value;
     133              : 
     134            0 :                         *e = '\0';
     135            0 :                         name = token;
     136            0 :                         value = e + 1;
     137              : 
     138            0 :                         if (strcmp(name, "error") == 0)
     139            0 :                             sgr_error = strdup(value);
     140            0 :                         if (strcmp(name, "warning") == 0)
     141            0 :                             sgr_warning = strdup(value);
     142            0 :                         if (strcmp(name, "note") == 0)
     143            0 :                             sgr_note = strdup(value);
     144            0 :                         if (strcmp(name, "locus") == 0)
     145            0 :                             sgr_locus = strdup(value);
     146              :                     }
     147              :                 }
     148              : 
     149            0 :                 free(colors);
     150              :             }
     151              :         }
     152              :         else
     153              :         {
     154            0 :             sgr_error = SGR_ERROR_DEFAULT;
     155            0 :             sgr_warning = SGR_WARNING_DEFAULT;
     156            0 :             sgr_note = SGR_NOTE_DEFAULT;
     157            0 :             sgr_locus = SGR_LOCUS_DEFAULT;
     158              :         }
     159              :     }
     160        14891 : }
     161              : 
     162              : /*
     163              :  * Change the logging flags.
     164              :  */
     165              : void
     166        19242 : pg_logging_config(int new_flags)
     167              : {
     168        19242 :     log_flags = new_flags;
     169        19242 : }
     170              : 
     171              : /*
     172              :  * pg_logging_init sets the default log level to INFO.  Programs that prefer
     173              :  * a different default should use this to set it, immediately afterward.
     174              :  */
     175              : void
     176          619 : pg_logging_set_level(enum pg_log_level new_level)
     177              : {
     178          619 :     __pg_log_level = new_level;
     179          619 : }
     180              : 
     181              : /*
     182              :  * Command line switches such as --verbose should invoke this.
     183              :  */
     184              : void
     185           94 : pg_logging_increase_verbosity(void)
     186              : {
     187              :     /*
     188              :      * The enum values are chosen such that we have to decrease __pg_log_level
     189              :      * in order to become more verbose.
     190              :      */
     191           94 :     if (__pg_log_level > PG_LOG_NOTSET + 1)
     192           94 :         __pg_log_level--;
     193           94 : }
     194              : 
     195              : void
     196         9889 : pg_logging_set_pre_callback(void (*cb) (void))
     197              : {
     198         9889 :     log_pre_callback = cb;
     199         9889 : }
     200              : 
     201              : void
     202         9889 : pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
     203              : {
     204         9889 :     log_locus_callback = cb;
     205         9889 : }
     206              : 
     207              : void
     208       182222 : pg_log_generic(enum pg_log_level level, enum pg_log_part part,
     209              :                const char *pg_restrict fmt,...)
     210              : {
     211              :     va_list     ap;
     212              : 
     213       182222 :     va_start(ap, fmt);
     214       182222 :     pg_log_generic_v(level, part, fmt, ap);
     215       182222 :     va_end(ap);
     216       182222 : }
     217              : 
     218              : void
     219       183280 : pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
     220              :                  const char *pg_restrict fmt, va_list ap)
     221              : {
     222       183280 :     int         save_errno = errno;
     223       183280 :     const char *filename = NULL;
     224       183280 :     uint64      lineno = 0;
     225              :     va_list     ap2;
     226              :     size_t      required_len;
     227              :     char       *buf;
     228              : 
     229              :     Assert(progname);
     230              :     Assert(level);
     231              :     Assert(fmt);
     232              :     Assert(fmt[strlen(fmt) - 1] != '\n');
     233              : 
     234              :     /* Do nothing if log level is too low. */
     235       183280 :     if (level < __pg_log_level)
     236        53829 :         return;
     237              : 
     238              :     /*
     239              :      * Flush stdout before output to stderr, to ensure sync even when stdout
     240              :      * is buffered.
     241              :      */
     242       129451 :     fflush(stdout);
     243              : 
     244       129451 :     if (log_pre_callback)
     245       101445 :         log_pre_callback();
     246              : 
     247       129451 :     if (log_locus_callback)
     248       101445 :         log_locus_callback(&filename, &lineno);
     249              : 
     250       129451 :     fmt = _(fmt);
     251              : 
     252       129451 :     if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
     253              :     {
     254        95570 :         if (sgr_locus)
     255            0 :             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
     256        95570 :         if (!(log_flags & PG_LOG_FLAG_TERSE))
     257        95570 :             fprintf(stderr, "%s:", progname);
     258        95570 :         if (filename)
     259              :         {
     260        67380 :             fprintf(stderr, "%s:", filename);
     261        67380 :             if (lineno > 0)
     262        67380 :                 fprintf(stderr, UINT64_FORMAT ":", lineno);
     263              :         }
     264        95570 :         fprintf(stderr, " ");
     265        95570 :         if (sgr_locus)
     266            0 :             fprintf(stderr, ANSI_ESCAPE_RESET);
     267              :     }
     268              : 
     269       129451 :     if (!(log_flags & PG_LOG_FLAG_TERSE))
     270              :     {
     271        95570 :         switch (part)
     272              :         {
     273        95416 :             case PG_LOG_PRIMARY:
     274        95416 :                 switch (level)
     275              :                 {
     276         1899 :                     case PG_LOG_ERROR:
     277         1899 :                         if (sgr_error)
     278            0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
     279         1899 :                         fprintf(stderr, _("error: "));
     280         1899 :                         if (sgr_error)
     281            0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     282         1899 :                         break;
     283          140 :                     case PG_LOG_WARNING:
     284          140 :                         if (sgr_warning)
     285            0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
     286          140 :                         fprintf(stderr, _("warning: "));
     287          140 :                         if (sgr_warning)
     288            0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     289          140 :                         break;
     290        93377 :                     default:
     291        93377 :                         break;
     292              :                 }
     293        95416 :                 break;
     294           18 :             case PG_LOG_DETAIL:
     295           18 :                 if (sgr_note)
     296            0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     297           18 :                 fprintf(stderr, _("detail: "));
     298           18 :                 if (sgr_note)
     299            0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     300           18 :                 break;
     301          136 :             case PG_LOG_HINT:
     302          136 :                 if (sgr_note)
     303            0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     304          136 :                 fprintf(stderr, _("hint: "));
     305          136 :                 if (sgr_note)
     306            0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     307          136 :                 break;
     308              :         }
     309              :     }
     310              : 
     311       129451 :     errno = save_errno;
     312              : 
     313       129451 :     va_copy(ap2, ap);
     314       129451 :     required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
     315       129451 :     va_end(ap2);
     316              : 
     317       129451 :     buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
     318              : 
     319       129451 :     errno = save_errno;         /* malloc might change errno */
     320              : 
     321       129451 :     if (!buf)
     322              :     {
     323              :         /* memory trouble, just print what we can and get out of here */
     324            0 :         vfprintf(stderr, fmt, ap);
     325            0 :         return;
     326              :     }
     327              : 
     328       129451 :     vsnprintf(buf, required_len, fmt, ap);
     329              : 
     330              :     /* strip one newline, for PQerrorMessage() */
     331       129451 :     if (required_len >= 2 && buf[required_len - 2] == '\n')
     332       100861 :         buf[required_len - 2] = '\0';
     333              : 
     334       129451 :     fprintf(stderr, "%s\n", buf);
     335              : 
     336       129451 :     free(buf);
     337              : }
        

Generated by: LCOV version 2.0-1