vacuumlazy.c: Tweak local variable name.
[pgsql.git] / src / common / logging.c
blobdab718b482ec306cb64d903892d3387d5cbc5788
1 /*-------------------------------------------------------------------------
2 * Logging framework for frontend programs
4 * Copyright (c) 2018-2023, PostgreSQL Global Development Group
6 * src/common/logging.c
8 *-------------------------------------------------------------------------
9 */
11 #ifndef FRONTEND
12 #error "This file is not expected to be compiled for backend code"
13 #endif
15 #include "postgres_fe.h"
17 #include <unistd.h>
19 #include "common/logging.h"
21 enum pg_log_level __pg_log_level;
23 static const char *progname;
24 static int log_flags;
26 static void (*log_pre_callback) (void);
27 static void (*log_locus_callback) (const char **, uint64 *);
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;
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"
39 #define ANSI_ESCAPE_FMT "\x1b[%sm"
40 #define ANSI_ESCAPE_RESET "\x1b[0m"
42 #ifdef WIN32
44 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
45 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
46 #endif
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.
53 static bool
54 enable_vt_processing(void)
56 /* Check stderr */
57 HANDLE hOut = GetStdHandle(STD_ERROR_HANDLE);
58 DWORD dwMode = 0;
60 if (hOut == INVALID_HANDLE_VALUE)
61 return false;
64 * Look for the current console settings and check if VT100 is already
65 * enabled.
67 if (!GetConsoleMode(hOut, &dwMode))
68 return false;
69 if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
70 return true;
72 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
73 if (!SetConsoleMode(hOut, dwMode))
74 return false;
75 return true;
77 #endif /* WIN32 */
80 * This should be called before any output happens.
82 void
83 pg_logging_init(const char *argv0)
85 const char *pg_color_env = getenv("PG_COLOR");
86 bool log_color = false;
87 bool color_terminal = isatty(fileno(stderr));
89 #ifdef WIN32
92 * On Windows, check if environment is VT100-compatible if using a
93 * terminal.
95 if (color_terminal)
96 color_terminal = enable_vt_processing();
97 #endif
99 /* usually the default, but not on Windows */
100 setvbuf(stderr, NULL, _IONBF, 0);
102 progname = get_progname(argv0);
103 __pg_log_level = PG_LOG_INFO;
105 if (pg_color_env)
107 if (strcmp(pg_color_env, "always") == 0 ||
108 (strcmp(pg_color_env, "auto") == 0 && color_terminal))
109 log_color = true;
112 if (log_color)
114 const char *pg_colors_env = getenv("PG_COLORS");
116 if (pg_colors_env)
118 char *colors = strdup(pg_colors_env);
120 if (colors)
122 for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":"))
124 char *e = strchr(token, '=');
126 if (e)
128 char *name;
129 char *value;
131 *e = '\0';
132 name = token;
133 value = e + 1;
135 if (strcmp(name, "error") == 0)
136 sgr_error = strdup(value);
137 if (strcmp(name, "warning") == 0)
138 sgr_warning = strdup(value);
139 if (strcmp(name, "note") == 0)
140 sgr_note = strdup(value);
141 if (strcmp(name, "locus") == 0)
142 sgr_locus = strdup(value);
146 free(colors);
149 else
151 sgr_error = SGR_ERROR_DEFAULT;
152 sgr_warning = SGR_WARNING_DEFAULT;
153 sgr_note = SGR_NOTE_DEFAULT;
154 sgr_locus = SGR_LOCUS_DEFAULT;
160 * Change the logging flags.
162 void
163 pg_logging_config(int new_flags)
165 log_flags = new_flags;
169 * pg_logging_init sets the default log level to INFO. Programs that prefer
170 * a different default should use this to set it, immediately afterward.
172 void
173 pg_logging_set_level(enum pg_log_level new_level)
175 __pg_log_level = new_level;
179 * Command line switches such as --verbose should invoke this.
181 void
182 pg_logging_increase_verbosity(void)
185 * The enum values are chosen such that we have to decrease __pg_log_level
186 * in order to become more verbose.
188 if (__pg_log_level > PG_LOG_NOTSET + 1)
189 __pg_log_level--;
192 void
193 pg_logging_set_pre_callback(void (*cb) (void))
195 log_pre_callback = cb;
198 void
199 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
201 log_locus_callback = cb;
204 void
205 pg_log_generic(enum pg_log_level level, enum pg_log_part part,
206 const char *pg_restrict fmt,...)
208 va_list ap;
210 va_start(ap, fmt);
211 pg_log_generic_v(level, part, fmt, ap);
212 va_end(ap);
215 void
216 pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
217 const char *pg_restrict fmt, va_list ap)
219 int save_errno = errno;
220 const char *filename = NULL;
221 uint64 lineno = 0;
222 va_list ap2;
223 size_t required_len;
224 char *buf;
226 Assert(progname);
227 Assert(level);
228 Assert(fmt);
229 Assert(fmt[strlen(fmt) - 1] != '\n');
231 /* Do nothing if log level is too low. */
232 if (level < __pg_log_level)
233 return;
236 * Flush stdout before output to stderr, to ensure sync even when stdout
237 * is buffered.
239 fflush(stdout);
241 if (log_pre_callback)
242 log_pre_callback();
244 if (log_locus_callback)
245 log_locus_callback(&filename, &lineno);
247 fmt = _(fmt);
249 if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
251 if (sgr_locus)
252 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
253 if (!(log_flags & PG_LOG_FLAG_TERSE))
254 fprintf(stderr, "%s:", progname);
255 if (filename)
257 fprintf(stderr, "%s:", filename);
258 if (lineno > 0)
259 fprintf(stderr, UINT64_FORMAT ":", lineno);
261 fprintf(stderr, " ");
262 if (sgr_locus)
263 fprintf(stderr, ANSI_ESCAPE_RESET);
266 if (!(log_flags & PG_LOG_FLAG_TERSE))
268 switch (part)
270 case PG_LOG_PRIMARY:
271 switch (level)
273 case PG_LOG_ERROR:
274 if (sgr_error)
275 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
276 fprintf(stderr, _("error: "));
277 if (sgr_error)
278 fprintf(stderr, ANSI_ESCAPE_RESET);
279 break;
280 case PG_LOG_WARNING:
281 if (sgr_warning)
282 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
283 fprintf(stderr, _("warning: "));
284 if (sgr_warning)
285 fprintf(stderr, ANSI_ESCAPE_RESET);
286 break;
287 default:
288 break;
290 break;
291 case PG_LOG_DETAIL:
292 if (sgr_note)
293 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
294 fprintf(stderr, _("detail: "));
295 if (sgr_note)
296 fprintf(stderr, ANSI_ESCAPE_RESET);
297 break;
298 case PG_LOG_HINT:
299 if (sgr_note)
300 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
301 fprintf(stderr, _("hint: "));
302 if (sgr_note)
303 fprintf(stderr, ANSI_ESCAPE_RESET);
304 break;
308 errno = save_errno;
310 va_copy(ap2, ap);
311 required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
312 va_end(ap2);
314 buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
316 errno = save_errno; /* malloc might change errno */
318 if (!buf)
320 /* memory trouble, just print what we can and get out of here */
321 vfprintf(stderr, fmt, ap);
322 return;
325 vsnprintf(buf, required_len, fmt, ap);
327 /* strip one newline, for PQerrorMessage() */
328 if (required_len >= 2 && buf[required_len - 2] == '\n')
329 buf[required_len - 2] = '\0';
331 fprintf(stderr, "%s\n", buf);
333 free(buf);