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

Generated by: LCOV version 1.13