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  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  */
165 void
166 pg_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  */
175 void
177 {
178  __pg_log_level = new_level;
179 }
180 
181 /*
182  * Command line switches such as --verbose should invoke this.
183  */
184 void
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  */
191  if (__pg_log_level > PG_LOG_NOTSET + 1)
192  __pg_log_level--;
193 }
194 
195 void
196 pg_logging_set_pre_callback(void (*cb) (void))
197 {
198  log_pre_callback = cb;
199 }
200 
201 void
202 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
203 {
204  log_locus_callback = cb;
205 }
206 
207 void
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 
218 void
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 
229  Assert(progname);
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 
244  if (log_pre_callback)
246 
247  if (log_locus_callback)
248  log_locus_callback(&filename, &lineno);
249 
250  fmt = _(fmt);
251 
253  {
254  if (sgr_locus)
256  if (!(log_flags & PG_LOG_FLAG_TERSE))
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 
269  if (!(log_flags & PG_LOG_FLAG_TERSE))
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)
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;
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 
317  buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
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:861
#define UINT64_FORMAT
Definition: c.h:552
#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 @160 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:237
const char * get_progname(const char *argv0)
Definition: path.c:575
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