LCOV - code coverage report
Current view: top level - src/common - logging.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 79 112 70.5 %
Date: 2020-06-03 11:07:14 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * Logging framework for frontend programs
       3             :  *
       4             :  * Copyright (c) 2018-2020, PostgreSQL Global Development Group
       5             :  *
       6             :  * src/common/logging.c
       7             :  *
       8             :  *-------------------------------------------------------------------------
       9             :  */
      10             : #include "postgres_fe.h"
      11             : 
      12             : #include <unistd.h>
      13             : 
      14             : #include "common/logging.h"
      15             : 
      16             : enum pg_log_level __pg_log_level;
      17             : 
      18             : static const char *progname;
      19             : static int  log_flags;
      20             : 
      21             : static void (*log_pre_callback) (void);
      22             : static void (*log_locus_callback) (const char **, uint64 *);
      23             : 
      24             : static const char *sgr_error = NULL;
      25             : static const char *sgr_warning = NULL;
      26             : static const char *sgr_locus = NULL;
      27             : 
      28             : #define SGR_ERROR_DEFAULT "01;31"
      29             : #define SGR_WARNING_DEFAULT "01;35"
      30             : #define SGR_LOCUS_DEFAULT "01"
      31             : 
      32             : #define ANSI_ESCAPE_FMT "\x1b[%sm"
      33             : #define ANSI_ESCAPE_RESET "\x1b[0m"
      34             : 
      35             : #ifdef WIN32
      36             : 
      37             : #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
      38             : #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
      39             : #endif
      40             : 
      41             : /*
      42             :  * Attempt to enable VT100 sequence processing for colorization on Windows.
      43             :  * If current environment is not VT100-compatible or if this mode could not
      44             :  * be enabled, return false.
      45             :  */
      46             : static bool
      47             : enable_vt_processing(void)
      48             : {
      49             :     /* Check stderr */
      50             :     HANDLE      hOut = GetStdHandle(STD_ERROR_HANDLE);
      51             :     DWORD       dwMode = 0;
      52             : 
      53             :     if (hOut == INVALID_HANDLE_VALUE)
      54             :         return false;
      55             : 
      56             :     /*
      57             :      * Look for the current console settings and check if VT100 is already
      58             :      * enabled.
      59             :      */
      60             :     if (!GetConsoleMode(hOut, &dwMode))
      61             :         return false;
      62             :     if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
      63             :         return true;
      64             : 
      65             :     dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
      66             :     if (!SetConsoleMode(hOut, dwMode))
      67             :         return false;
      68             :     return true;
      69             : }
      70             : #endif                          /* WIN32 */
      71             : 
      72             : /*
      73             :  * This should be called before any output happens.
      74             :  */
      75             : void
      76        8840 : pg_logging_init(const char *argv0)
      77             : {
      78        8840 :     const char *pg_color_env = getenv("PG_COLOR");
      79        8840 :     bool        log_color = false;
      80        8840 :     bool        color_terminal = isatty(fileno(stderr));
      81             : 
      82             : #ifdef WIN32
      83             : 
      84             :     /*
      85             :      * On Windows, check if environment is VT100-compatible if using a
      86             :      * terminal.
      87             :      */
      88             :     if (color_terminal)
      89             :         color_terminal = enable_vt_processing();
      90             : #endif
      91             : 
      92             :     /* usually the default, but not on Windows */
      93        8840 :     setvbuf(stderr, NULL, _IONBF, 0);
      94             : 
      95        8840 :     progname = get_progname(argv0);
      96        8840 :     __pg_log_level = PG_LOG_INFO;
      97             : 
      98        8840 :     if (pg_color_env)
      99             :     {
     100           0 :         if (strcmp(pg_color_env, "always") == 0 ||
     101           0 :             (strcmp(pg_color_env, "auto") == 0 && color_terminal))
     102           0 :             log_color = true;
     103             :     }
     104             : 
     105        8840 :     if (log_color)
     106             :     {
     107           0 :         const char *pg_colors_env = getenv("PG_COLORS");
     108             : 
     109           0 :         if (pg_colors_env)
     110             :         {
     111           0 :             char       *colors = strdup(pg_colors_env);
     112             : 
     113           0 :             if (colors)
     114             :             {
     115           0 :                 for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":"))
     116             :                 {
     117           0 :                     char       *e = strchr(token, '=');
     118             : 
     119           0 :                     if (e)
     120             :                     {
     121             :                         char       *name;
     122             :                         char       *value;
     123             : 
     124           0 :                         *e = '\0';
     125           0 :                         name = token;
     126           0 :                         value = e + 1;
     127             : 
     128           0 :                         if (strcmp(name, "error") == 0)
     129           0 :                             sgr_error = strdup(value);
     130           0 :                         if (strcmp(name, "warning") == 0)
     131           0 :                             sgr_warning = strdup(value);
     132           0 :                         if (strcmp(name, "locus") == 0)
     133           0 :                             sgr_locus = strdup(value);
     134             :                     }
     135             :                 }
     136             : 
     137           0 :                 free(colors);
     138             :             }
     139             :         }
     140             :         else
     141             :         {
     142           0 :             sgr_error = SGR_ERROR_DEFAULT;
     143           0 :             sgr_warning = SGR_WARNING_DEFAULT;
     144           0 :             sgr_locus = SGR_LOCUS_DEFAULT;
     145             :         }
     146             :     }
     147        8840 : }
     148             : 
     149             : void
     150        8188 : pg_logging_config(int new_flags)
     151             : {
     152        8188 :     log_flags = new_flags;
     153        8188 : }
     154             : 
     155             : void
     156         426 : pg_logging_set_level(enum pg_log_level new_level)
     157             : {
     158         426 :     __pg_log_level = new_level;
     159         426 : }
     160             : 
     161             : void
     162        5052 : pg_logging_set_pre_callback(void (*cb) (void))
     163             : {
     164        5052 :     log_pre_callback = cb;
     165        5052 : }
     166             : 
     167             : void
     168        5052 : pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
     169             : {
     170        5052 :     log_locus_callback = cb;
     171        5052 : }
     172             : 
     173             : void
     174       54986 : pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
     175             : {
     176             :     va_list     ap;
     177             : 
     178       54986 :     va_start(ap, fmt);
     179       54986 :     pg_log_generic_v(level, fmt, ap);
     180       54986 :     va_end(ap);
     181       54986 : }
     182             : 
     183             : void
     184       55090 : pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
     185             : {
     186       55090 :     int         save_errno = errno;
     187       55090 :     const char *filename = NULL;
     188       55090 :     uint64      lineno = 0;
     189             :     va_list     ap2;
     190             :     size_t      required_len;
     191             :     char       *buf;
     192             : 
     193             :     Assert(progname);
     194             :     Assert(level);
     195             :     Assert(fmt);
     196             :     Assert(fmt[strlen(fmt) - 1] != '\n');
     197             : 
     198             :     /*
     199             :      * Flush stdout before output to stderr, to ensure sync even when stdout
     200             :      * is buffered.
     201             :      */
     202       55090 :     fflush(stdout);
     203             : 
     204       55090 :     if (log_pre_callback)
     205       29548 :         log_pre_callback();
     206             : 
     207       55090 :     if (log_locus_callback)
     208       29548 :         log_locus_callback(&filename, &lineno);
     209             : 
     210       55090 :     fmt = _(fmt);
     211             : 
     212       55090 :     if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
     213             :     {
     214       26230 :         if (sgr_locus)
     215           0 :             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
     216       26230 :         if (!(log_flags & PG_LOG_FLAG_TERSE))
     217       26230 :             fprintf(stderr, "%s:", progname);
     218       26230 :         if (filename)
     219             :         {
     220         326 :             fprintf(stderr, "%s:", filename);
     221         326 :             if (lineno > 0)
     222         326 :                 fprintf(stderr, UINT64_FORMAT ":", lineno);
     223             :         }
     224       26230 :         fprintf(stderr, " ");
     225       26230 :         if (sgr_locus)
     226           0 :             fprintf(stderr, ANSI_ESCAPE_RESET);
     227             :     }
     228             : 
     229       55090 :     if (!(log_flags & PG_LOG_FLAG_TERSE))
     230             :     {
     231       26230 :         switch (level)
     232             :         {
     233         314 :             case PG_LOG_FATAL:
     234         314 :                 if (sgr_error)
     235           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
     236         314 :                 fprintf(stderr, _("fatal: "));
     237         314 :                 if (sgr_error)
     238           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     239         314 :                 break;
     240         776 :             case PG_LOG_ERROR:
     241         776 :                 if (sgr_error)
     242           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
     243         776 :                 fprintf(stderr, _("error: "));
     244         776 :                 if (sgr_error)
     245           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     246         776 :                 break;
     247         154 :             case PG_LOG_WARNING:
     248         154 :                 if (sgr_warning)
     249           0 :                     fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
     250         154 :                 fprintf(stderr, _("warning: "));
     251         154 :                 if (sgr_warning)
     252           0 :                     fprintf(stderr, ANSI_ESCAPE_RESET);
     253         154 :                 break;
     254       24986 :             default:
     255       24986 :                 break;
     256             :         }
     257       28860 :     }
     258             : 
     259       55090 :     errno = save_errno;
     260             : 
     261       55090 :     va_copy(ap2, ap);
     262       55090 :     required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
     263       55090 :     va_end(ap2);
     264             : 
     265       55090 :     buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
     266             : 
     267       55090 :     errno = save_errno;         /* malloc might change errno */
     268             : 
     269       55090 :     if (!buf)
     270             :     {
     271             :         /* memory trouble, just print what we can and get out of here */
     272           0 :         vfprintf(stderr, fmt, ap);
     273           0 :         return;
     274             :     }
     275             : 
     276       55090 :     vsnprintf(buf, required_len, fmt, ap);
     277             : 
     278             :     /* strip one newline, for PQerrorMessage() */
     279       55090 :     if (required_len >= 2 && buf[required_len - 2] == '\n')
     280       29448 :         buf[required_len - 2] = '\0';
     281             : 
     282       55090 :     fprintf(stderr, "%s\n", buf);
     283             : 
     284       55090 :     free(buf);
     285             : }

Generated by: LCOV version 1.13