summaryrefslogtreecommitdiff
path: root/src/common/logging.c
diff options
context:
space:
mode:
authorTom Lane2019-05-14 18:19:49 +0000
committerTom Lane2019-05-14 18:20:10 +0000
commitfc9a62af3f87f4bec1e8c904ea99ae50f3c881ef (patch)
treea566ea096bbebc18e307370917e061d49f725291 /src/common/logging.c
parentb71dad22ce8a645a47c01e544f640f35b91bfbd3 (diff)
Move logging.h and logging.c from src/fe_utils/ to src/common/.
The original placement of this module in src/fe_utils/ is ill-considered, because several src/common/ modules have dependencies on it, meaning that libpgcommon and libpgfeutils now have mutual dependencies. That makes it pointless to have distinct libraries at all. The intended design is that libpgcommon is lower-level than libpgfeutils, so only dependencies from the latter to the former are acceptable. We already have the precedent that fe_memutils and a couple of other modules in src/common/ are frontend-only, so it's not stretching anything out of whack to treat logging.c as a frontend-only module in src/common/. To the extent that such modules help provide a common frontend/backend environment for the rest of common/ to use, it's a reasonable design. (logging.c does not yet provide an ereport() emulation, but one can dream.) Hence, move these files over, and revert basically all of the build-system changes made by commit cc8d41511. There are no places that need to grow new dependencies on libpgcommon, further reinforcing the idea that this is the right solution. Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/common/logging.c')
-rw-r--r--src/common/logging.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/common/logging.c b/src/common/logging.c
new file mode 100644
index 00000000000..59f60445c72
--- /dev/null
+++ b/src/common/logging.c
@@ -0,0 +1,235 @@
+/*-------------------------------------------------------------------------
+ * Logging framework for frontend programs
+ *
+ * Copyright (c) 2018-2019, PostgreSQL Global Development Group
+ *
+ * src/common/logging.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <unistd.h>
+
+#include "common/logging.h"
+
+enum pg_log_level __pg_log_level;
+
+static const char *progname;
+static int log_flags;
+
+static void (*log_pre_callback)(void);
+static void (*log_locus_callback)(const char **, uint64 *);
+
+static const char *sgr_error = NULL;
+static const char *sgr_warning = NULL;
+static const char *sgr_locus = NULL;
+
+#define SGR_ERROR_DEFAULT "01;31"
+#define SGR_WARNING_DEFAULT "01;35"
+#define SGR_LOCUS_DEFAULT "01"
+
+#define ANSI_ESCAPE_FMT "\x1b[%sm"
+#define ANSI_ESCAPE_RESET "\x1b[0m"
+
+/*
+ * This should be called before any output happens.
+ */
+void
+pg_logging_init(const char *argv0)
+{
+ const char *pg_color_env = getenv("PG_COLOR");
+ bool log_color = false;
+
+ /* usually the default, but not on Windows */
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ progname = get_progname(argv0);
+ __pg_log_level = PG_LOG_INFO;
+
+ if (pg_color_env)
+ {
+ if (strcmp(pg_color_env, "always") == 0 ||
+ (strcmp(pg_color_env, "auto") == 0 && isatty(fileno(stderr))))
+ log_color = true;
+ }
+
+ if (log_color)
+ {
+ const char *pg_colors_env = getenv("PG_COLORS");
+
+ if (pg_colors_env)
+ {
+ char *colors = strdup(pg_colors_env);
+
+ if (colors)
+ {
+ for (char *token = strtok(colors, ":"); token; token = strtok(NULL, ":"))
+ {
+ char *e = strchr(token, '=');
+
+ if (e)
+ {
+ char *name;
+ char *value;
+
+ *e = '\0';
+ name = token;
+ value = e + 1;
+
+ if (strcmp(name, "error") == 0)
+ sgr_error = strdup(value);
+ if (strcmp(name, "warning") == 0)
+ sgr_warning = strdup(value);
+ if (strcmp(name, "locus") == 0)
+ sgr_locus = strdup(value);
+ }
+ }
+
+ free(colors);
+ }
+ }
+ else
+ {
+ sgr_error = SGR_ERROR_DEFAULT;
+ sgr_warning = SGR_WARNING_DEFAULT;
+ sgr_locus = SGR_LOCUS_DEFAULT;
+ }
+ }
+}
+
+void
+pg_logging_config(int new_flags)
+{
+ log_flags = new_flags;
+}
+
+void
+pg_logging_set_level(enum pg_log_level new_level)
+{
+ __pg_log_level = new_level;
+}
+
+void
+pg_logging_set_pre_callback(void (*cb)(void))
+{
+ log_pre_callback = cb;
+}
+
+void
+pg_logging_set_locus_callback(void (*cb)(const char **filename, uint64 *lineno))
+{
+ log_locus_callback = cb;
+}
+
+void
+pg_log_generic(enum pg_log_level level, const char * pg_restrict fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ pg_log_generic_v(level, fmt, ap);
+ va_end(ap);
+}
+
+void
+pg_log_generic_v(enum pg_log_level level, const char * pg_restrict fmt, va_list ap)
+{
+ int save_errno = errno;
+ const char *filename = NULL;
+ uint64 lineno = 0;
+ va_list ap2;
+ size_t required_len;
+ char *buf;
+
+ Assert(progname);
+ Assert(level);
+ Assert(fmt);
+ Assert(fmt[strlen(fmt) - 1] != '\n');
+
+ /*
+ * Flush stdout before output to stderr, to ensure sync even when stdout
+ * is buffered.
+ */
+ fflush(stdout);
+
+ if (log_pre_callback)
+ log_pre_callback();
+
+ if (log_locus_callback)
+ log_locus_callback(&filename, &lineno);
+
+ fmt = _(fmt);
+
+ if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+ {
+ if (sgr_locus)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
+ if (!(log_flags & PG_LOG_FLAG_TERSE))
+ fprintf(stderr, "%s:", progname);
+ if (filename)
+ {
+ fprintf(stderr, "%s:", filename);
+ if (lineno > 0)
+ fprintf(stderr, UINT64_FORMAT ":", lineno);
+ }
+ fprintf(stderr, " ");
+ if (sgr_locus)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ }
+
+ if (!(log_flags & PG_LOG_FLAG_TERSE))
+ {
+ switch (level)
+ {
+ case PG_LOG_FATAL:
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
+ fprintf(stderr, _("fatal: "));
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ break;
+ case PG_LOG_ERROR:
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
+ fprintf(stderr, _("error: "));
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ break;
+ case PG_LOG_WARNING:
+ if (sgr_warning)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
+ fprintf(stderr, _("warning: "));
+ if (sgr_warning)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ break;
+ default:
+ break;
+ }
+ }
+
+ errno = save_errno;
+
+ va_copy(ap2, ap);
+ required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
+ va_end(ap2);
+
+ buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM);
+
+ if (!buf)
+ {
+ /* memory trouble, just print what we can and get out of here */
+ vfprintf(stderr, fmt, ap);
+ return;
+ }
+
+ vsnprintf(buf, required_len, fmt, ap);
+
+ /* strip one newline, for PQerrorMessage() */
+ if (required_len >= 2 && buf[required_len - 2] == '\n')
+ buf[required_len - 2] = '\0';
+
+ fprintf(stderr, "%s\n", buf);
+
+ free(buf);
+}