elog.c 6.9 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * elog.c
4
 *	  error logger
5 6 7 8 9
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.42 1999/04/25 03:19:11 tgl Exp $
11 12 13 14 15 16 17 18 19
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#ifndef O_RDONLY
#include <sys/file.h>
20
#endif	 /* O_RDONLY */
21 22 23
#include <sys/types.h>
#include <stdarg.h>
#include <errno.h>
24 25
#include <unistd.h>
#include <signal.h>
26

M
 
Marc G. Fournier 已提交
27 28 29 30
#ifdef USE_SYSLOG
#include <syslog.h>
#endif

31 32 33 34
#include "postgres.h"
#include "miscadmin.h"
#include "libpq/libpq.h"
#include "storage/proc.h"
35
#include "tcop/tcopprot.h"
M
 
Marc G. Fournier 已提交
36 37 38 39 40 41
#include "utils/trace.h"

#ifdef USE_SYSLOG
/*
 * Global option to control the use of syslog(3) for logging:
 *
42 43 44
 *		0	stdout/stderr only
 *		1	stdout/stderr + syslog
 *		2	syslog only
M
 
Marc G. Fournier 已提交
45 46
 */
#define UseSyslog pg_options[OPT_SYSLOG]
47
#define PG_LOG_FACILITY LOG_LOCAL0
M
 
Marc G. Fournier 已提交
48 49 50
#else
#define UseSyslog 0
#endif
51

52 53 54
static int	Debugfile = -1;
static int	Err_file = -1;
static int	ElogDebugIndentLevel = 0;
55 56

/*
57
 * elog 
58
 *		Old error logging function.
59 60
 */
void
61
elog(int lev, const char *fmt,...)
62
{
63 64 65
	va_list		ap;
	char		buf[ELOG_MAXLEN],
				line[ELOG_MAXLEN];
66
	char	   *bp;
67
	const char *cp;
68 69
	extern int	errno,
				sys_nerr;
70

M
 
Marc G. Fournier 已提交
71 72
#ifdef USE_SYSLOG
	int			log_level;
73

B
Bruce Momjian 已提交
74
#endif
M
 
Marc G. Fournier 已提交
75

76 77
	int			len;
	int			i = 0;
78 79 80 81 82 83

	va_start(ap, fmt);
	if (lev == DEBUG && Debugfile < 0)
		return;
	switch (lev)
	{
84 85 86 87 88 89
		case NOIND:
			i = ElogDebugIndentLevel - 1;
			if (i < 0)
				i = 0;
			if (i > 30)
				i = i % 30;
B
Bruce Momjian 已提交
90
			cp = "DEBUG:  ";
91 92 93 94 95 96 97
			break;
		case DEBUG:
			i = ElogDebugIndentLevel;
			if (i < 0)
				i = 0;
			if (i > 30)
				i = i % 30;
B
Bruce Momjian 已提交
98
			cp = "DEBUG:  ";
99 100
			break;
		case NOTICE:
B
Bruce Momjian 已提交
101
			cp = "NOTICE:  ";
102
			break;
103
		case ERROR:
B
Bruce Momjian 已提交
104
			cp = "ERROR:  ";
105
			break;
106
		default:
B
Bruce Momjian 已提交
107
			sprintf(line, "FATAL %d:  ", lev);
108
			cp = line;
109
	}
110
#ifdef ELOG_TIMESTAMPS
M
 
Marc G. Fournier 已提交
111 112
	strcpy(buf, tprintf_timestamp());
	strcat(buf, cp);
113
#else
114
	strcpy(buf, cp);
115
#endif
M
 
Marc G. Fournier 已提交
116
	bp = buf + strlen(buf);
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	while (i-- > 0)
		*bp++ = ' ';
	for (cp = fmt; *cp; cp++)
		if (*cp == '%' && *(cp + 1) == 'm')
		{
			if (errno < sys_nerr && errno >= 0)
				strcpy(bp, strerror(errno));
			else
				sprintf(bp, "error %d", errno);
			bp += strlen(bp);
			cp++;
		}
		else
			*bp++ = *cp;
	*bp = '\0';
132
	vsnprintf(line, ELOG_MAXLEN - 1, buf, ap);
133
	va_end(ap);
M
 
Marc G. Fournier 已提交
134 135

#ifdef USE_SYSLOG
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	switch (lev)
	{
		case NOIND:
			log_level = LOG_DEBUG;
			break;
		case DEBUG:
			log_level = LOG_DEBUG;
			break;
		case NOTICE:
			log_level = LOG_NOTICE;
			break;
		case ERROR:
			log_level = LOG_WARNING;
			break;
		case FATAL:
		default:
			log_level = LOG_ERR;
			break;
M
 
Marc G. Fournier 已提交
154
	}
155
	write_syslog(log_level, line + TIMESTAMP_SIZE);
M
 
Marc G. Fournier 已提交
156 157
#endif

158
	len = strlen(strcat(line, "\n"));
M
 
Marc G. Fournier 已提交
159
	if ((Debugfile > -1) && (UseSyslog <= 1))
160 161 162 163
		write(Debugfile, line, len);
	if (lev == DEBUG || lev == NOIND)
		return;

164
	/*
165 166 167 168
	 * If there's an error log file other than our channel to the
	 * front-end program, write to it first.  This is important because
	 * there's a bug in the socket code on ultrix.  If the front end has
	 * gone away (so the channel to it has been closed at the other end),
169
	 * then writing here can cause this backend to exit without warning 
170 171 172
	 * that is, write() does an exit(). In this case, our only hope of
	 * finding out what's going on is if Err_file was set to some disk
	 * log.  This is a major pain.
173
	 */
174

M
 
Marc G. Fournier 已提交
175
	if (Err_file > -1 && Debugfile != Err_file && (UseSyslog <= 1))
176 177 178 179 180 181
	{
		if (write(Err_file, line, len) < 0)
		{
			write(open("/dev/console", O_WRONLY, 0666), line, len);
			fflush(stdout);
			fflush(stderr);
182
			proc_exit(lev);
183 184 185 186 187 188
		}
		fsync(Err_file);
	}

#ifndef PG_STANDALONE
	/* Send IPC message to the front-end program */
M
 
Marc G. Fournier 已提交
189
	if (IsUnderPostmaster && lev > DEBUG)
190
	{
191 192
		/* notices are not errors, handle 'em differently */
		char msgtype;
193
		if (lev == NOTICE)
194
			msgtype = 'N';
195
		else
196 197 198 199 200 201 202 203 204 205
		{
			/* Abort any COPY OUT in progress when an error is detected.
			 * This hack is necessary because of poor design of copy protocol.
			 */
			pq_endcopyout(true);
			msgtype = 'E';
		}
		/* exclude the timestamp from msg sent to frontend */
		pq_putmessage(msgtype, line + TIMESTAMP_SIZE,
					  strlen(line + TIMESTAMP_SIZE) + 1);
206 207 208 209 210 211 212 213
		/*
		 * This flush is normally not necessary, since postgres.c will
		 * flush out waiting data when control returns to the main loop.
		 * But it seems best to leave it here, so that the client has some
		 * clue what happened if the backend dies before getting back to the
		 * main loop ... error/notice messages should not be a performance-
		 * critical path anyway, so an extra flush won't hurt much ...
		 */
214 215
		pq_flush();
	}
M
 
Marc G. Fournier 已提交
216
	if (!IsUnderPostmaster)
217 218 219 220 221 222
	{

		/*
		 * There is no socket.	One explanation for this is we are running
		 * as the Postmaster.  So we'll write the message to stderr.
		 */
223 224
		fputs(line, stderr);
	}
225
#endif	 /* !PG_STANDALONE */
226

227
	if (lev == ERROR)
228 229
	{
		ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
230
		if (!InError)
231
		{
232 233
			/* exit to main loop */
			siglongjmp(Warn_restart, 1);
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
		}
	}

	if (lev == FATAL)
	{

		/*
		 * Assume that if we have detected the failure we can exit with a
		 * normal exit status.	This will prevent the postmaster from
		 * cleaning up when it's not needed.
		 */
		fflush(stdout);
		fflush(stderr);
		ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
		ProcReleaseLocks();		/* get rid of real locks we hold */
249
		proc_exit(0);
250 251 252 253 254 255
	}

	if (lev > FATAL)
	{
		fflush(stdout);
		fflush(stderr);
256
		proc_exit(lev);
257
	}
258 259 260 261
}

