aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/misc/trace.c350
-rw-r--r--src/include/utils/trace.h85
2 files changed, 435 insertions, 0 deletions
diff --git a/src/backend/utils/misc/trace.c b/src/backend/utils/misc/trace.c
new file mode 100644
index 00000000000..04148e7df40
--- /dev/null
+++ b/src/backend/utils/misc/trace.c
@@ -0,0 +1,350 @@
+/*-------------------------------------------------------------------------
+ *
+ * trace.c--
+ *
+ * Conditional trace ans logging functions.
+ *
+ * Massimo Dal Zotto <dz@cs.unitn.it>
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/trace.h"
+#include "libpq/pqsignal.h"
+
+#ifdef USE_SYSLOG
+/*
+ * Global option to control the use of syslog(3) for logging:
+ *
+ * 0 stdout/stderr only
+ * 1 stdout/stderr + syslog
+ * 2 syslog only
+ */
+#define UseSyslog pg_options[OPT_SYSLOG]
+#define PG_LOG_FACILITY LOG_LOCAL0
+#define PG_LOG_IDENT "postgres"
+#else
+#define UseSyslog 0
+#endif
+
+/*
+ * Trace option names, must match the constants in trace_opts[].
+ */
+static char *opt_names[] = {
+ "all",
+ "verbose",
+ "query",
+ "plan",
+ "parse",
+ "rewritten",
+ "parserstats",
+ "plannerstats",
+ "executorstats",
+ "shortlocks", /* currently unused but needed, see lock.c */
+ "locks",
+ "userlocks",
+ "spinlocks",
+ "notify",
+ "malloc",
+ "palloc",
+ "lock_debug_oidmin",
+ "lock_debug_relid",
+ "lock_read_priority", /* lock priority, see lock.c */
+ "deadlock_timeout", /* deadlock timeout, see proc.c */
+ "syslog", /* use syslog for error messages */
+ "hostlookup", /* enable hostname lookup in ps_status */
+ "showportnumber", /* show port number in ps_status */
+ "notifyunlock", /* enable unlock of pg_listener after notify */
+ "notifyhack" /* enable notify hack to remove duplicate tuples */
+};
+
+/*
+ * Array of trace flags which can be set or reset independently.
+ */
+int pg_options[NUM_PG_OPTIONS] = { 0 };
+
+static int openlog_done = 0;
+
+/*
+ * Print a timestamp and a message to stdout if the trace flag
+ * indexed by the flag value is set.
+ */
+int
+tprintf(int flag, const char *fmt, ... )
+{
+ va_list ap;
+ char line[ELOG_MAXLEN+TIMESTAMP_SIZE+1];
+
+#ifdef USE_SYSLOG
+ int log_level;
+#endif
+
+ if ((flag == TRACE_ALL) || (pg_options[TRACE_ALL] > 0)) {
+ /* uconditional trace or trace all option set */
+ } else if (pg_options[TRACE_ALL] == 0) {
+ if ((flag < 0) || (flag >= NUM_PG_OPTIONS) || (!pg_options[flag])) {
+ return 0;
+ }
+ } else if (pg_options[TRACE_ALL] < 0) {
+ return 0;
+ }
+
+ va_start(ap, fmt);
+#ifdef ELOG_TIMESTAMPS
+ strcpy(line, tprintf_timestamp());
+#endif
+ vsprintf(line+TIMESTAMP_SIZE, fmt, ap);
+ va_end(ap);
+
+#ifdef USE_SYSLOG
+ log_level = ((flag == TRACE_ALL) ? LOG_INFO : LOG_DEBUG);
+ write_syslog(log_level, line+TIMESTAMP_SIZE);
+#endif
+
+ if (UseSyslog <= 1) {
+ puts(line);
+ fflush(stdout);
+ }
+
+ return 1;
+}
+
+/*
+ * Print a timestamp and a message to stderr.
+ */
+int
+eprintf(const char *fmt, ... )
+{
+ va_list ap;
+ char line[ELOG_MAXLEN+TIMESTAMP_SIZE+1];
+
+ va_start(ap, fmt);
+#ifdef ELOG_TIMESTAMPS
+ strcpy(line, tprintf_timestamp());
+#endif
+ vsprintf(line+TIMESTAMP_SIZE, fmt, ap);
+ va_end(ap);
+
+#ifdef USE_SYSLOG
+ write_syslog(LOG_ERR, line+TIMESTAMP_SIZE);
+#endif
+
+ if (UseSyslog <= 1) {
+ fputs(line, stderr);
+ fputc('\n', stderr);
+ fflush(stderr);
+ }
+
+ return 1;
+}
+
+#ifdef USE_SYSLOG
+/*
+ * Write a message line to syslog if the syslog option is set.
+ */
+void
+write_syslog(int level, char *line)
+{
+ if (UseSyslog >= 1) {
+ if (!openlog_done) {
+ openlog_done = 1;
+ openlog(PG_LOG_IDENT, LOG_PID|LOG_NDELAY, PG_LOG_FACILITY);
+ }
+ syslog(level, "%s", line);
+ }
+}
+#endif
+
+#ifdef ELOG_TIMESTAMPS
+/*
+ * Return a timestamp string like "980119.17:25:59.902 [21974] "
+ */
+char *
+tprintf_timestamp()
+{
+ struct timeval tv;
+ struct tm *time;
+ time_t tm;
+ static char timestamp[32], pid[8];
+
+ gettimeofday(&tv, DST_NONE);
+ tm = tv.tv_sec;
+ time = localtime(&tm);
+
+ sprintf(pid, "[%d]", MyProcPid);
+ sprintf(timestamp, "%02d%02d%02d.%02d:%02d:%02d.%03d %7s ",
+ time->tm_year, time->tm_mon+1, time->tm_mday,
+ time->tm_hour, time->tm_min, time->tm_sec,
+ tv.tv_usec/1000, pid);
+
+ return timestamp;
+}
+#endif
+
+int
+option_flag(int flag)
+{
+ if ((flag < 0) || (flag >= NUM_PG_OPTIONS)) {
+ return 0;
+ }
+ return pg_options[flag];
+}
+
+int
+set_option_flag(int flag, int value)
+{
+ if ((flag < 0) || (flag >= NUM_PG_OPTIONS)) {
+ return -1;
+ }
+
+ pg_options[flag] = value;
+ return value;
+}
+
+/*
+ * Parse an option string like "name,name+,name-,name=value".
+ * Single options are delimited by ',',space,tab,newline or cr.
+ */
+void
+parse_options(char *str)
+{
+ char *s,
+ *name;
+ int i,
+ len,
+ val,
+ is_comment;
+
+ Assert((sizeof(opt_names)/sizeof(char*)) == NUM_PG_OPTIONS);
+
+ str = strdup(str);
+ for (s=str; *s;) {
+ is_comment = 0;
+ name = s;
+ val = 1;
+ for (; *s; s++) {
+ switch (*s) {
+ case '#':
+ is_comment = 1;
+ break;
+ case '=':
+ *s++ = '\0';
+ val = strtol(s, &s, 10);
+ goto setval;
+ case '-':
+ *s++ = '\0';
+ val = 0;
+ goto setval;
+ case '+':
+ *s++ = '\0';
+ val = 1;
+ goto setval;
+ case ' ':
+ case ',':
+ case '\t':
+ case '\n':
+ case '\r':
+ *s = ',';
+ val = 1;
+ goto setval;
+ }
+ }
+ setval:
+ for (; *s; s++) {
+ if (*s == ',') {
+ *s++ = '\0';
+ break;
+ }
+ }
+ len = strlen(name);
+ if (len == 0) {
+ continue;
+ }
+ for (i=0; i<NUM_PG_OPTIONS; i++) {
+ if (strncmp(name, opt_names[i], len) == 0) {
+ pg_options[i] = val;
+ break;
+ }
+ }
+ if (!is_comment && (i >= NUM_PG_OPTIONS)) {
+ fprintf(stderr, "invalid option: %s\n", name);
+ }
+ }
+ free(str);
+}
+
+#define BUF_SIZE 4096
+
+void
+read_pg_options(SIGNAL_ARGS)
+{
+ int fd;
+ int n;
+ int verbose;
+ char buffer[BUF_SIZE];
+ char c;
+ char *s,
+ *p;
+
+ sprintf(buffer, "%s/%s", DataDir, "pg_options");
+ if ((fd = open(buffer, O_RDONLY)) < 0) {
+ return;
+ }
+
+ if ((n = read(fd, buffer, BUF_SIZE-1)) > 0) {
+ /* collpse buffer in place removing comments and spaces */
+ for (s=buffer,p=buffer,c='\0'; s<(buffer+n); ) {
+ switch (*s) {
+ case '#':
+ while ((s < (buffer+n)) && (*s++ != '\n'));
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ if (c != ',')
+ c = *p++ = ',';
+ s++;
+ break;
+ default:
+ c = *p++ = *s++;
+ break;
+ }
+ }
+ if (c == ',')
+ p--;
+ *p = '\0';
+ verbose = pg_options[TRACE_VERBOSE];
+ parse_options(buffer);
+ verbose |= pg_options[TRACE_VERBOSE];
+ if (verbose || postgres_signal_arg == SIGHUP) {
+ tprintf(TRACE_ALL, "read_pg_options: %s", buffer);
+ }
+ }
+
+ close(fd);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/src/include/utils/trace.h b/src/include/utils/trace.h
new file mode 100644
index 00000000000..7884e197fd2
--- /dev/null
+++ b/src/include/utils/trace.h
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * trace.h--
+ *
+ * Conditional trace definitions.
+ *
+ * Massimo Dal Zotto <dz@cs.unitn.it>
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include "postgres.h"
+
+#ifdef ELOG_TIMESTAMPS
+char *tprintf_timestamp(void);
+#define TIMESTAMP_SIZE 28
+#else
+#define TIMESTAMP_SIZE 0
+#endif
+
+extern int tprintf(int flag, const char *fmt, ...);
+extern int eprintf(const char *fmt, ...);
+extern int option_flag(int flag);
+extern int set_option_flag(int flag, int value);
+extern void write_syslog(int level, char *line);
+extern void parse_options(char *str);
+extern void read_pg_options(SIGNAL_ARGS);
+
+/*
+ * Trace options, used as index into pg_options.
+ * Must match the constants in pg_options[].
+ */
+enum pg_option_enum {
+ TRACE_ALL, /* 0=trace some, 1=trace all, -1=trace none */
+ TRACE_VERBOSE,
+ TRACE_QUERY,
+ TRACE_PLAN,
+ TRACE_PARSE,
+ TRACE_REWRITTEN,
+ TRACE_PARSERSTATS,
+ TRACE_PLANNERSTATS,
+ TRACE_EXECUTORSTATS,
+ TRACE_SHORTLOCKS, /* currently unused but needed, see lock.c */
+ TRACE_LOCKS,
+ TRACE_USERLOCKS,
+ TRACE_SPINLOCKS,
+ TRACE_NOTIFY,
+ TRACE_MALLOC,
+ TRACE_PALLOC,
+ TRACE_LOCKOIDMIN,
+ TRACE_LOCKRELATION,
+ OPT_LOCKREADPRIORITY, /* lock priority, see lock.c */
+ OPT_DEADLOCKTIMEOUT, /* deadlock timeout, see proc.c */
+ OPT_SYSLOG, /* use syslog for error messages */
+ OPT_HOSTLOOKUP, /* enable hostname lookup in ps_status */
+ OPT_SHOWPORTNUMBER, /* show port number in ps_status */
+ OPT_NOTIFYUNLOCK, /* enable unlock of pg_listener after notify */
+ OPT_NOTIFYHACK, /* enable notify hack to remove duplicate tuples */
+
+ NUM_PG_OPTIONS /* must be the last item of enum */
+};
+
+extern int pg_options[NUM_PG_OPTIONS];
+
+#define PRINTF(args...) tprintf(TRACE_ALL, args)
+#define EPRINTF(args...) eprintf(args)
+#define TPRINTF(flag, args...) tprintf(flag, args)
+
+#endif /* TRACE_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * End:
+ */