1 /*------------------------------------------------------------------------- 2 * Logging framework for frontend programs 3 * 4 * Copyright (c) 2018-2019, 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 pg_logging_init(const char *argv0) 77 { 78 const char *pg_color_env = getenv("PG_COLOR"); 79 bool log_color = false; 80 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 setvbuf(stderr, NULL, _IONBF, 0); 94 95 progname = get_progname(argv0); 96 __pg_log_level = PG_LOG_INFO; 97 98 if (pg_color_env) 99 { 100 if (strcmp(pg_color_env, "always") == 0 || 101 (strcmp(pg_color_env, "auto") == 0 && color_terminal)) 102 log_color = true; 103 } 104 105 if (log_color) 106 { 107 const char *pg_colors_env = getenv("PG_COLORS"); 108 109 if (pg_colors_env) 110 { 111 char *colors = strdup(pg_colors_env); 112 113 if (colors) 114 { 115 for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":")) 116 { 117 char *e = strchr(token, '='); 118 119 if (e) 120 { 121 char *name; 122 char *value; 123 124 *e = '\0'; 125 name = token; 126 value = e + 1; 127 128 if (strcmp(name, "error") == 0) 129 sgr_error = strdup(value); 130 if (strcmp(name, "warning") == 0) 131 sgr_warning = strdup(value); 132 if (strcmp(name, "locus") == 0) 133 sgr_locus = strdup(value); 134 } 135 } 136 137 free(colors); 138 } 139 } 140 else 141 { 142 sgr_error = SGR_ERROR_DEFAULT; 143 sgr_warning = SGR_WARNING_DEFAULT; 144 sgr_locus = SGR_LOCUS_DEFAULT; 145 } 146 } 147 } 148 149 void 150 pg_logging_config(int new_flags) 151 { 152 log_flags = new_flags; 153 } 154 155 void 156 pg_logging_set_level(enum pg_log_level new_level) 157 { 158 __pg_log_level = new_level; 159 } 160 161 void 162 pg_logging_set_pre_callback(void (*cb) (void)) 163 { 164 log_pre_callback = cb; 165 } 166 167 void 168 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)) 169 { 170 log_locus_callback = cb; 171 } 172 173 void 174 pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) 175 { 176 va_list ap; 177 178 va_start(ap, fmt); 179 pg_log_generic_v(level, fmt, ap); 180 va_end(ap); 181 } 182 183 void 184 pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) 185 { 186 int save_errno = errno; 187 const char *filename = NULL; 188 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 fflush(stdout); 203 204 if (log_pre_callback) 205 log_pre_callback(); 206 207 if (log_locus_callback) 208 log_locus_callback(&filename, &lineno); 209 210 fmt = _(fmt); 211 212 if (!(log_flags & PG_LOG_FLAG_TERSE) || filename) 213 { 214 if (sgr_locus) 215 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus); 216 if (!(log_flags & PG_LOG_FLAG_TERSE)) 217 fprintf(stderr, "%s:", progname); 218 if (filename) 219 { 220 fprintf(stderr, "%s:", filename); 221 if (lineno > 0) 222 fprintf(stderr, UINT64_FORMAT ":", lineno); 223 } 224 fprintf(stderr, " "); 225 if (sgr_locus) 226 fprintf(stderr, ANSI_ESCAPE_RESET); 227 } 228 229 if (!(log_flags & PG_LOG_FLAG_TERSE)) 230 { 231 switch (level) 232 { 233 case PG_LOG_FATAL: 234 if (sgr_error) 235 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error); 236 fprintf(stderr, _("fatal: ")); 237 if (sgr_error) 238 fprintf(stderr, ANSI_ESCAPE_RESET); 239 break; 240 case PG_LOG_ERROR: 241 if (sgr_error) 242 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error); 243 fprintf(stderr, _("error: ")); 244 if (sgr_error) 245 fprintf(stderr, ANSI_ESCAPE_RESET); 246 break; 247 case PG_LOG_WARNING: 248 if (sgr_warning) 249 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning); 250 fprintf(stderr, _("warning: ")); 251 if (sgr_warning) 252 fprintf(stderr, ANSI_ESCAPE_RESET); 253 break; 254 default: 255 break; 256 } 257 } 258 259 errno = save_errno; 260 261 va_copy(ap2, ap); 262 required_len = vsnprintf(NULL, 0, fmt, ap2) + 1; 263 va_end(ap2); 264 265 buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM); 266 267 errno = save_errno; /* malloc might change errno */ 268 269 if (!buf) 270 { 271 /* memory trouble, just print what we can and get out of here */ 272 vfprintf(stderr, fmt, ap); 273 return; 274 } 275 276 vsnprintf(buf, required_len, fmt, ap); 277 278 /* strip one newline, for PQerrorMessage() */ 279 if (required_len >= 2 && buf[required_len - 2] == '\n') 280 buf[required_len - 2] = '\0'; 281 282 fprintf(stderr, "%s\n", buf); 283 284 free(buf); 285 } 286