LCOV - code coverage report
Current view: top level - src/common - logging.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 92 131 70.2 %
Date: 2024-11-21 08:14:44 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       27954 : pg_logging_init(const char *argv0)
      84             : {
      85       27954 :     const char *pg_color_env = getenv("PG_COLOR");
      86       27954 :     bool        log_color = false;
      87       27954 :     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       27954 :     setvbuf(stderr, NULL, _IONBF, 0);
     101             : 
     102       27954 :     progname = get_progname(argv0);
     103       27954 :     __pg_log_level = PG_LOG_INFO;
     104             : 
     105       27954 :     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       27954 :     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       27954 : }
     161             : 
     162             : /*
     163             :  * Change the logging flags.
     164             :  */
     165             : void
     166       36440 : pg_logging_config(int new_flags)
     167             : {
     168       36440 :     log_flags = new_flags;
     169       36440 : }
     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         750 : pg_logging_set_level(enum pg_log_level new_level)
     177             : {
     178         750 :     __pg_log_level = new_level;
     179         750 : }
     180             : 
     181             : /*
     182             :  * Command line switches such as --verbose should invoke this.
     183             :  */
     184             : void
     185         136 : 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         136 :     if (__pg_log_level > PG_LOG_NOTSET + 1)
     192         136 :         __pg_log_level--;
     193         136 : }
     194             : 
     195             : void
     196       18686 : pg_logging_set_pre_callback(void (*cb) (void))
     197             : {
     198       18686 :     log_pre_callback = cb;
     199       18686 : }
     200             : 
     201             : void
     202       18686 : pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
     203             : {
     204       18686 :     log_locus_callback = cb;
     205       18686 : }
     206             : 
     207             : void
     208      329492 : 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      329492 :     va_start(ap, fmt);
     214      329492 :     pg_log_generic_v(level, part, fmt, ap);
     215      329492 :     va_end(ap);
     216      329492 : }
     217             : 
     218             : void
     219      331610 : 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      331610 :     int         save_errno = errno;
     223      331610 :     const char *filename = NULL;
     224      331610 :     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      331610 :     if (level < __pg_log_level)
     236       86944 :         return;
     237             : 
     238             :     /*
     239             :      * Flush stdout before output to stderr, to ensure sync even when stdout
     240             :      * is buffered.
     241             :      */
     242      244666 :     fflush(stdout);
     243             : 
     244      244666 :     if (log_pre_callback)
     245      194790 :         log_pre_callback();
     246             : 
     247      244666 :     if (log_locus_callback)
     248      194790 :         log_locus_callback(&filename, &lineno);
     249             : 
     250      244666 :     fmt = _(fmt);
     251             : 
     252      244666 :     if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
     253             :     {
     254      183806 :         if (sgr_locus)
     255           0 :             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
     256      183806 :         if (!(log_flags & PG_LOG_FLAG_TERSE))
     257      183806 :             fprintf(stderr, "%s:", progname);
     258      183806 :         if (filename)
     259             :         {
     260      133600 :             fprintf(stderr, "%s:", filename);
     261      133600 :             if (lineno > 0)
     262      133600 :                 fprintf(stderr, UINT64_FORMAT ":", lineno);
     263             :         }
     264      183806 :         fprintf(stderr, " ");
     265      183806 :         if (sgr_locus)
     266           0 :             fprintf(stderr, ANSI_ESCAPE_RESET);
     267             :     }
     268             : 
     269      244666 :     if (!(log_flags & PG_LOG_FLAG_TERSE))
     270             :     {
     271      183806 :         switch (part)
     272             :         {
     273      183508 :             case PG_LOG_PRIMARY:
     274             :                 switch (level)
     275             :                 {
     276        3650 :                     case PG_LOG_ERROR:
     277        3650 :                         if (sgr_error)
     278           0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
     279        3650 :                         fprintf(stderr, _("error: "));
     280        3650 :                         if (sgr_error)
     281           0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     282        3650 :                         break;
     283         278 :                     case PG_LOG_WARNING:
     284         278 :                         if (sgr_warning)
     285           0 :                             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
     286         278 :                         fprintf(stderr, _("warning: "));
     287         278 :                         if (sgr_warning)
     288           0 :                             fprintf(stderr, ANSI_ESCAPE_RESET);
     289         278 :                         break;
     290      179580 :                     default:
     291      179580 :                         break;
     292             :                 }
     293      183508 :                 break;
     294          36 :             case PG_LOG_DETAIL:
     295          36 :                 if (sgr_note)
     296           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     297          36 :                 fprintf(stderr, _("detail: "));
     298          36 :                 if (sgr_note)
     299           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     300          36 :                 break;
     301         262 :             case PG_LOG_HINT:
     302         262 :                 if (sgr_note)
     303           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
     304         262 :                 fprintf(stderr, _("hint: "));
     305         262 :                 if (sgr_note)
     306           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     307         262 :                 break;
     308             :         }
     309       60860 :     }
     310             : 
     311      244666 :     errno = save_errno;
     312             : 
     313      244666 :     va_copy(ap2, ap);
     314      244666 :     required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
     315      244666 :     va_end(ap2);
     316             : 
     317      244666 :     buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
     318             : 
     319      244666 :     errno = save_errno;         /* malloc might change errno */
     320             : 
     321      244666 :     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      244666 :     vsnprintf(buf, required_len, fmt, ap);
     329             : 
     330             :     /* strip one newline, for PQerrorMessage() */
     331      244666 :     if (required_len >= 2 && buf[required_len - 2] == '\n')
     332      193756 :         buf[required_len - 2] = '\0';
     333             : 
     334      244666 :     fprintf(stderr, "%s\n", buf);
     335             : 
     336      244666 :     free(buf);
     337             : }

Generated by: LCOV version 1.14