Fix a compiler warning in initStringInfo().
[pgsql.git] / src / common / logging.c
blob125a172af80cf61d306943eaf3ca976bd6a80ee0
1 /*-------------------------------------------------------------------------
2 * Logging framework for frontend programs
4 * Copyright (c) 2018-2025, 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 char *token;
123 char *cp = colors;
125 while ((token = strsep(&cp, ":")))
127 char *e = strchr(token, '=');
129 if (e)
131 char *name;
132 char *value;
134 *e = '\0';
135 name = token;
136 value = e + 1;
138 if (strcmp(name, "error") == 0)
139 sgr_error = strdup(value);
140 if (strcmp(name, "warning") == 0)
141 sgr_warning = strdup(value);
142 if (strcmp(name, "note") == 0)
143 sgr_note = strdup(value);
144 if (strcmp(name, "locus") == 0)
145 sgr_locus = strdup(value);
149 free(colors);
152 else
154 sgr_error = SGR_ERROR_DEFAULT;
155 sgr_warning = SGR_WARNING_DEFAULT;
156 sgr_note = SGR_NOTE_DEFAULT;
157 sgr_locus = SGR_LOCUS_DEFAULT;
163 * Change the logging flags.
165 void
166 pg_logging_config(int new_flags)
168 log_flags = new_flags;
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.
175 void
176 pg_logging_set_level(enum pg_log_level new_level)
178 __pg_log_level = new_level;
182 * Command line switches such as --verbose should invoke this.
184 void
185 pg_logging_increase_verbosity(void)
188 * The enum values are chosen such that we have to decrease __pg_log_level
189 * in order to become more verbose.
191 if (__pg_log_level > PG_LOG_NOTSET + 1)
192 __pg_log_level--;
195 void
196 pg_logging_set_pre_callback(void (*cb) (void))
198 log_pre_callback = cb;
201 void
202 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
204 log_locus_callback = cb;
207 void
208 pg_log_generic(enum pg_log_level level, enum pg_log_part part,
209 const char *pg_restrict fmt,...)
211 va_list ap;
213 va_start(ap, fmt);
214 pg_log_generic_v(level, part, fmt, ap);
215 va_end(ap);
218 void
219 pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
220 const char *pg_restrict fmt, va_list ap)
222 int save_errno = errno;
223 const char *filename = NULL;
224 uint64 lineno = 0;
225 va_list ap2;
226 size_t required_len;
227 char *buf;
229 Assert(progname);
230 Assert(level);
231 Assert(fmt);
232 Assert(fmt[strlen(fmt) - 1] != '\n');
234 /* Do nothing if log level is too low. */
235 if (level < __pg_log_level)
236 return;
239 * Flush stdout before output to stderr, to ensure sync even when stdout
240 * is buffered.
242 fflush(stdout);
244 if (log_pre_callback)
245 log_pre_callback();
247 if (log_locus_callback)
248 log_locus_callback(&filename, &lineno);
250 fmt = _(fmt);
252 if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
254 if (sgr_locus)
255 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
256 if (!(log_flags & PG_LOG_FLAG_TERSE))
257 fprintf(stderr, "%s:", progname);
258 if (filename)
260 fprintf(stderr, "%s:", filename);
261 if (lineno > 0)
262 fprintf(stderr, UINT64_FORMAT ":", lineno);
264 fprintf(stderr, " ");
265 if (sgr_locus)
266 fprintf(stderr, ANSI_ESCAPE_RESET);
269 if (!(log_flags & PG_LOG_FLAG_TERSE))
271 switch (part)
273 case PG_LOG_PRIMARY:
274 switch (level)
276 case PG_LOG_ERROR:
277 if (sgr_error)
278 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
279 fprintf(stderr, _("error: "));
280 if (sgr_error)
281 fprintf(stderr, ANSI_ESCAPE_RESET);
282 break;
283 case PG_LOG_WARNING:
284 if (sgr_warning)
285 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
286 fprintf(stderr, _("warning: "));
287 if (sgr_warning)
288 fprintf(stderr, ANSI_ESCAPE_RESET);
289 break;
290 default:
291 break;
293 break;
294 case PG_LOG_DETAIL:
295 if (sgr_note)
296 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
297 fprintf(stderr, _("detail: "));
298 if (sgr_note)
299 fprintf(stderr, ANSI_ESCAPE_RESET);
300 break;
301 case PG_LOG_HINT:
302 if (sgr_note)
303 fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
304 fprintf(stderr, _("hint: "));
305 if (sgr_note)
306 fprintf(stderr, ANSI_ESCAPE_RESET);
307 break;
311 errno = save_errno;
313 va_copy(ap2, ap);
314 required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
315 va_end(ap2);
317 buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
319 errno = save_errno; /* malloc might change errno */
321 if (!buf)
323 /* memory trouble, just print what we can and get out of here */
324 vfprintf(stderr, fmt, ap);
325 return;
328 vsnprintf(buf, required_len, fmt, ap);
330 /* strip one newline, for PQerrorMessage() */
331 if (required_len >= 2 && buf[required_len - 2] == '\n')
332 buf[required_len - 2] = '\0';
334 fprintf(stderr, "%s\n", buf);
336 free(buf);