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

Generated by: LCOV version 1.14