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

Generated by: LCOV version 2.0-1