#ifndef PG_STANDALONE
int
262
DebugFileOpen(void)
263
{
264 265
	int			fd,
				istty;
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

	Err_file = Debugfile = -1;
	ElogDebugIndentLevel = 0;

	if (OutputFileName[0])
	{
		OutputFileName[MAXPGPATH - 1] = '\0';
		if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
					   0666)) < 0)
			elog(FATAL, "DebugFileOpen: open of %s: %m",
				 OutputFileName);
		istty = isatty(fd);
		close(fd);

		/*
		 * If the file is a tty and we're running under the postmaster,
		 * try to send stdout there as well (if it isn't a tty then stderr
		 * will block out stdout, so we may as well let stdout go wherever
		 * it was going before).
		 */
		if (istty &&
			IsUnderPostmaster &&
			!freopen(OutputFileName, "a", stdout))
			elog(FATAL, "DebugFileOpen: %s reopen as stdout: %m",
				 OutputFileName);
		if (!freopen(OutputFileName, "a", stderr))
			elog(FATAL, "DebugFileOpen: %s reopen as stderr: %m",
				 OutputFileName);
		Err_file = Debugfile = fileno(stderr);
295
		return Debugfile;
296 297 298 299 300
	}

	/*
	 * If no filename was specified, send debugging output to stderr. If
	 * stderr has been hosed, try to open a file.
301
	 */
302 303 304 305
	fd = fileno(stderr);
	if (fcntl(fd, F_GETFD, 0) < 0)
	{
		sprintf(OutputFileName, "%s/pg.errors.%d",
B
Bruce Momjian 已提交
306
				DataDir, (int) MyProcPid);
307 308 309 310 311 312
		fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY, 0666);
	}
	if (fd < 0)
		elog(FATAL, "DebugFileOpen: could not open debugging file");

	Err_file = Debugfile = fd;
313
	return Debugfile;
314
}
315

316
#endif