1 /*-------------------------------------------------------------------------
2 * Logging framework for frontend programs
4 * Copyright (c) 2018-2025, PostgreSQL Global Development Group
8 *-------------------------------------------------------------------------
12 #error "This file is not expected to be compiled for backend code"
15 #include "postgres_fe.h"
19 #include "common/logging.h"
21 enum pg_log_level __pg_log_level
;
23 static const char *progname
;
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"
44 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
45 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
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.
54 enable_vt_processing(void)
57 HANDLE hOut
= GetStdHandle(STD_ERROR_HANDLE
);
60 if (hOut
== INVALID_HANDLE_VALUE
)
64 * Look for the current console settings and check if VT100 is already
67 if (!GetConsoleMode(hOut
, &dwMode
))
69 if ((dwMode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
) != 0)
72 dwMode
|= ENABLE_VIRTUAL_TERMINAL_PROCESSING
;
73 if (!SetConsoleMode(hOut
, dwMode
))
80 * This should be called before any output happens.
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
));
92 * On Windows, check if environment is VT100-compatible if using a
96 color_terminal
= enable_vt_processing();
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
;
107 if (strcmp(pg_color_env
, "always") == 0 ||
108 (strcmp(pg_color_env
, "auto") == 0 && color_terminal
))
114 const char *pg_colors_env
= getenv("PG_COLORS");
118 char *colors
= strdup(pg_colors_env
);
125 while ((token
= strsep(&cp
, ":")))
127 char *e
= strchr(token
, '=');
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
);
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.
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.
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.
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)
196 pg_logging_set_pre_callback(void (*cb
) (void))
198 log_pre_callback
= cb
;
202 pg_logging_set_locus_callback(void (*cb
) (const char **filename
, uint64
*lineno
))
204 log_locus_callback
= cb
;
208 pg_log_generic(enum pg_log_level level
, enum pg_log_part part
,
209 const char *pg_restrict fmt
,...)
214 pg_log_generic_v(level
, part
, fmt
, ap
);
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
;
232 Assert(fmt
[strlen(fmt
) - 1] != '\n');
234 /* Do nothing if log level is too low. */
235 if (level
< __pg_log_level
)
239 * Flush stdout before output to stderr, to ensure sync even when stdout
244 if (log_pre_callback
)
247 if (log_locus_callback
)
248 log_locus_callback(&filename
, &lineno
);
252 if (!(log_flags
& PG_LOG_FLAG_TERSE
) || filename
)
255 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_locus
);
256 if (!(log_flags
& PG_LOG_FLAG_TERSE
))
257 fprintf(stderr
, "%s:", progname
);
260 fprintf(stderr
, "%s:", filename
);
262 fprintf(stderr
, UINT64_FORMAT
":", lineno
);
264 fprintf(stderr
, " ");
266 fprintf(stderr
, ANSI_ESCAPE_RESET
);
269 if (!(log_flags
& PG_LOG_FLAG_TERSE
))
278 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_error
);
279 fprintf(stderr
, _("error: "));
281 fprintf(stderr
, ANSI_ESCAPE_RESET
);
285 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_warning
);
286 fprintf(stderr
, _("warning: "));
288 fprintf(stderr
, ANSI_ESCAPE_RESET
);
296 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_note
);
297 fprintf(stderr
, _("detail: "));
299 fprintf(stderr
, ANSI_ESCAPE_RESET
);
303 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_note
);
304 fprintf(stderr
, _("hint: "));
306 fprintf(stderr
, ANSI_ESCAPE_RESET
);
314 required_len
= vsnprintf(NULL
, 0, fmt
, ap2
) + 1;
317 buf
= pg_malloc_extended(required_len
, MCXT_ALLOC_NO_OOM
);
319 errno
= save_errno
; /* malloc might change errno */
323 /* memory trouble, just print what we can and get out of here */
324 vfprintf(stderr
, fmt
, ap
);
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
);