1 /*-------------------------------------------------------------------------
2 * Logging framework for frontend programs
4 * Copyright (c) 2018-2023, 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
);
122 for (char *token
= strtok(colors
, ":"); token
; token
= strtok(NULL
, ":"))
124 char *e
= strchr(token
, '=');
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
);
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.
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.
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.
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)
193 pg_logging_set_pre_callback(void (*cb
) (void))
195 log_pre_callback
= cb
;
199 pg_logging_set_locus_callback(void (*cb
) (const char **filename
, uint64
*lineno
))
201 log_locus_callback
= cb
;
205 pg_log_generic(enum pg_log_level level
, enum pg_log_part part
,
206 const char *pg_restrict fmt
,...)
211 pg_log_generic_v(level
, part
, fmt
, ap
);
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
;
229 Assert(fmt
[strlen(fmt
) - 1] != '\n');
231 /* Do nothing if log level is too low. */
232 if (level
< __pg_log_level
)
236 * Flush stdout before output to stderr, to ensure sync even when stdout
241 if (log_pre_callback
)
244 if (log_locus_callback
)
245 log_locus_callback(&filename
, &lineno
);
249 if (!(log_flags
& PG_LOG_FLAG_TERSE
) || filename
)
252 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_locus
);
253 if (!(log_flags
& PG_LOG_FLAG_TERSE
))
254 fprintf(stderr
, "%s:", progname
);
257 fprintf(stderr
, "%s:", filename
);
259 fprintf(stderr
, UINT64_FORMAT
":", lineno
);
261 fprintf(stderr
, " ");
263 fprintf(stderr
, ANSI_ESCAPE_RESET
);
266 if (!(log_flags
& PG_LOG_FLAG_TERSE
))
275 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_error
);
276 fprintf(stderr
, _("error: "));
278 fprintf(stderr
, ANSI_ESCAPE_RESET
);
282 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_warning
);
283 fprintf(stderr
, _("warning: "));
285 fprintf(stderr
, ANSI_ESCAPE_RESET
);
293 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_note
);
294 fprintf(stderr
, _("detail: "));
296 fprintf(stderr
, ANSI_ESCAPE_RESET
);
300 fprintf(stderr
, ANSI_ESCAPE_FMT
, sgr_note
);
301 fprintf(stderr
, _("hint: "));
303 fprintf(stderr
, ANSI_ESCAPE_RESET
);
311 required_len
= vsnprintf(NULL
, 0, fmt
, ap2
) + 1;
314 buf
= pg_malloc_extended(required_len
, MCXT_ALLOC_NO_OOM
);
316 errno
= save_errno
; /* malloc might change errno */
320 /* memory trouble, just print what we can and get out of here */
321 vfprintf(stderr
, fmt
, ap
);
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
);