]> granicus.if.org Git - postgresql/blob - src/backend/utils/error/elog.c
Preliminary code cleanup in elog(). Split out some code into utility
[postgresql] / src / backend / utils / error / elog.c
1 /*-------------------------------------------------------------------------
2  *
3  * elog.c
4  *        error logger
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.86 2001/06/08 21:16:48 petere Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <time.h>
18 #include <fcntl.h>
19 #include <sys/types.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <sys/time.h>
24 #include <ctype.h>
25 #ifdef ENABLE_SYSLOG
26 #include <syslog.h>
27 #endif
28
29 #include "commands/copy.h"
30 #include "libpq/libpq.h"
31 #include "libpq/pqformat.h"
32 #include "miscadmin.h"
33 #include "storage/proc.h"
34 #include "tcop/tcopprot.h"
35 #include "utils/memutils.h"
36
37 #ifdef MULTIBYTE
38 #include "mb/pg_wchar.h"
39 #endif
40
41 #ifdef ENABLE_SYSLOG
42 /*
43  * 0 = only stdout/stderr
44  * 1 = stdout+stderr and syslog
45  * 2 = syslog only
46  * ... in theory anyway
47  */
48 int                     Use_syslog = 0;
49 char       *Syslog_facility;
50 char       *Syslog_ident;
51
52 static void write_syslog(int level, const char *line);
53
54 #else
55 #define Use_syslog 0
56 #endif
57
58 bool            Log_timestamp;
59 bool            Log_pid;
60
61 #define TIMESTAMP_SIZE 20               /* format `YYYY-MM-DD HH:MM:SS ' */
62 #define PID_SIZE 9                              /* format `[123456] ' */
63
64 static const char *print_timestamp(void);
65 static const char *print_pid(void);
66 static void send_notice_to_frontend(const char *msg);
67 static void send_error_to_frontend(const char *msg);
68 static const char *useful_strerror(int errnum);
69 static const char *elog_message_prefix(int lev);
70
71
72 static int      Debugfile = -1;
73
74
75 /*--------------------
76  * elog
77  *              Primary error logging function.
78  *
79  * 'lev': error level; indicates recovery action to take, if any.
80  * 'fmt': a printf-style string.
81  * Additional arguments, if any, are formatted per %-escapes in 'fmt'.
82  *
83  * In addition to the usual %-escapes recognized by printf, "%m" in
84  * fmt is replaced by the error message for the current value of errno.
85  *
86  * Note: no newline is needed at the end of the fmt string, since
87  * elog will provide one for the output methods that need it.
88  *
89  * If 'lev' is ERROR or worse, control does not return to the caller.
90  * See elog.h for the error level definitions.
91  *--------------------
92  */
93 void
94 elog(int lev, const char *fmt,...)
95 {
96         va_list         ap;
97
98         /*
99          * The expanded format and final output message are dynamically
100          * allocated if necessary, but not if they fit in the "reasonable
101          * size" buffers shown here.  In extremis, we'd rather depend on
102          * having a few hundred bytes of stack space than on malloc() still
103          * working (since memory-clobber errors often take out malloc first).
104          * Don't make these buffers unreasonably large though, on pain of
105          * having to chase a bug with no error message.
106          *
107          * Note that we use malloc() not palloc() because we want to retain
108          * control if we run out of memory.  palloc() would recursively call
109          * elog(ERROR), which would be all right except if we are working on a
110          * FATAL or REALLYFATAL error.  We'd lose track of the fatal condition
111          * and report a mere ERROR to outer loop, which would be a Bad Thing.
112          * So, we substitute an appropriate message in-place, without
113          * downgrading the level if it's above ERROR.
114          */
115         char            fmt_fixedbuf[128];
116         char            msg_fixedbuf[256];
117         char       *fmt_buf = fmt_fixedbuf;
118         char       *msg_buf = msg_fixedbuf;
119
120         /* for COPY line numbers */
121         char            copylineno_buf[32];
122
123         const char *errorstr;
124         const char *prefix;
125         const char *cp;
126         char       *bp;
127         size_t          space_needed;
128
129         /* size of the prefix needed for timestamp and pid, if enabled */
130         size_t          timestamp_size;
131
132         /* ignore debug msgs if noplace to send */
133         if (lev == DEBUG && Debugfile < 0)
134                 return;
135
136         /* Save error str before calling any function that might change errno */
137         errorstr = useful_strerror(errno);
138
139         /*
140          * Convert initialization errors into fatal errors. This is
141          * probably redundant, because Warn_restart_ready won't be set
142          * anyway.
143          */
144         if (lev == ERROR && IsInitProcessingMode())
145                 lev = FATAL;
146
147         /*
148          * If we are inside a critical section, all errors become
149          * REALLYFATAL errors.  See miscadmin.h.
150          */
151         if (lev == ERROR || lev == FATAL)
152         {
153                 if (CritSectionCount > 0)
154                         lev = REALLYFATAL;
155         }
156
157         prefix = elog_message_prefix(lev);
158
159         timestamp_size = 0;
160         if (Log_timestamp)
161                 timestamp_size += TIMESTAMP_SIZE;
162         if (Log_pid)
163                 timestamp_size += PID_SIZE;
164
165         fmt = gettext(fmt);
166         /*
167          * Set up the expanded format, consisting of the prefix string plus
168          * input format, with any %m replaced by strerror() string (since
169          * vsnprintf won't know what to do with %m).  To keep space
170          * calculation simple, we only allow one %m.
171          */
172         space_needed = timestamp_size + strlen(prefix)
173                 + strlen(fmt) + strlen(errorstr) + 1;
174
175         if (copy_lineno)
176         {
177                 /* translator: This string will be truncated at 31 characters. */
178                 snprintf(copylineno_buf, 32, gettext("copy: line %d, "), copy_lineno);
179                 space_needed += strlen(copylineno_buf);
180         }
181
182         if (space_needed > sizeof(fmt_fixedbuf))
183         {
184                 fmt_buf = malloc(space_needed);
185                 if (fmt_buf == NULL)
186                 {
187                         /* We're up against it, convert to out-of-memory error */
188                         fmt_buf = fmt_fixedbuf;
189                         if (lev != FATAL && lev != REALLYFATAL)
190                         {
191                                 lev = ERROR;
192                                 prefix = elog_message_prefix(lev);
193                         }
194                         /*
195                          * gettext doesn't allocate memory, except in the very
196                          * first call (which this isn't), so it's safe to
197                          * translate here.  Worst case we get the untranslated
198                          * string back.
199                          */
200                         /* translator: This must fit in fmt_fixedbuf. */
201                         fmt = gettext("elog: out of memory");
202                 }
203         }
204
205         fmt_buf[0] = '\0';
206
207         if (Log_timestamp)
208                 strcat(fmt_buf, print_timestamp());
209         if (Log_pid)
210                 strcat(fmt_buf, print_pid());
211
212         strcat(fmt_buf, prefix);
213
214         /* If error was in CopyFrom() print the offending line number -- dz */
215         if (copy_lineno)
216         {
217                 strcat(fmt_buf, copylineno_buf);
218                 if (lev == ERROR || lev == FATAL || lev == REALLYFATAL)
219                         copy_lineno = 0;
220         }
221
222         bp = fmt_buf + strlen(fmt_buf);
223
224         for (cp = fmt; *cp; cp++)
225         {
226                 if (cp[0] == '%' && cp[1] != '\0')
227                 {
228                         if (cp[1] == 'm')
229                         {
230                                 /*
231                                  * XXX If there are any %'s in errorstr then vsnprintf
232                                  * will do the Wrong Thing; do we need to cope? Seems
233                                  * unlikely that % would appear in system errors.
234                                  */
235                                 strcpy(bp, errorstr);
236
237                                 /*
238                                  * copy the rest of fmt literally, since we can't afford
239                                  * to insert another %m.
240                                  */
241                                 strcat(bp, cp + 2);
242                                 bp += strlen(bp);
243                                 break;
244                         }
245                         else
246                         {
247                                 /* copy % and next char --- this avoids trouble with %%m */
248                                 *bp++ = *cp++;
249                                 *bp++ = *cp;
250                         }
251                 }
252                 else
253                         *bp++ = *cp;
254         }
255         *bp = '\0';
256
257         /*
258          * Now generate the actual output text using vsnprintf(). Be sure to
259          * leave space for \n added later as well as trailing null.
260          */
261         space_needed = sizeof(msg_fixedbuf);
262         for (;;)
263         {
264                 int                     nprinted;
265
266                 va_start(ap, fmt);
267                 nprinted = vsnprintf(msg_buf, space_needed - 2, fmt_buf, ap);
268                 va_end(ap);
269
270                 /*
271                  * Note: some versions of vsnprintf return the number of chars
272                  * actually stored, but at least one returns -1 on failure. Be
273                  * conservative about believing whether the print worked.
274                  */
275                 if (nprinted >= 0 && nprinted < space_needed - 3)
276                         break;
277                 /* It didn't work, try to get a bigger buffer */
278                 if (msg_buf != msg_fixedbuf)
279                         free(msg_buf);
280                 space_needed *= 2;
281                 msg_buf = malloc(space_needed);
282                 if (msg_buf == NULL)
283                 {
284                         /* We're up against it, convert to out-of-memory error */
285                         msg_buf = msg_fixedbuf;
286                         if (lev != FATAL && lev != REALLYFATAL)
287                         {
288                                 lev = ERROR;
289                                 prefix = elog_message_prefix(lev);
290                         }
291                         msg_buf[0] = '\0';
292                         if (Log_timestamp)
293                                 strcat(msg_buf, print_timestamp());
294                         if (Log_pid)
295                                 strcat(msg_buf, print_pid());
296                         strcat(msg_buf, prefix);
297                         strcat(msg_buf, gettext("elog: out of memory"));
298                         break;
299                 }
300         }
301
302         /*
303          * Message prepared; send it where it should go
304          */
305
306 #ifdef ENABLE_SYSLOG
307         /* Write to syslog, if enabled */
308         if (Use_syslog >= 1)
309         {
310                 int                     syslog_level;
311
312                 switch (lev)
313                 {
314                         case DEBUG:
315                                 syslog_level = LOG_DEBUG;
316                                 break;
317                         case NOTICE:
318                                 syslog_level = LOG_NOTICE;
319                                 break;
320                         case ERROR:
321                                 syslog_level = LOG_WARNING;
322                                 break;
323                         case FATAL:
324                                 syslog_level = LOG_ERR;
325                                 break;
326                         case REALLYFATAL:
327                         default:
328                                 syslog_level = LOG_CRIT;
329                                 break;
330                 }
331
332                 write_syslog(syslog_level, msg_buf + timestamp_size);
333         }
334 #endif /* ENABLE_SYSLOG */
335
336         /* syslog doesn't want a trailing newline, but other destinations do */
337         strcat(msg_buf, "\n");
338
339         /* Write to debug file, if open and enabled */
340         /* NOTE: debug file is typically pointed at stderr */
341         if (Debugfile >= 0 && Use_syslog <= 1)
342                 write(Debugfile, msg_buf, strlen(msg_buf));
343
344         if (lev > DEBUG && whereToSendOutput == Remote)
345         {
346                 /* Send IPC message to the front-end program */
347                 MemoryContext oldcxt;
348
349                 /*
350                  * Since backend libpq may call palloc(), switch to a context
351                  * where there's fairly likely to be some free space.  After all
352                  * the pushups above, we don't want to drop the ball by running
353                  * out of space now...
354                  */
355                 oldcxt = MemoryContextSwitchTo(ErrorContext);
356
357                 if (lev == NOTICE)
358                         /* exclude the timestamp from msg sent to frontend */
359                         send_notice_to_frontend(msg_buf + timestamp_size);
360                 else
361                 {
362                         /*
363                          * Abort any COPY OUT in progress when an error is detected.
364                          * This hack is necessary because of poor design of copy
365                          * protocol.
366                          */
367                         pq_endcopyout(true);
368                         send_error_to_frontend(msg_buf + timestamp_size);
369                 }
370
371                 MemoryContextSwitchTo(oldcxt);
372         }
373
374         if (lev > DEBUG && whereToSendOutput != Remote)
375         {
376                 /*
377                  * We are running as an interactive backend, so just send the
378                  * message to stderr.  But don't send a duplicate if Debugfile
379                  * write, above, already sent to stderr.
380                  */
381                 if (Debugfile != fileno(stderr))
382                         fputs(msg_buf, stderr);
383         }
384
385         /* done with the message, release space */
386         if (fmt_buf != fmt_fixedbuf)
387                 free(fmt_buf);
388         if (msg_buf != msg_fixedbuf)
389                 free(msg_buf);
390
391         /*
392          * Perform error recovery action as specified by lev.
393          */
394         if (lev == ERROR || lev == FATAL)
395         {
396                 /* Prevent immediate interrupt while entering error recovery */
397                 ImmediateInterruptOK = false;
398
399                 /*
400                  * For a FATAL error, we let proc_exit clean up and exit.
401                  *
402                  * If we have not yet entered the main backend loop (ie, we are in
403                  * the postmaster or in backend startup), we also go directly to
404                  * proc_exit.  The same is true if anyone tries to report an error
405                  * after proc_exit has begun to run.  (It's proc_exit's
406                  * responsibility to see that this doesn't turn into infinite
407                  * recursion!)  But in the latter case, we exit with nonzero exit
408                  * code to indicate that something's pretty wrong.  We also want
409                  * to exit with nonzero exit code if not running under the
410                  * postmaster (for example, if we are being run from the initdb
411                  * script, we'd better return an error status).
412                  */
413                 if (lev == FATAL || !Warn_restart_ready || proc_exit_inprogress)
414                 {
415                         /*
416                          * fflush here is just to improve the odds that we get to see
417                          * the error message, in case things are so hosed that
418                          * proc_exit crashes.  Any other code you might be tempted to
419                          * add here should probably be in an on_proc_exit callback
420                          * instead.
421                          */
422                         fflush(stdout);
423                         fflush(stderr);
424                         proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
425                 }
426
427                 /*
428                  * Guard against infinite loop from elog() during error recovery.
429                  */
430                 if (InError)
431                         elog(REALLYFATAL, "elog: error during error recovery, giving up!");
432                 InError = true;
433
434                 /*
435                  * Otherwise we can return to the main loop in postgres.c.
436                  */
437                 siglongjmp(Warn_restart, 1);
438         }
439
440         if (lev == FATAL || lev == REALLYFATAL)
441         {
442                 /*
443                  * Serious crash time. Postmaster will observe nonzero process
444                  * exit status and kill the other backends too.
445                  *
446                  * XXX: what if we are *in* the postmaster?  proc_exit() won't kill
447                  * our children...
448                  */
449                 ImmediateInterruptOK = false;
450                 fflush(stdout);
451                 fflush(stderr);
452                 proc_exit(lev);
453         }
454
455         /* We reach here if lev <= NOTICE.      OK to return to caller. */
456 }
457
458
459 int
460 DebugFileOpen(void)
461 {
462         int                     fd,
463                                 istty;
464
465         Debugfile = -1;
466
467         if (OutputFileName[0])
468         {
469                 /*
470                  * A debug-output file name was given.
471                  *
472                  * Make sure we can write the file, and find out if it's a tty.
473                  */
474                 if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
475                                            0666)) < 0)
476                         elog(FATAL, "DebugFileOpen: open of %s: %m",
477                                  OutputFileName);
478                 istty = isatty(fd);
479                 close(fd);
480
481                 /*
482                  * Redirect our stderr to the debug output file.
483                  */
484                 if (!freopen(OutputFileName, "a", stderr))
485                         elog(FATAL, "DebugFileOpen: %s reopen as stderr: %m",
486                                  OutputFileName);
487                 Debugfile = fileno(stderr);
488
489                 /*
490                  * If the file is a tty and we're running under the postmaster,
491                  * try to send stdout there as well (if it isn't a tty then stderr
492                  * will block out stdout, so we may as well let stdout go wherever
493                  * it was going before).
494                  */
495                 if (istty && IsUnderPostmaster)
496                         if (!freopen(OutputFileName, "a", stdout))
497                                 elog(FATAL, "DebugFileOpen: %s reopen as stdout: %m",
498                                          OutputFileName);
499                 return Debugfile;
500         }
501
502         /*
503          * If no filename was specified, send debugging output to stderr. If
504          * stderr has been hosed, try to open a file.
505          */
506         fd = fileno(stderr);
507         if (fcntl(fd, F_GETFD, 0) < 0)
508         {
509                 snprintf(OutputFileName, MAXPGPATH, "%s/pg.errors.%d",
510                                  DataDir, (int) MyProcPid);
511                 fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY, 0666);
512         }
513         if (fd < 0)
514                 elog(FATAL, "DebugFileOpen: could not open debugging file");
515
516         Debugfile = fd;
517         return Debugfile;
518 }
519
520
521 /*
522  * Return a timestamp string like
523  *
524  *       "2000-06-04 13:12:03 "
525  */
526 static const char *
527 print_timestamp(void)
528 {
529         time_t          curtime;
530         static char buf[TIMESTAMP_SIZE + 1];
531
532         curtime = time(NULL);
533
534         strftime(buf, sizeof(buf),
535                          "%Y-%m-%d %H:%M:%S ",
536                          localtime(&curtime));
537
538         return buf;
539 }
540
541
542
543 /*
544  * Return a string like
545  *
546  *         "[123456] "
547  *
548  * with the current pid.
549  */
550 static const char *
551 print_pid(void)
552 {
553         static char buf[PID_SIZE + 1];
554
555         snprintf(buf, PID_SIZE + 1, "[%d]      ", (int) MyProcPid);
556         return buf;
557 }
558
559
560
561 #ifdef ENABLE_SYSLOG
562
563 #ifndef PG_SYSLOG_LIMIT
564 #define PG_SYSLOG_LIMIT 128
565 #endif
566
567 /*
568  * Write a message line to syslog if the syslog option is set.
569  *
570  * Our problem here is that many syslog implementations don't handle
571  * long messages in an acceptable manner. While this function doesn't
572  * help that fact, it does work around by splitting up messages into
573  * smaller pieces.
574  */
575 static void
576 write_syslog(int level, const char *line)
577 {
578         static bool openlog_done = false;
579         static unsigned long seq = 0;
580         static int      syslog_fac = LOG_LOCAL0;
581
582         int                     len = strlen(line);
583
584         if (Use_syslog == 0)
585                 return;
586
587         if (!openlog_done)
588         {
589                 if (strcasecmp(Syslog_facility, "LOCAL0") == 0)
590                         syslog_fac = LOG_LOCAL0;
591                 if (strcasecmp(Syslog_facility, "LOCAL1") == 0)
592                         syslog_fac = LOG_LOCAL1;
593                 if (strcasecmp(Syslog_facility, "LOCAL2") == 0)
594                         syslog_fac = LOG_LOCAL2;
595                 if (strcasecmp(Syslog_facility, "LOCAL3") == 0)
596                         syslog_fac = LOG_LOCAL3;
597                 if (strcasecmp(Syslog_facility, "LOCAL4") == 0)
598                         syslog_fac = LOG_LOCAL4;
599                 if (strcasecmp(Syslog_facility, "LOCAL5") == 0)
600                         syslog_fac = LOG_LOCAL5;
601                 if (strcasecmp(Syslog_facility, "LOCAL6") == 0)
602                         syslog_fac = LOG_LOCAL6;
603                 if (strcasecmp(Syslog_facility, "LOCAL7") == 0)
604                         syslog_fac = LOG_LOCAL7;
605                 openlog(Syslog_ident, LOG_PID | LOG_NDELAY, syslog_fac);
606                 openlog_done = true;
607         }
608
609         /*
610          * We add a sequence number to each log message to suppress "same"
611          * messages.
612          */
613         seq++;
614
615         /* divide into multiple syslog() calls if message is too long */
616         /* or if the message contains embedded NewLine(s) '\n' */
617         if (len > PG_SYSLOG_LIMIT || strchr(line, '\n') != NULL)
618         {
619                 int                     chunk_nr = 0;
620
621                 while (len > 0)
622                 {
623                         char            buf[PG_SYSLOG_LIMIT + 1];
624                         int                     buflen;
625                         int                     l;
626                         int                     i;
627
628                         /* if we start at a newline, move ahead one char */
629                         if (line[0] == '\n')
630                         {
631                                 line++;
632                                 len--;
633                                 continue;
634                         }
635
636                         strncpy(buf, line, PG_SYSLOG_LIMIT);
637                         buf[PG_SYSLOG_LIMIT] = '\0';
638                         if (strchr(buf, '\n') != NULL)
639                                 *strchr(buf, '\n') = '\0';
640
641                         l = strlen(buf);
642 #ifdef MULTIBYTE
643                         /* trim to multibyte letter boundary */
644                         buflen = pg_mbcliplen(buf, l, l);
645                         if (buflen <= 0)
646                                 return;
647                         buf[buflen] = '\0';
648                         l = strlen(buf);
649 #endif
650                         /* already word boundary? */
651                         if (isspace((unsigned char) line[l]) || line[l] == '\0')
652                                 buflen = l;
653                         else
654                         {
655                                 /* try to divide at word boundary */
656                                 i = l - 1;
657                                 while (i > 0 && !isspace((unsigned char) buf[i]))
658                                         i--;
659
660                                 if (i <= 0)             /* couldn't divide word boundary */
661                                         buflen = l;
662                                 else
663                                 {
664                                         buflen = i;
665                                         buf[i] = '\0';
666                                 }
667                         }
668
669                         chunk_nr++;
670
671                         syslog(level, "[%lu-%d] %s", seq, chunk_nr, buf);
672                         line += buflen;
673                         len -= buflen;
674                 }
675         }
676         else
677         {
678                 /* message short enough */
679                 syslog(level, "[%lu] %s", seq, line);
680         }
681 }
682
683 #endif /* ENABLE_SYSLOG */
684
685
686
687 static void
688 send_notice_or_error_to_frontend(int type, const char *msg);
689
690
691 static void
692 send_notice_to_frontend(const char *msg)
693 {
694         send_notice_or_error_to_frontend(NOTICE, msg);
695 }
696
697
698 static void
699 send_error_to_frontend(const char *msg)
700 {
701         send_notice_or_error_to_frontend(ERROR, msg);
702 }
703
704
705 static void
706 send_notice_or_error_to_frontend(int type, const char *msg)
707 {
708         StringInfo buf;
709
710         AssertArg(type == NOTICE || type == ERROR);
711
712         buf = makeStringInfo();
713
714         pq_beginmessage(buf);
715         pq_sendbyte(buf, type == NOTICE ? 'N' : 'E');
716         pq_sendstring(buf, msg);
717         pq_endmessage(buf);
718
719         pfree(buf);
720         /*
721          * This flush is normally not necessary, since postgres.c will
722          * flush out waiting data when control returns to the main loop.
723          * But it seems best to leave it here, so that the client has some
724          * clue what happened if the backend dies before getting back to
725          * the main loop ... error/notice messages should not be a
726          * performance-critical path anyway, so an extra flush won't hurt
727          * much ...
728          */
729         pq_flush();
730 }
731
732
733 static const char *useful_strerror(int errnum)
734 {
735         /* this buffer is only used if errno has a bogus value */
736         static char     errorstr_buf[48];
737         char       *str;
738
739         str = strerror(errnum);
740
741         /*
742          * Some strerror()s return an empty string for out-of-range errno.
743          * This is ANSI C spec compliant, but not exactly useful.
744          */
745         if (str == NULL || *str == '\0')
746         {
747                 /* translator: This string will be truncated at 47 characters expanded. */
748                 snprintf(errorstr_buf, 48, gettext("operating system error %d"), errnum);
749                 str = errorstr_buf;
750         }
751
752         return str;
753 }
754
755
756
757 static const char *
758 elog_message_prefix(int lev)
759 {
760         const char * prefix = NULL;
761
762         switch (lev)
763         {
764                 case DEBUG:
765                         prefix = gettext("DEBUG:  ");
766                         break;
767                 case NOTICE:
768                         prefix = gettext("NOTICE:  ");
769                         break;
770                 case ERROR:
771                         prefix = gettext("ERROR:  ");
772                         break;
773                 case FATAL:
774                         prefix = gettext("FATAL 1:  ");
775                         break;
776                 case REALLYFATAL:
777                         prefix = gettext("FATAL 2:  ");
778                         break;
779         }
780
781         Assert(prefix != NULL);
782         return prefix;
783 }