PostgreSQL Source Code git master
logging.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 * Logging framework for frontend programs
3 *
4 * Copyright (c) 2018-2025, 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
22
23static const char *progname;
24static int log_flags;
25
26static void (*log_pre_callback) (void);
27static void (*log_locus_callback) (const char **, uint64 *);
28
29static const char *sgr_error = NULL;
30static const char *sgr_warning = NULL;
31static const char *sgr_note = NULL;
32static const char *sgr_locus = NULL;
33
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"
38
39#define ANSI_ESCAPE_FMT "\x1b[%sm"
40#define ANSI_ESCAPE_RESET "\x1b[0m"
41
42#ifdef WIN32
43
44#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
45#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
46#endif
47
48/*
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.
52 */
53static bool
54enable_vt_processing(void)
55{
56 /* Check stderr */
57 HANDLE hOut = GetStdHandle(STD_ERROR_HANDLE);
58 DWORD dwMode = 0;
59
60 if (hOut == INVALID_HANDLE_VALUE)
61 return false;
62
63 /*
64 * Look for the current console settings and check if VT100 is already
65 * enabled.
66 */
67 if (!GetConsoleMode(hOut, &dwMode))
68 return false;
69 if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
70 return true;
71
72 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
73 if (!SetConsoleMode(hOut, dwMode))
74 return false;
75 return true;
76}
77#endif /* WIN32 */
78
79/*
80 * This should be called before any output happens.
81 */
82void
84{
85 const char *pg_color_env = getenv("PG_COLOR");
86 bool log_color = false;
87 bool color_terminal = isatty(fileno(stderr));
88
89#ifdef WIN32
90
91 /*
92 * On Windows, check if environment is VT100-compatible if using a
93 * terminal.
94 */
95 if (color_terminal)
96 color_terminal = enable_vt_processing();
97#endif
98
99 /* usually the default, but not on Windows */
100 setvbuf(stderr, NULL, _IONBF, 0);
101
104
105 if (pg_color_env)
106 {
107 if (strcmp(pg_color_env, "always") == 0 ||
108 (strcmp(pg_color_env, "auto") == 0 && color_terminal))
109 log_color = true;
110 }
111
112 if (log_color)
113 {
114 const char *pg_colors_env = getenv("PG_COLORS");
115
116 if (pg_colors_env)
117 {
118 char *colors = strdup(pg_colors_env);
119
120 if (colors)
121 {
122 char *token;
123 char *cp = colors;
124
125 while ((token = strsep(&cp, ":")))
126 {
127 char *e = strchr(token, '=');
128
129 if (e)
130 {
131 char *name;
132 char *value;
133
134 *e = '\0';
135 name = token;
136 value = e + 1;
137
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);
146 }
147 }
148
149 free(colors);
150 }
151 }
152 else
153 {
158 }
159 }
160}
161
162/*
163 * Change the logging flags.
164 */
165void
166pg_logging_config(int new_flags)
167{
168 log_flags = new_flags;
169}
170
171/*
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.
174 */
175void
177{
178 __pg_log_level = new_level;
179}
180
181/*
182 * Command line switches such as --verbose should invoke this.
183 */
184void
186{
187 /*
188 * The enum values are chosen such that we have to decrease __pg_log_level
189 * in order to become more verbose.
190 */
193}
194
195void
197{
198 log_pre_callback = cb;
199}
200
201void
202pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
203{
205}
206
207void
209 const char *pg_restrict fmt,...)
210{
211 va_list ap;
212
213 va_start(ap, fmt);
214 pg_log_generic_v(level, part, fmt, ap);
215 va_end(ap);
216}
217
218void
220 const char *pg_restrict fmt, va_list ap)
221{
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;
228
230 Assert(level);
231 Assert(fmt);
232 Assert(fmt[strlen(fmt) - 1] != '\n');
233
234 /* Do nothing if log level is too low. */
235 if (level < __pg_log_level)
236 return;
237
238 /*
239 * Flush stdout before output to stderr, to ensure sync even when stdout
240 * is buffered.
241 */
242 fflush(stdout);
243
246
248 log_locus_callback(&filename, &lineno);
249
250 fmt = _(fmt);
251
253 {
254 if (sgr_locus)
257 fprintf(stderr, "%s:", progname);
258 if (filename)
259 {
260 fprintf(stderr, "%s:", filename);
261 if (lineno > 0)
262 fprintf(stderr, UINT64_FORMAT ":", lineno);
263 }
264 fprintf(stderr, " ");
265 if (sgr_locus)
266 fprintf(stderr, ANSI_ESCAPE_RESET);
267 }
268
270 {
271 switch (part)
272 {
273 case PG_LOG_PRIMARY:
274 switch (level)
275 {
276 case PG_LOG_ERROR:
277 if (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)
286 fprintf(stderr, _("warning: "));
287 if (sgr_warning)
288 fprintf(stderr, ANSI_ESCAPE_RESET);
289 break;
290 default:
291 break;
292 }
293 break;
294 case PG_LOG_DETAIL:
295 if (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)
304 fprintf(stderr, _("hint: "));
305 if (sgr_note)
306 fprintf(stderr, ANSI_ESCAPE_RESET);
307 break;
308 }
309 }
310
311 errno = save_errno;
312
313 va_copy(ap2, ap);
314 required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
315 va_end(ap2);
316
318
319 errno = save_errno; /* malloc might change errno */
320
321 if (!buf)
322 {
323 /* memory trouble, just print what we can and get out of here */
324 vfprintf(stderr, fmt, ap);
325 return;
326 }
327
328 vsnprintf(buf, required_len, fmt, ap);
329
330 /* strip one newline, for PQerrorMessage() */
331 if (required_len >= 2 && buf[required_len - 2] == '\n')
332 buf[required_len - 2] = '\0';
333
334 fprintf(stderr, "%s\n", buf);
335
336 free(buf);
337}
#define Assert(condition)
Definition: c.h:815
#define UINT64_FORMAT
Definition: c.h:507
uint64_t uint64
Definition: c.h:489
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
#define _(x)
Definition: elog.c:90
void * pg_malloc_extended(size_t size, int flags)
Definition: fe_memutils.c:59
#define MCXT_ALLOC_NO_OOM
Definition: fe_memutils.h:29
#define free(a)
Definition: header.h:65
#define token
Definition: indent_globs.h:126
static struct @162 value
static void const char * fmt
static void const char fflush(stdout)
va_end(args)
vfprintf(stderr, fmt, args)
va_start(args, fmt)
void pg_logging_increase_verbosity(void)
Definition: logging.c:185
static int log_flags
Definition: logging.c:24
#define SGR_WARNING_DEFAULT
Definition: logging.c:35
#define SGR_NOTE_DEFAULT
Definition: logging.c:36
static const char * sgr_note
Definition: logging.c:31
static const char * sgr_error
Definition: logging.c:29
#define SGR_LOCUS_DEFAULT
Definition: logging.c:37
void pg_logging_init(const char *argv0)
Definition: logging.c:83
#define SGR_ERROR_DEFAULT
Definition: logging.c:34
void pg_logging_set_locus_callback(void(*cb)(const char **filename, uint64 *lineno))
Definition: logging.c:202
static const char * sgr_warning
Definition: logging.c:30
static void(* log_pre_callback)(void)
Definition: logging.c:26
static void(* log_locus_callback)(const char **, uint64 *)
Definition: logging.c:27
static const char * sgr_locus
Definition: logging.c:32
void pg_logging_config(int new_flags)
Definition: logging.c:166
void pg_logging_set_level(enum pg_log_level new_level)
Definition: logging.c:176
void pg_log_generic_v(enum pg_log_level level, enum pg_log_part part, const char *pg_restrict fmt, va_list ap)
Definition: logging.c:219
void pg_logging_set_pre_callback(void(*cb)(void))
Definition: logging.c:196
static const char * progname
Definition: logging.c:23
enum pg_log_level __pg_log_level
Definition: logging.c:21
#define ANSI_ESCAPE_RESET
Definition: logging.c:40
void pg_log_generic(enum pg_log_level level, enum pg_log_part part, const char *pg_restrict fmt,...)
Definition: logging.c:208
#define ANSI_ESCAPE_FMT
Definition: logging.c:39
#define PG_LOG_FLAG_TERSE
Definition: logging.h:86
pg_log_part
Definition: logging.h:62
@ PG_LOG_PRIMARY
Definition: logging.h:67
@ PG_LOG_HINT
Definition: logging.h:79
@ PG_LOG_DETAIL
Definition: logging.h:73
pg_log_level
Definition: logging.h:17
@ PG_LOG_INFO
Definition: logging.h:33
@ PG_LOG_NOTSET
Definition: logging.h:21
@ PG_LOG_WARNING
Definition: logging.h:38
@ PG_LOG_ERROR
Definition: logging.h:43
static char * argv0
Definition: pg_ctl.c:93
static char * filename
Definition: pg_dumpall.c:119
static char * buf
Definition: pg_test_fsync.c:72
#define vsnprintf
Definition: port.h:238
char * strsep(char **stringp, const char *delim)
Definition: strsep.c:49
const char * get_progname(const char *argv0)
Definition: path.c:652
e
Definition: preproc-init.c:82
const char * name