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-2024, 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 
23 static const char *progname;
24 static int log_flags;
25 
26 static void (*log_pre_callback) (void);
27 static void (*log_locus_callback) (const char **, uint64 *);
28 
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;
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  */
53 static bool
54 enable_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  */
82 void
83 pg_logging_init(const char *argv0)
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 
124  while ((token = strsep(&colors, ":")))
125  {
126  char *e = strchr(token, '=');
127 
128  if (e)
129  {
130  char *name;
131  char *value;
132 
133  *e = '\0';
134  name = token;
135  value = e + 1;
136 
137  if (strcmp(name, "error") == 0)
138  sgr_error = strdup(value);
139  if (strcmp(name, "warning") == 0)
140  sgr_warning = strdup(value);
141  if (strcmp(name, "note") == 0)
142  sgr_note = strdup(value);
143  if (strcmp(name, "locus") == 0)
144  sgr_locus = strdup(value);
145  }
146  }
147 
148  free(colors);
149  }
150  }
151  else
152  {
157  }
158  }
159 }
160 
161 /*
162  * Change the logging flags.
163  */
164 void
165 pg_logging_config(int new_flags)
166 {
167  log_flags = new_flags;
168 }
169 
170 /*
171  * pg_logging_init sets the default log level to INFO. Programs that prefer
172  * a different default should use this to set it, immediately afterward.
173  */
174 void
176 {
177  __pg_log_level = new_level;
178 }
179 
180 /*
181  * Command line switches such as --verbose should invoke this.
182  */
183 void
185 {
186  /*
187  * The enum values are chosen such that we have to decrease __pg_log_level
188  * in order to become more verbose.
189  */
190  if (__pg_log_level > PG_LOG_NOTSET + 1)
191  __pg_log_level--;
192 }
193 
194 void
195 pg_logging_set_pre_callback(void (*cb) (void))
196 {
197  log_pre_callback = cb;
198 }
199 
200 void
201 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
202 {
203  log_locus_callback = cb;
204 }
205 
206 void
208  const char *pg_restrict fmt,...)
209 {
210  va_list ap;
211 
212  va_start(ap, fmt);
213  pg_log_generic_v(level, part, fmt, ap);
214  va_end(ap);
215 }
216 
217 void
219  const char *pg_restrict fmt, va_list ap)
220 {
221  int save_errno = errno;
222  const char *filename = NULL;
223  uint64 lineno = 0;
224  va_list ap2;
225  size_t required_len;
226  char *buf;
227 
228  Assert(progname);
229  Assert(level);
230  Assert(fmt);
231  Assert(fmt[strlen(fmt) - 1] != '\n');
232 
233  /* Do nothing if log level is too low. */
234  if (level < __pg_log_level)
235  return;
236 
237  /*
238  * Flush stdout before output to stderr, to ensure sync even when stdout
239  * is buffered.
240  */
241  fflush(stdout);
242 
243  if (log_pre_callback)
245 
246  if (log_locus_callback)
247  log_locus_callback(&filename, &lineno);
248 
249  fmt = _(fmt);
250 
252  {
253  if (sgr_locus)
255  if (!(log_flags & PG_LOG_FLAG_TERSE))
256  fprintf(stderr, "%s:", progname);
257  if (filename)
258  {
259  fprintf(stderr, "%s:", filename);
260  if (lineno > 0)
261  fprintf(stderr, UINT64_FORMAT ":", lineno);
262  }
263  fprintf(stderr, " ");
264  if (sgr_locus)
265  fprintf(stderr, ANSI_ESCAPE_RESET);
266  }
267 
268  if (!(log_flags & PG_LOG_FLAG_TERSE))
269  {
270  switch (part)
271  {
272  case PG_LOG_PRIMARY:
273  switch (level)
274  {
275  case PG_LOG_ERROR:
276  if (sgr_error)
278  fprintf(stderr, _("error: "));
279  if (sgr_error)
280  fprintf(stderr, ANSI_ESCAPE_RESET);
281  break;
282  case PG_LOG_WARNING:
283  if (sgr_warning)
285  fprintf(stderr, _("warning: "));
286  if (sgr_warning)
287  fprintf(stderr, ANSI_ESCAPE_RESET);
288  break;
289  default:
290  break;
291  }
292  break;
293  case PG_LOG_DETAIL:
294  if (sgr_note)
295  fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
296  fprintf(stderr, _("detail: "));
297  if (sgr_note)
298  fprintf(stderr, ANSI_ESCAPE_RESET);
299  break;
300  case PG_LOG_HINT:
301  if (sgr_note)
302  fprintf(stderr, ANSI_ESCAPE_FMT, sgr_note);
303  fprintf(stderr, _("hint: "));
304  if (sgr_note)
305  fprintf(stderr, ANSI_ESCAPE_RESET);
306  break;
307  }
308  }
309 
310  errno = save_errno;
311 
312  va_copy(ap2, ap);
313  required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
314  va_end(ap2);
315 
316  buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
317 
318  errno = save_errno; /* malloc might change errno */
319 
320  if (!buf)
321  {
322  /* memory trouble, just print what we can and get out of here */
323  vfprintf(stderr, fmt, ap);
324  return;
325  }
326 
327  vsnprintf(buf, required_len, fmt, ap);
328 
329  /* strip one newline, for PQerrorMessage() */
330  if (required_len >= 2 && buf[required_len - 2] == '\n')
331  buf[required_len - 2] = '\0';
332 
333  fprintf(stderr, "%s\n", buf);
334 
335  free(buf);
336 }
#define Assert(condition)
Definition: c.h:858
#define UINT64_FORMAT
Definition: c.h:549
#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:17
#define free(a)
Definition: header.h:65
#define token
Definition: indent_globs.h:126
static struct @155 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:184
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:201
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:165
void pg_logging_set_level(enum pg_log_level new_level)
Definition: logging.c:175
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:218
void pg_logging_set_pre_callback(void(*cb)(void))
Definition: logging.c:195
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:207
#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:73
#define vsnprintf
Definition: port.h:237
const char * get_progname(const char *argv0)
Definition: path.c:574
char * strsep(char **stringp, const char *delim)
Definition: strsep.c:49
#define fprintf
Definition: port.h:242
e
Definition: preproc-init.c:82
const char * name