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-2020, 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_locus = NULL;
32 
33 #define SGR_ERROR_DEFAULT "01;31"
34 #define SGR_WARNING_DEFAULT "01;35"
35 #define SGR_LOCUS_DEFAULT "01"
36 
37 #define ANSI_ESCAPE_FMT "\x1b[%sm"
38 #define ANSI_ESCAPE_RESET "\x1b[0m"
39 
40 #ifdef WIN32
41 
42 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
43 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
44 #endif
45 
46 /*
47  * Attempt to enable VT100 sequence processing for colorization on Windows.
48  * If current environment is not VT100-compatible or if this mode could not
49  * be enabled, return false.
50  */
51 static bool
52 enable_vt_processing(void)
53 {
54  /* Check stderr */
55  HANDLE hOut = GetStdHandle(STD_ERROR_HANDLE);
56  DWORD dwMode = 0;
57 
58  if (hOut == INVALID_HANDLE_VALUE)
59  return false;
60 
61  /*
62  * Look for the current console settings and check if VT100 is already
63  * enabled.
64  */
65  if (!GetConsoleMode(hOut, &dwMode))
66  return false;
67  if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
68  return true;
69 
70  dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
71  if (!SetConsoleMode(hOut, dwMode))
72  return false;
73  return true;
74 }
75 #endif /* WIN32 */
76 
77 /*
78  * This should be called before any output happens.
79  */
80 void
81 pg_logging_init(const char *argv0)
82 {
83  const char *pg_color_env = getenv("PG_COLOR");
84  bool log_color = false;
85  bool color_terminal = isatty(fileno(stderr));
86 
87 #ifdef WIN32
88 
89  /*
90  * On Windows, check if environment is VT100-compatible if using a
91  * terminal.
92  */
93  if (color_terminal)
94  color_terminal = enable_vt_processing();
95 #endif
96 
97  /* usually the default, but not on Windows */
98  setvbuf(stderr, NULL, _IONBF, 0);
99 
100  progname = get_progname(argv0);
102 
103  if (pg_color_env)
104  {
105  if (strcmp(pg_color_env, "always") == 0 ||
106  (strcmp(pg_color_env, "auto") == 0 && color_terminal))
107  log_color = true;
108  }
109 
110  if (log_color)
111  {
112  const char *pg_colors_env = getenv("PG_COLORS");
113 
114  if (pg_colors_env)
115  {
116  char *colors = strdup(pg_colors_env);
117 
118  if (colors)
119  {
120  for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":"))
121  {
122  char *e = strchr(token, '=');
123 
124  if (e)
125  {
126  char *name;
127  char *value;
128 
129  *e = '\0';
130  name = token;
131  value = e + 1;
132 
133  if (strcmp(name, "error") == 0)
134  sgr_error = strdup(value);
135  if (strcmp(name, "warning") == 0)
136  sgr_warning = strdup(value);
137  if (strcmp(name, "locus") == 0)
138  sgr_locus = strdup(value);
139  }
140  }
141 
142  free(colors);
143  }
144  }
145  else
146  {
150  }
151  }
152 }
153 
154 void
155 pg_logging_config(int new_flags)
156 {
157  log_flags = new_flags;
158 }
159 
160 void
162 {
163  __pg_log_level = new_level;
164 }
165 
166 void
167 pg_logging_set_pre_callback(void (*cb) (void))
168 {
169  log_pre_callback = cb;
170 }
171 
172 void
173 pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno))
174 {
175  log_locus_callback = cb;
176 }
177 
178 void
179 pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
180 {
181  va_list ap;
182 
183  va_start(ap, fmt);
184  pg_log_generic_v(level, fmt, ap);
185  va_end(ap);
186 }
187 
188 void
189 pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
190 {
191  int save_errno = errno;
192  const char *filename = NULL;
193  uint64 lineno = 0;
194  va_list ap2;
195  size_t required_len;
196  char *buf;
197 
198  Assert(progname);
199  Assert(level);
200  Assert(fmt);
201  Assert(fmt[strlen(fmt) - 1] != '\n');
202 
203  /*
204  * Flush stdout before output to stderr, to ensure sync even when stdout
205  * is buffered.
206  */
207  fflush(stdout);
208 
209  if (log_pre_callback)
211 
212  if (log_locus_callback)
213  log_locus_callback(&filename, &lineno);
214 
215  fmt = _(fmt);
216 
217  if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
218  {
219  if (sgr_locus)
221  if (!(log_flags & PG_LOG_FLAG_TERSE))
222  fprintf(stderr, "%s:", progname);
223  if (filename)
224  {
225  fprintf(stderr, "%s:", filename);
226  if (lineno > 0)
227  fprintf(stderr, UINT64_FORMAT ":", lineno);
228  }
229  fprintf(stderr, " ");
230  if (sgr_locus)
231  fprintf(stderr, ANSI_ESCAPE_RESET);
232  }
233 
234  if (!(log_flags & PG_LOG_FLAG_TERSE))
235  {
236  switch (level)
237  {
238  case PG_LOG_FATAL:
239  if (sgr_error)
241  fprintf(stderr, _("fatal: "));
242  if (sgr_error)
243  fprintf(stderr, ANSI_ESCAPE_RESET);
244  break;
245  case PG_LOG_ERROR:
246  if (sgr_error)
248  fprintf(stderr, _("error: "));
249  if (sgr_error)
250  fprintf(stderr, ANSI_ESCAPE_RESET);
251  break;
252  case PG_LOG_WARNING:
253  if (sgr_warning)
255  fprintf(stderr, _("warning: "));
256  if (sgr_warning)
257  fprintf(stderr, ANSI_ESCAPE_RESET);
258  break;
259  default:
260  break;
261  }
262  }
263 
264  errno = save_errno;
265 
266  va_copy(ap2, ap);
267  required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
268  va_end(ap2);
269 
270  buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
271 
272  errno = save_errno; /* malloc might change errno */
273 
274  if (!buf)
275  {
276  /* memory trouble, just print what we can and get out of here */
277  vfprintf(stderr, fmt, ap);
278  return;
279  }
280 
281  vsnprintf(buf, required_len, fmt, ap);
282 
283  /* strip one newline, for PQerrorMessage() */
284  if (required_len >= 2 && buf[required_len - 2] == '\n')
285  buf[required_len - 2] = '\0';
286 
287  fprintf(stderr, "%s\n", buf);
288 
289  free(buf);
290 }
static void(* log_locus_callback)(const char **, uint64 *)
Definition: logging.c:27
static char * argv0
Definition: pg_ctl.c:97
#define ANSI_ESCAPE_RESET
Definition: logging.c:38
const char * get_progname(const char *argv0)
Definition: path.c:453
void pg_logging_init(const char *argv0)
Definition: logging.c:81
pg_log_level
Definition: logging.h:16
void pg_logging_set_pre_callback(void(*cb)(void))
Definition: logging.c:167
#define MCXT_ALLOC_NO_OOM
Definition: fe_memutils.h:18
#define fprintf
Definition: port.h:197
#define ANSI_ESCAPE_FMT
Definition: logging.c:37
#define SGR_LOCUS_DEFAULT
Definition: logging.c:35
static void(* log_pre_callback)(void)
Definition: logging.c:26
#define vsnprintf
Definition: port.h:192
static const char * sgr_error
Definition: logging.c:29
static char * buf
Definition: pg_test_fsync.c:67
void * pg_malloc_extended(size_t size, int flags)
Definition: fe_memutils.c:59
static const char * sgr_warning
Definition: logging.c:30
void pg_logging_set_locus_callback(void(*cb)(const char **filename, uint64 *lineno))
Definition: logging.c:173
enum pg_log_level __pg_log_level
Definition: logging.c:21
static int log_flags
Definition: logging.c:24
static const char * progname
Definition: logging.c:23
static struct @143 value
void pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
Definition: logging.c:179
#define free(a)
Definition: header.h:65
#define Assert(condition)
Definition: c.h:738
void pg_logging_config(int new_flags)
Definition: logging.c:155
void pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
Definition: logging.c:189
#define SGR_ERROR_DEFAULT
Definition: logging.c:33
const char * name
Definition: encode.c:561
static char * filename
Definition: pg_dumpall.c:90
e
Definition: preproc-init.c:82
#define PG_LOG_FLAG_TERSE
Definition: logging.h:64
void pg_logging_set_level(enum pg_log_level new_level)
Definition: logging.c:161
#define vfprintf
Definition: port.h:196
static const char * sgr_locus
Definition: logging.c:31
#define _(x)
Definition: elog.c:88
#define UINT64_FORMAT
Definition: c.h:410
#define SGR_WARNING_DEFAULT
Definition: logging.c:34