Обсуждение: Frontend error logging style

Поиск
Список
Период
Сортировка

Frontend error logging style

От
Tom Lane
Дата:
ISTM that the recently-introduced new frontend logging support
(common/logging.h et al) could use a second pass now that we have
some experience with it.  There are two points that are bugging me:

1.  The distinction between "error" and "fatal" levels seems squishy
to the point of uselessness.  I think we should either get rid of it
entirely, or make an effort to use "fatal" exactly for the cases that
are going to give up and exit right away.  Of the approximately 830
pg_log_error calls in HEAD, I count at least 450 that are immediately
followed by exit(1), and so should be pg_log_fatal if this distinction
means anything at all.  OTOH, if we decide it doesn't mean anything,
there are only about 90 pg_log_fatal calls to convert.  I lean
slightly to the "get rid of the distinction" option, not only because
it'd be a much smaller patch but also because I don't care to expend
brain cells on the which-to-use question while reviewing future
patches.

2. What is the preferred style for adding extra lines to log messages?
I see a lot of direct prints to stderr:

        pg_log_error("missing required argument: database name");
        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
        exit(1);

but a number of places have chosen to do this:

        pg_log_error("query failed: %s", PQerrorMessage(conn));
        pg_log_error("query was: %s", todo);

and some places got creative and did this:

        pg_log_error("query failed: %s", PQerrorMessage(conn));
        pg_log_info("query was: %s", sql.data);

I think this ought to be cleaned up so we have a more-or-less uniform
approach.  Aside from the randomly different source code, each of
these choices has different implications for whether the extra line
gets printed or not depending on verbosity level, and that seems bad.

One plausible choice is to drop the first style (which surely has
little to recommend it) and use either the second or third style
depending on whether you think the addendum ought to appear at the
same or higher verbosity level as the main message.  But we'd
still be at hazard of people making randomly different choices
about that in identical cases, as indeed the second and third
examples show.

Another idea is to reduce such cases to one call:

        pg_log_error("query failed: %s\nquery was: %s",
                     PQerrorMessage(conn), sql.data);

but I don't much like that: it knows more than it should about
the presentation format, and it can't support hiding the detail
portion at lower verbosity levels.

So I'm not totally satisfied with these ideas, but I don't
immediately have a better one.

Thoughts?

            regards, tom lane



Re: Frontend error logging style

От
Kyotaro Horiguchi
Дата:
At Tue, 09 Nov 2021 17:20:41 -0500, Tom Lane <tgl@sss.pgh.pa.us> wrote in 
> ISTM that the recently-introduced new frontend logging support
> (common/logging.h et al) could use a second pass now that we have
> some experience with it.  There are two points that are bugging me:
> 
> 1.  The distinction between "error" and "fatal" levels seems squishy
> to the point of uselessness.  I think we should either get rid of it
> entirely, or make an effort to use "fatal" exactly for the cases that
> are going to give up and exit right away.  Of the approximately 830
> pg_log_error calls in HEAD, I count at least 450 that are immediately
> followed by exit(1), and so should be pg_log_fatal if this distinction
> means anything at all.  OTOH, if we decide it doesn't mean anything,
> there are only about 90 pg_log_fatal calls to convert.  I lean
> slightly to the "get rid of the distinction" option, not only because
> it'd be a much smaller patch but also because I don't care to expend
> brain cells on the which-to-use question while reviewing future
> patches.

Agreed. On backends, FATAL is bound to a behavior defferent from
ERROR.  I doubt of the necessity of the difference between the two on
a console application (or frontend in PG-term).  I would prefer to get
rid of the FATAL level.

I once thought ERROR should cause outoright end of an application, but
things don't seem so simple..

> 2. What is the preferred style for adding extra lines to log messages?
> I see a lot of direct prints to stderr:
> 
>         pg_log_error("missing required argument: database name");
>         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
>         exit(1);
> 
> but a number of places have chosen to do this:
> 
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_error("query was: %s", todo);
> 
> and some places got creative and did this:
> 
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_info("query was: %s", sql.data);

In the first place the pg_log_info doesn't work at all since pgbench
doesn't make the log-level go below PG_LOG_INFO. (--quiet works the
different way..)  In that case the pg_log_info can even be said to be
used just to avoid the message from being prepended by a severity tag.
I think we should fix that.

> I think this ought to be cleaned up so we have a more-or-less uniform
> approach.  Aside from the randomly different source code, each of
> these choices has different implications for whether the extra line
> gets printed or not depending on verbosity level, and that seems bad.
> 
> One plausible choice is to drop the first style (which surely has
> little to recommend it) and use either the second or third style
> depending on whether you think the addendum ought to appear at the
> same or higher verbosity level as the main message.  But we'd
> still be at hazard of people making randomly different choices
> about that in identical cases, as indeed the second and third
> examples show.
> 
> Another idea is to reduce such cases to one call:
> 
>         pg_log_error("query failed: %s\nquery was: %s",
>                      PQerrorMessage(conn), sql.data);
> 
> but I don't much like that: it knows more than it should about
> the presentation format, and it can't support hiding the detail
> portion at lower verbosity levels.
> 
> So I'm not totally satisfied with these ideas, but I don't
> immediately have a better one.
> 
> Thoughts?

Honestly I don't like that:p

The cause of the confusion is we are using verbosity to hide *a part
of* a grouped messages.  We make distinction beween the two concepts
in the backend code but not in frontend.  At least in pgbench, it
seems to me as if the severity tag prepended by the pg_log_hoge
functions is mere a label just to be shown on console.

That being said it is a kind of impssible to expect users to specify
the two independent levels on command line.

Couldn't we have a function, say, named as "pg_log_error_detail",
which prints the message when __pg_log_level is *above* ERROR and
prepended by "detail:" tag? (or not showing the tag, keeping the
current behavior on surface.)  We would have the same for
pg_log_warning. (pg_log_error_detail behaves a bit differetnly from
pg_log_info in this definition.)

>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_error_detail("query was: %s", sql.data);

So pg_log_generic(_v) have an additional parameter, say, detailed bool.

pg_log_generic(enum pg_log_level level, bool detailed, const char *pg_restrict fmt,...)

Considering that the level is used only to identify severity tag
string, it is somewhat strange that the detailed flag conflicts with
it. But I'm not sure it is sane to add instead an extra-and-hidden log
level to pg_log_level..


Or as a simpler way, we could just have aliases for pg_log_info().

#define pg_log_error_detail(...)  pg_log_info(__VA_ARGS__)
#define pg_log_warning_detail(...)  pg_log_info(__VA_ARGS__)

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center



Re: Frontend error logging style

От
Robert Haas
Дата:
On Tue, Nov 9, 2021 at 5:20 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> 1.  The distinction between "error" and "fatal" levels seems squishy
> to the point of uselessness.
>
> 2. What is the preferred style for adding extra lines to log messages?

I agree with this list of problems. I think that the end game here is
getting to be able to use ereport() and friends in the frontend, which
would require confronting both of these issues at a deep level. We
don't necessarily have to do that now, though, but I think it's an
argument against just nuking "fatal" from orbit. What I think we ought
to be driving towards is having pg_log_fatal() forcibly exit, and
pg_log_error() do the same unless the error is somehow caught. That
might require more changes than you or whoever wants to do right now,
so perhaps what we ought to do is just enforce the policy you
suggested before: if we're going to exit immediately afterward, it's
fatal; if not, it's an error.

I have been wondering for some time about trying to make the frontend
and backend facilities symmetric and using case to distinguish. That
is, if you see:

ERROR: this stinks
DETAIL: It smells very bad.
CONTEXT: garbage dump

...well then that's a backend message. And if you see:

error: this stinks
detail: It smells very bad.
context: garbage dump

...well then that's a frontend message. I don't completely love that
way of making a distinction, but I think we need something, and that's
pretty nearly the present practice at least for the primary message.
We don't really have a solid convention for detail/context/hint on the
FE side; this is one idea.

-- 
Robert Haas
EDB: http://www.enterprisedb.com



Re: Frontend error logging style

От
Tom Lane
Дата:
Robert Haas <robertmhaas@gmail.com> writes:
> I agree with this list of problems. I think that the end game here is
> getting to be able to use ereport() and friends in the frontend, which
> would require confronting both of these issues at a deep level. We
> don't necessarily have to do that now, though, but I think it's an
> argument against just nuking "fatal" from orbit. What I think we ought
> to be driving towards is having pg_log_fatal() forcibly exit, and
> pg_log_error() do the same unless the error is somehow caught.

Perhaps.  The usage that I'm concerned about is exemplified by this
common pattern:

        pg_log_error("%s", PQerrorMessage(conn));
        PQfinish(conn);
        exit(1);

ie there's some cleanup you want to do between emitting the error and
actually exiting.  (I grant that maybe someday the PQfinish could be
done in an atexit handler or the like, but that's way more redesign
than I want to do right now.)  In the case where you *don't* have
any cleanup to do, though, it'd be nice to just write one line not
two.

That leads me to think that we should redefine pg_log_fatal as

#define pg_log_fatal(...) (pg_log_error(__VA_ARGS__), exit(1))

We still want to get rid of the distinct PG_LOG_FATAL log level,
because whether you are using this form of pg_log_fatal or writing
exit() separately is a coding detail that should not manifest
as visibly distinct user output.

This proposal doesn't move us very far towards your endgame,
but I think it's a reasonable incremental step, and it makes
the difference between pg_log_error() and pg_log_fatal()
clear and useful.

> I have been wondering for some time about trying to make the frontend
> and backend facilities symmetric and using case to distinguish. That
> is, if you see:

> ERROR: this stinks
> DETAIL: It smells very bad.
> CONTEXT: garbage dump

> ...well then that's a backend message. And if you see:

> error: this stinks
> detail: It smells very bad.
> context: garbage dump

> ...well then that's a frontend message. I don't completely love that
> way of making a distinction, but I think we need something, and that's
> pretty nearly the present practice at least for the primary message.
> We don't really have a solid convention for detail/context/hint on the
> FE side; this is one idea.

Hmm, interesting.  Taking up my point #2, I'd been thinking about
proposing that we convert

        pg_log_error("query failed: %s", PQerrorMessage(conn));
        pg_log_error("query was: %s", todo);

to

        pg_log_error("query failed: %s", PQerrorMessage(conn));
        pg_log_error_detail("Query was: %s", todo);

and similarly add a pg_log_info_detail() companion for pg_log_info(), etc.
With your point above, the prefix these'd print would be "detail:" not
"DETAIL:", but otherwise it seems to match up pretty well.

Also, if we are modeling this on backend conventions, we should say that
style rules for pg_log_error_detail match those for errdetail(), with
complete sentences and so forth.  I wonder whether "detail:" followed
by a capitalized sentence would look weird ... but you did it above,
so maybe that's what people would expect.

Again, there's more that could be done later, but this seems to be
enough to address the cases that people have been inventing ad-hoc
solutions for.

            regards, tom lane



Re: Frontend error logging style

От
Tom Lane
Дата:
I wrote:
> Hmm, interesting.  Taking up my point #2, I'd been thinking about
> proposing that we convert
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_error("query was: %s", todo);
> to
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_error_detail("Query was: %s", todo);

After looking around a bit, I see that a lot of these add-on messages
are more nearly hints than details, so we'd probably better support
both those cases right off the bat.

To move things along a bit, here's a draft patch to logging.h/.c only
to implement what I'm envisioning.  I don't think there's much point
in doing the per-call-site gruntwork until we have agreement on what
the API is, so this seems like enough for discussion.

(As a fervent hater of colorization, I don't have an opinion about
whether or how to colorize the "detail:" and "hint:" fragments.
But I'll happily take somebody else's adjustment to add that.)

            regards, tom lane

diff --git a/src/common/logging.c b/src/common/logging.c
index fb1a9b1f87..84a4cdfd1f 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -151,6 +151,9 @@ pg_logging_init(const char *argv0)
     }
 }

+/*
+ * Change the logging flags.
+ */
 void
 pg_logging_config(int new_flags)
 {
@@ -194,17 +197,19 @@ pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)
 }

 void
-pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
+pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+               const char *pg_restrict fmt,...)
 {
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(level, fmt, ap);
+    pg_log_generic_v(level, part, fmt, ap);
     va_end(ap);
 }

 void
-pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
+pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                 const char *pg_restrict fmt, va_list ap)
 {
     int            save_errno = errno;
     const char *filename = NULL;
@@ -232,7 +237,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     fmt = _(fmt);

-    if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+    if (part == PG_LOG_PRIMARY &&
+        (!(log_flags & PG_LOG_FLAG_TERSE) || filename))
     {
         if (sgr_locus)
             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
@@ -251,30 +257,34 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     if (!(log_flags & PG_LOG_FLAG_TERSE))
     {
-        switch (level)
+        switch (part)
         {
-            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);
+            case PG_LOG_PRIMARY:
+                switch (level)
+                {
+                    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;
+                }
                 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);
+            case PG_LOG_DETAIL:
+                fprintf(stderr, _("detail: "));
                 break;
-            default:
+            case PG_LOG_HINT:
+                fprintf(stderr, _("hint: "));
                 break;
         }
     }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index a71cf84249..ec589ccd03 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
 enum pg_log_level
 {
     /*
-     * Not initialized yet
+     * Not initialized yet (not to be used as an actual message log level).
      */
     PG_LOG_NOTSET = 0,

@@ -43,20 +43,42 @@ enum pg_log_level
     PG_LOG_ERROR,

     /*
-     * Severe errors that cause program termination.  (One-shot programs may
-     * chose to label even fatal errors as merely "errors".  The distinction
-     * is up to the program.)
-     */
-    PG_LOG_FATAL,
-
-    /*
-     * Turn all logging off.
+     * Turn all logging off (not to be used as an actual message log level).
      */
     PG_LOG_OFF,
 };

+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
 extern enum pg_log_level __pg_log_level;

+/*
+ * A log message can have several parts.  The primary message is required,
+ * others are optional.  When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+    /*
+     * The primary message.  Try to keep it to one line; follow the backend's
+     * style guideline for primary messages.
+     */
+    PG_LOG_PRIMARY,
+
+    /*
+     * Additional detail.  Follow the backend's style guideline for detail
+     * messages.
+     */
+    PG_LOG_DETAIL,
+
+    /*
+     * Hint (not guaranteed correct) about how to fix the problem.  Follow the
+     * backend's style guideline for hint messages.
+     */
+    PG_LOG_HINT,
+};
+
 /*
  * Kind of a hack to be able to produce the psql output exactly as required by
  * the regression tests.
@@ -70,27 +92,85 @@ void        pg_logging_increase_verbosity(void);
 void        pg_logging_set_pre_callback(void (*cb) (void));
 void        pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));

-void        pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void        pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2,
0);
+void        pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+                           const char *pg_restrict fmt,...)
+            pg_attribute_printf(3, 4);
+void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                             const char *pg_restrict fmt, va_list ap)
+            pg_attribute_printf(3, 0);

-#define pg_log_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

-#define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_error_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
-        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_detail(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_hint(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+    } while(0)
+
+/*
+ * A common special case: pg_log_error() and immediately exit(1).  There is
+ * no situation where it makes sense to suppress the message, so we ignore
+ * __pg_log_level.
+ */
+#define pg_log_fatal(...) do { \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit(1); \
     } while(0)

 #endif                            /* COMMON_LOGGING_H */

Re: Frontend error logging style

От
Kyotaro Horiguchi
Дата:
At Wed, 10 Nov 2021 14:25:08 -0500, Tom Lane <tgl@sss.pgh.pa.us> wrote in 
> I wrote:
> > Hmm, interesting.  Taking up my point #2, I'd been thinking about
> > proposing that we convert
> >         pg_log_error("query failed: %s", PQerrorMessage(conn));
> >         pg_log_error("query was: %s", todo);
> > to
> >         pg_log_error("query failed: %s", PQerrorMessage(conn));
> >         pg_log_error_detail("Query was: %s", todo);
> 
> After looking around a bit, I see that a lot of these add-on messages
> are more nearly hints than details, so we'd probably better support
> both those cases right off the bat.

Sounds reasonable.

> To move things along a bit, here's a draft patch to logging.h/.c only
> to implement what I'm envisioning.  I don't think there's much point
> in doing the per-call-site gruntwork until we have agreement on what
> the API is, so this seems like enough for discussion.
> 
> (As a fervent hater of colorization, I don't have an opinion about
> whether or how to colorize the "detail:" and "hint:" fragments.
> But I'll happily take somebody else's adjustment to add that.)

(:) I don't hate colorization so much, but I'm frequently annoyed by
needing to turn off colorization and I disgust when there's no easy
way to disable that in a simple steps..)

Aren't DETAIL and HINT expected to be hidden at the targetted cutoff
level?  In other words, I suspect that people want to hide non-primary
messages for a lower verbosity level.  On the other hand I'm not sure
it is a proper behavior that log_level = WARNING causes ERROR messages
are accompanied by DETAIL/HINT submessages...

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center



Re: Frontend error logging style

От
Tom Lane
Дата:
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
> Aren't DETAIL and HINT expected to be hidden at the targetted cutoff
> level?  In other words, I suspect that people want to hide non-primary
> messages for a lower verbosity level.  On the other hand I'm not sure
> it is a proper behavior that log_level = WARNING causes ERROR messages
> are accompanied by DETAIL/HINT submessages...

I abandoned that idea in the draft patch.  We could maybe do something
about it further down the line, but I'm not sure there's really any
demand.

As the patch is set up, you could theoretically do something like

    pg_log_error("blah blah");
    pg_log_info_detail("Very boring detail goes here.");

(note the intentionally different log priorities).  But that feels wrong
to me --- it doesn't seem like individual call sites should be setting
such policy.  If we do arrange for a way to hide the optional message
parts, I'd rather that the control were centralized in logging.c.

It certainly wouldn't be hard for logging.c to make different decisions
about what to print; the thing that's not clear to me is what the
user-level knob for it should look like.  We already used up the
option of more or fewer -v switches.

            regards, tom lane



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 09.11.21 23:20, Tom Lane wrote:
> 1.  The distinction between "error" and "fatal" levels seems squishy
> to the point of uselessness.  I think we should either get rid of it
> entirely, or make an effort to use "fatal" exactly for the cases that
> are going to give up and exit right away.  Of the approximately 830
> pg_log_error calls in HEAD, I count at least 450 that are immediately
> followed by exit(1), and so should be pg_log_fatal if this distinction
> means anything at all.  OTOH, if we decide it doesn't mean anything,
> there are only about 90 pg_log_fatal calls to convert.  I lean
> slightly to the "get rid of the distinction" option, not only because
> it'd be a much smaller patch but also because I don't care to expend
> brain cells on the which-to-use question while reviewing future
> patches.

This logging system has been designed more generally, drawing some 
inspiration from Python and Java libraries, for example.  It's up to the 
program using this to make sensible use of it.  I think there are 
programs such as pg_receivewal where there is a meaningful distinction 
between errors flowing by and a fatal exit.  But with something like 
pg_basebackup, there is no real difference, so that code sort of uses 
pg_log_error by default, since any error is implicitly fatal.  I see the 
apparent inconsistency, but I don't think it's a real problem.  Each 
program by itself has arguably sensible behavior.



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 09.11.21 23:20, Tom Lane wrote:
> 2. What is the preferred style for adding extra lines to log messages?
> I see a lot of direct prints to stderr:
> 
>         pg_log_error("missing required argument: database name");
>         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
>         exit(1);

This is mainly used for those few messages that are sort of a "global 
standard".

> but a number of places have chosen to do this:
> 
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_error("query was: %s", todo);
> 
> and some places got creative and did this:
> 
>         pg_log_error("query failed: %s", PQerrorMessage(conn));
>         pg_log_info("query was: %s", sql.data);

I can't decide between those two.  It depends on how each program 
handles the log level internally.

Some kind of "detail" system could be useful.  But it would need to be 
tied into the log level, layout, etc.  I have not seen much inspiration 
for this kind of thing in other logging libraries, so I didn't do 
anything about it yet.



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 10.11.21 16:28, Robert Haas wrote:
> What I think we ought
> to be driving towards is having pg_log_fatal() forcibly exit, and
> pg_log_error() do the same unless the error is somehow caught.

This is specifically designed not to do any flow control.  In the 
backend, we have many instances, where log messages are issued with the 
wrong log level because the stronger log level would have flow control 
impact that is not appropriate at the call site.  I don't think we want 
more of that, especially since the flow control requirements in the 
varied suite of frontend programs is quite diverse.  Moreover, we also 
require control over the exit codes in some cases, which this kind of 
API wouldn't allow.

Several programs wrap, say, pg_log_fatal() into a pg_fatal(), that does 
logging, cleanup, and exit, as the case may be.  I think that's a good 
solution.  If someone wanted to write a more widely reusable pg_fatal(), 
why not, but in my previous attempts, this was quite complicated and 
didn't turn out to be useful.




Re: Frontend error logging style

От
Robert Haas
Дата:
On Mon, Nov 15, 2021 at 2:15 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> This is specifically designed not to do any flow control.  In the
> backend, we have many instances, where log messages are issued with the
> wrong log level because the stronger log level would have flow control
> impact that is not appropriate at the call site.  I don't think we want
> more of that, especially since the flow control requirements in the
> varied suite of frontend programs is quite diverse.  Moreover, we also
> require control over the exit codes in some cases, which this kind of
> API wouldn't allow.

I agree that the system we use in the backend isn't problem free, but
making the frontend do something randomly different isn't an
improvement. I think that most people who are writing PostgreSQL
frontend code have also written a lot of backend code, and they are
used to the way things work in the backend. More importantly, there's
an increasing amount of code that wants to run in either environment.
And there have been suggestions that we want to make more things, like
memory contexts, work that way. The design decision that you've made
here makes that harder, and results in stuff like this:

[rhaas pgsql]$ git grep pg_log_fatal.*VA_ARGS
src/bin/pg_rewind/pg_rewind.h:#define pg_fatal(...) do {
pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
src/bin/pg_waldump/pg_waldump.c:#define fatal_error(...) do {
pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)
src/include/lib/simplehash.h:    do { pg_log_fatal(__VA_ARGS__);
exit(1); } while(0)

Having different frontend utilities each invent their own
slightly-different way of doing this makes it hard to reuse code, and
hard to understand code. We need to find ways to make it more uniform,
not just observe that it isn't uniform today and give up.

IOW, I think the fact that it's not designed to do any flow control is
a bad thing.

-- 
Robert Haas
EDB: http://www.enterprisedb.com



Re: Frontend error logging style

От
Tom Lane
Дата:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
> Several programs wrap, say, pg_log_fatal() into a pg_fatal(), that does 
> logging, cleanup, and exit, as the case may be.  I think that's a good 
> solution.

I agree, and my draft patch formalized that by turning pg_log_fatal into
exactly that.

The question that I think is relevant here is what is the point of
labeling errors as "error:" or "fatal:" if we're not going to make any
serious attempt to make that distinction meaningful.  I'm not really
buying your argument that it's fine as-is.  Anybody who thinks that
there's a difference is going to be very confused by the behavior they
observe.  But, if we all know there's no difference, why have the
difference?

            regards, tom lane



Re: Frontend error logging style

От
Michael Paquier
Дата:
On Mon, Nov 15, 2021 at 02:40:10PM -0500, Robert Haas wrote:
> Having different frontend utilities each invent their own
> slightly-different way of doing this makes it hard to reuse code, and
> hard to understand code. We need to find ways to make it more uniform,
> not just observe that it isn't uniform today and give up.

I agree with this sentiment, but this is a bit more complex than just
calling exit() with pg_log_fatal(), no?  pg_dump likes playing a lot
with its exit_nicely(), meaning that we may want to allow frontends to
plug in callbacks.
--
Michael

Вложения

Re: Frontend error logging style

От
Robert Haas
Дата:
On Mon, Nov 15, 2021 at 10:02 PM Michael Paquier <michael@paquier.xyz> wrote:
> On Mon, Nov 15, 2021 at 02:40:10PM -0500, Robert Haas wrote:
> > Having different frontend utilities each invent their own
> > slightly-different way of doing this makes it hard to reuse code, and
> > hard to understand code. We need to find ways to make it more uniform,
> > not just observe that it isn't uniform today and give up.
>
> I agree with this sentiment, but this is a bit more complex than just
> calling exit() with pg_log_fatal(), no?  pg_dump likes playing a lot
> with its exit_nicely(), meaning that we may want to allow frontends to
> plug in callbacks.

Yep.

I think we need frontend facilities that look like the backend
facilities, so try/catch blocks, on-exit callbacks, and whatever else
there is. Otherwise code reuse is going to continue to be annoying.

-- 
Robert Haas
EDB: http://www.enterprisedb.com



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 16.11.21 16:18, Robert Haas wrote:
> I think we need frontend facilities that look like the backend
> facilities, so try/catch blocks, on-exit callbacks, and whatever else
> there is. Otherwise code reuse is going to continue to be annoying.

If people want to do that kind of thing (I'm undecided whether the 
complexity is worth it), then make it a different API.  The pg_log_* 
calls are for writing formatted output.  They normalized existing 
hand-coded patterns at the time.  We can wrap another API on top of them 
that does flow control and output.  The pg_log_* stuff is more on the 
level of syslog(), which also just outputs stuff.  Nobody is suggesting 
that syslog(LOG_EMERG) should exit the program automatically.  But you 
can wrap higher-level APIs such as ereport() on top of that that might 
do that.



Re: Frontend error logging style

От
Robert Haas
Дата:
On Fri, Nov 19, 2021 at 5:17 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> If people want to do that kind of thing (I'm undecided whether the
> complexity is worth it), then make it a different API.  The pg_log_*
> calls are for writing formatted output.  They normalized existing
> hand-coded patterns at the time.  We can wrap another API on top of them
> that does flow control and output.  The pg_log_* stuff is more on the
> level of syslog(), which also just outputs stuff.  Nobody is suggesting
> that syslog(LOG_EMERG) should exit the program automatically.  But you
> can wrap higher-level APIs such as ereport() on top of that that might
> do that.

Yeah, that might be a way forward.

-- 
Robert Haas
EDB: http://www.enterprisedb.com



Re: Frontend error logging style

От
Tom Lane
Дата:
Robert Haas <robertmhaas@gmail.com> writes:
> On Fri, Nov 19, 2021 at 5:17 AM Peter Eisentraut
> <peter.eisentraut@enterprisedb.com> wrote:
>> If people want to do that kind of thing (I'm undecided whether the
>> complexity is worth it), then make it a different API.  The pg_log_*
>> calls are for writing formatted output.  They normalized existing
>> hand-coded patterns at the time.  We can wrap another API on top of them
>> that does flow control and output.  The pg_log_* stuff is more on the
>> level of syslog(), which also just outputs stuff.  Nobody is suggesting
>> that syslog(LOG_EMERG) should exit the program automatically.  But you
>> can wrap higher-level APIs such as ereport() on top of that that might
>> do that.

> Yeah, that might be a way forward.

This conversation seems to have tailed off without full resolution,
but I observe that pretty much everyone except Peter is on board
with defining pg_log_fatal as pg_log_error + exit(1).  So I think
we should just do that, unless Peter wants to produce a finished
alternative proposal.

I've now gone through and fleshed out the patch I sketched upthread.
This exercise convinced me that we absolutely should do something
very like this, because

(1) As it stands, this patch removes a net of just about 1000 lines
of code.  So we clearly need *something*.

(2) The savings would be even more, except that people have invented
macros for pg_log_error + exit(1) in at least four places already.
(None of them quite the same of course.)  Here I've just fixed those
macro definitions to use pg_log_fatal.  In the name of consistency, we
should probably get rid of those macros in favor of using pg_log_fatal
directly; but I've not done so here, since this patch is eyewateringly
long and boring already.

(3) The amount of inconsistency in how we add on details/hints right
now is even worse than I thought.  For example, we've got places
burying the lede like this:

-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
-        fatal("aborting because of server version mismatch");
+        pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
+        exit(1);

or misidentifying the primary message altogether, like this:

     pg_log_error("query failed: %s",
                  PQerrorMessage(AH->connection));
-    fatal("query was: %s", query);
+    pg_log_error_detail("Query was: %s", query);
+    exit(1);

Thoughts?

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 65cce49993..66fe1c2de9 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -182,16 +182,17 @@ get_opts(int argc, char **argv, struct options *my_opts)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (optind < argc)
     {
-        fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
-                progname, argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error("too many command-line arguments (first is \"%s\")",
+                     argv[optind]);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
 }
@@ -328,11 +329,8 @@ sql_conn(struct options *my_opts)
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s",
+            pg_log_fatal("could not connect to database %s",
                          my_opts->dbname);
-            exit(1);
-        }

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -359,7 +357,7 @@ sql_conn(struct options *my_opts)
                      PQerrorMessage(conn));
         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }
     PQclear(res);

@@ -390,11 +388,11 @@ sql_exec(PGconn *conn, const char *todo, bool quiet)
     if (!res || PQresultStatus(res) > 2)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", todo);
+        pg_log_error_detail("Query was: %s", todo);

         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }

     /* get the number of fields */
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index d15edca454..28c859c295 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -492,19 +492,13 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
             case 'h':
                 param.pg_host = pg_strdup(optarg);
                 break;
             case 'l':
                 param.transaction_limit = strtol(optarg, NULL, 10);
                 if (param.transaction_limit < 0)
-                {
-                    pg_log_error("transaction limit must not be negative (0 disables)");
-                    exit(1);
-                }
+                    pg_log_fatal("transaction limit must not be negative (0 disables)");
                 break;
             case 'n':
                 param.dry_run = 1;
@@ -513,10 +507,7 @@ main(int argc, char **argv)
             case 'p':
                 port = strtol(optarg, NULL, 10);
                 if ((port < 1) || (port > 65535))
-                {
-                    pg_log_error("invalid port number: %s", optarg);
-                    exit(1);
-                }
+                    pg_log_fatal("invalid port number: %s", optarg);
                 param.pg_port = pg_strdup(optarg);
                 break;
             case 'U':
@@ -532,7 +523,8 @@ main(int argc, char **argv)
                 param.pg_prompt = TRI_YES;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -541,7 +533,7 @@ main(int argc, char **argv)
     if (optind >= argc)
     {
         pg_log_error("missing required argument: database name");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 97f15971e2..22c3874236 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -329,10 +329,7 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");
     return result;
 }

@@ -462,10 +459,7 @@ readfile(const char *path)
     int            n;

     if ((infile = fopen(path, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\" for reading: %m", path);

     initStringInfo(&line);

@@ -506,24 +500,15 @@ writefile(char *path, char **lines)
     char      **line;

     if ((out_file = fopen(path, "w")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\" for writing: %m", path);
     for (line = lines; *line != NULL; line++)
     {
         if (fputs(*line, out_file) < 0)
-        {
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
-        }
+            pg_log_fatal("could not write file \"%s\": %m", path);
         free(*line);
     }
     if (fclose(out_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not write file \"%s\": %m", path);
 }

 /*
@@ -539,7 +524,7 @@ popen_check(const char *command, const char *mode)
     errno = 0;
     cmdfd = popen(command, mode);
     if (cmdfd == NULL)
-        pg_log_error("could not execute command \"%s\": %m", command);
+        pg_log_fatal("could not execute command \"%s\": %m", command);
     return cmdfd;
 }

@@ -609,9 +594,7 @@ get_id(void)
     if (geteuid() == 0)            /* 0 is root's uid */
     {
         pg_log_error("cannot be run as root");
-        fprintf(stderr,
-                _("Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n"
-                  "own the server process.\n"));
+        pg_log_error_hint("Please log in (using, e.g., \"su\") as the (unprivileged) user that will own the server
process.");
         exit(1);
     }
 #endif
@@ -643,9 +626,8 @@ get_encoding_id(const char *encoding_name)
         if ((enc = pg_valid_server_encoding(encoding_name)) >= 0)
             return enc;
     }
-    pg_log_error("\"%s\" is not a valid server encoding name",
+    pg_log_fatal("\"%s\" is not a valid server encoding name",
                  encoding_name ? encoding_name : "(null)");
-    exit(1);
 }

 /*
@@ -789,25 +771,19 @@ check_input(char *path)
         if (errno == ENOENT)
         {
             pg_log_error("file \"%s\" does not exist", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         else
         {
             pg_log_error("could not access file \"%s\": %m", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         exit(1);
     }
     if (!S_ISREG(statbuf.st_mode))
     {
         pg_log_error("file \"%s\" is not a regular file", path);
-        fprintf(stderr,
-                _("This might mean you have a corrupted installation or identified\n"
-                  "the wrong directory with the invocation option -L.\n"));
+        pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with
theinvocation option -L."); 
         exit(1);
     }
 }
@@ -828,16 +804,10 @@ write_version_file(const char *extrapath)
         path = psprintf("%s/%s/PG_VERSION", pg_data, extrapath);

     if ((version_file = fopen(path, PG_BINARY_W)) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\" for writing: %m", path);
     if (fprintf(version_file, "%s\n", PG_MAJORVERSION) < 0 ||
         fclose(version_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -854,15 +824,9 @@ set_null_conf(void)
     path = psprintf("%s/postgresql.conf", pg_data);
     conf_file = fopen(path, PG_BINARY_W);
     if (conf_file == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\" for writing: %m", path);
     if (fclose(conf_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -1216,10 +1180,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     /*
      * create the automatic configuration file to store the configuration
@@ -1235,10 +1196,7 @@ setup_config(void)

     writefile(path, autoconflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1321,10 +1279,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1336,10 +1291,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1373,9 +1325,8 @@ bootstrap_template1(void)
     {
         pg_log_error("input file \"%s\" does not belong to PostgreSQL %s",
                      bki_file, PG_VERSION);
-        fprintf(stderr,
-                _("Check your installation or specify the correct path "
-                  "using the option -L.\n"));
+        pg_log_error_hint("Check your installation or specify the correct path "
+                          "using the option -L.");
         exit(1);
     }

@@ -1495,21 +1446,17 @@ get_su_pwd(void)
         FILE       *pwf = fopen(pwfilename, "r");

         if (!pwf)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
+            pg_log_fatal("could not open file \"%s\" for reading: %m",
                          pwfilename);
-            exit(1);
-        }
         pwd1 = pg_get_line(pwf, NULL);
         if (!pwd1)
         {
             if (ferror(pwf))
-                pg_log_error("could not read password from file \"%s\": %m",
+                pg_log_fatal("could not read password from file \"%s\": %m",
                              pwfilename);
             else
-                pg_log_error("password file \"%s\" is empty",
+                pg_log_fatal("password file \"%s\" is empty",
                              pwfilename);
-            exit(1);
         }
         fclose(pwf);

@@ -2049,10 +1996,7 @@ check_locale_name(int category, const char *locale, char **canonname)

     save = setlocale(category, NULL);
     if (!save)
-    {
-        pg_log_error("setlocale() failed");
-        exit(1);
-    }
+        pg_log_fatal("setlocale() failed");

     /* save may be pointing at a modifiable scratch variable, so copy it. */
     save = pg_strdup(save);
@@ -2070,17 +2014,14 @@ check_locale_name(int category, const char *locale, char **canonname)

     /* restore old value. */
     if (!setlocale(category, save))
-    {
-        pg_log_error("failed to restore old locale \"%s\"", save);
-        exit(1);
-    }
+        pg_log_fatal("failed to restore old locale \"%s\"", save);
     free(save);

     /* complain if locale wasn't valid */
     if (res == NULL)
     {
         if (*locale)
-            pg_log_error("invalid locale name \"%s\"", locale);
+            pg_log_fatal("invalid locale name \"%s\"", locale);
         else
         {
             /*
@@ -2091,9 +2032,8 @@ check_locale_name(int category, const char *locale, char **canonname)
              * setlocale's behavior is implementation-specific, it's hard to
              * be sure what it didn't like.  Print a safe generic message.
              */
-            pg_log_error("invalid locale settings; check LANG and LC_* environment variables");
+            pg_log_fatal("invalid locale settings; check LANG and LC_* environment variables");
         }
-        exit(1);
     }
 }

@@ -2119,15 +2059,14 @@ check_locale_encoding(const char *locale, int user_enc)
           user_enc == PG_SQL_ASCII))
     {
         pg_log_error("encoding mismatch");
-        fprintf(stderr,
-                _("The encoding you selected (%s) and the encoding that the\n"
-                  "selected locale uses (%s) do not match.  This would lead to\n"
-                  "misbehavior in various character string processing functions.\n"
-                  "Rerun %s and either do not specify an encoding explicitly,\n"
-                  "or choose a matching combination.\n"),
-                pg_encoding_to_char(user_enc),
-                pg_encoding_to_char(locale_enc),
-                progname);
+        pg_log_error_detail("The encoding you selected (%s) and the encoding that the\n"
+                            "selected locale uses (%s) do not match.  This would lead to\n"
+                            "misbehavior in various character string processing functions.\n"
+                            "Rerun %s and either do not specify an encoding explicitly,\n"
+                            "or choose a matching combination.",
+                            pg_encoding_to_char(user_enc),
+                            pg_encoding_to_char(locale_enc),
+                            progname);
         return false;
     }
     return true;
@@ -2259,9 +2198,8 @@ check_authmethod_valid(const char *authmethod, const char *const *valid_methods,
                 return;
     }

-    pg_log_error("invalid authentication method \"%s\" for \"%s\" connections",
+    pg_log_fatal("invalid authentication method \"%s\" for \"%s\" connections",
                  authmethod, conntype);
-    exit(1);
 }

 static void
@@ -2274,10 +2212,7 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
          strcmp(authmethodhost, "password") == 0 ||
          strcmp(authmethodhost, "scram-sha-256") == 0) &&
         !(pwprompt || pwfilename))
-    {
-        pg_log_error("must specify a password for the superuser to enable password authentication");
-        exit(1);
-    }
+        pg_log_fatal("must specify a password for the superuser to enable password authentication");
 }


@@ -2297,10 +2232,9 @@ setup_pgdata(void)
         else
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr,
-                    _("You must identify the directory where the data for this database system\n"
-                      "will reside.  Do this with either the invocation option -D or the\n"
-                      "environment variable PGDATA.\n"));
+            pg_log_error_hint("You must identify the directory where the data for this database system "
+                              "will reside.  Do this with either the invocation option -D or the "
+                              "environment variable PGDATA.");
             exit(1);
         }
     }
@@ -2315,10 +2249,7 @@ setup_pgdata(void)
      * have embedded spaces.
      */
     if (setenv("PGDATA", pg_data, 1) != 0)
-    {
-        pg_log_error("could not set environment");
-        exit(1);
-    }
+        pg_log_fatal("could not set environment");
 }


@@ -2336,15 +2267,13 @@ setup_bin_paths(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" was found by \"%s\" "
+                         "but was not the same version as %s",
                          "postgres", full_path, progname);
+        pg_log_error_hint("Check your installation.");
         exit(1);
     }

@@ -2359,10 +2288,7 @@ setup_bin_paths(const char *argv0)
         get_share_path(backend_exec, share_path);
     }
     else if (!is_absolute_path(share_path))
-    {
-        pg_log_error("input file location must be an absolute path");
-        exit(1);
-    }
+        pg_log_fatal("input file location must be an absolute path");

     canonicalize_path(share_path);
 }
@@ -2406,9 +2332,8 @@ setup_locale_encoding(void)
             /* Couldn't recognize the locale's codeset */
             pg_log_error("could not find suitable encoding for locale \"%s\"",
                          lc_ctype);
-            fprintf(stderr, _("Rerun %s with the -E option.\n"), progname);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Rerun %s with the -E option.", progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         else if (!pg_valid_server_encoding_id(ctype_enc))
@@ -2427,10 +2352,10 @@ setup_locale_encoding(void)
 #else
             pg_log_error("locale \"%s\" requires unsupported encoding \"%s\"",
                          lc_ctype, pg_encoding_to_char(ctype_enc));
-            fprintf(stderr,
-                    _("Encoding \"%s\" is not allowed as a server-side encoding.\n"
-                      "Rerun %s with a different locale selection.\n"),
-                    pg_encoding_to_char(ctype_enc), progname);
+            pg_log_error_detail("Encoding \"%s\" is not allowed as a server-side encoding.",
+                                pg_encoding_to_char(ctype_enc));
+            pg_log_error_hint("Rerun %s with a different locale selection.",
+                              progname);
             exit(1);
 #endif
         }
@@ -2573,10 +2498,7 @@ create_data_directory(void)
             fflush(stdout);

             if (pg_mkdir_p(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not create directory \"%s\": %m", pg_data);
-                exit(1);
-            }
+                pg_log_fatal("could not create directory \"%s\": %m", pg_data);
             else
                 check_ok();

@@ -2590,11 +2512,8 @@ create_data_directory(void)
             fflush(stdout);

             if (chmod(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not change permissions of directory \"%s\": %m",
+                pg_log_fatal("could not change permissions of directory \"%s\": %m",
                              pg_data);
-                exit(1);
-            }
             else
                 check_ok();

@@ -2609,17 +2528,15 @@ create_data_directory(void)
             if (ret != 4)
                 warn_on_mount_point(ret);
             else
-                fprintf(stderr,
-                        _("If you want to create a new database system, either remove or empty\n"
-                          "the directory \"%s\" or run %s\n"
-                          "with an argument other than \"%s\".\n"),
-                        pg_data, progname, pg_data);
+                pg_log_error_hint("If you want to create a new database system, either remove or empty "
+                                  "the directory \"%s\" or run %s "
+                                  "with an argument other than \"%s\".",
+                                  pg_data, progname, pg_data);
             exit(1);            /* no further message needed */

         default:
             /* Trouble accessing directory */
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
+            pg_log_fatal("could not access directory \"%s\": %m", pg_data);
     }
 }

@@ -2640,10 +2557,7 @@ create_xlog_or_symlink(void)
         /* clean up xlog directory name, check it's absolute */
         canonicalize_path(xlog_dir);
         if (!is_absolute_path(xlog_dir))
-        {
-            pg_log_error("WAL directory location must be an absolute path");
-            exit(1);
-        }
+            pg_log_fatal("WAL directory location must be an absolute path");

         /* check if the specified xlog directory exists/is empty */
         switch ((ret = pg_check_dir(xlog_dir)))
@@ -2655,11 +2569,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (pg_mkdir_p(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not create directory \"%s\": %m",
+                    pg_log_fatal("could not create directory \"%s\": %m",
                                  xlog_dir);
-                    exit(1);
-                }
                 else
                     check_ok();

@@ -2673,11 +2584,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (chmod(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not change permissions of directory \"%s\": %m",
+                    pg_log_fatal("could not change permissions of directory \"%s\": %m",
                                  xlog_dir);
-                    exit(1);
-                }
                 else
                     check_ok();

@@ -2692,39 +2600,29 @@ create_xlog_or_symlink(void)
                 if (ret != 4)
                     warn_on_mount_point(ret);
                 else
-                    fprintf(stderr,
-                            _("If you want to store the WAL there, either remove or empty the directory\n"
-                              "\"%s\".\n"),
-                            xlog_dir);
+                    pg_log_error_hint("If you want to store the WAL there, either remove or empty the directory
\"%s\".",
+                                      xlog_dir);
                 exit(1);

             default:
                 /* Trouble accessing directory */
-                pg_log_error("could not access directory \"%s\": %m", xlog_dir);
-                exit(1);
+                pg_log_fatal("could not access directory \"%s\": %m", xlog_dir);
         }

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, subdirloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m",
+            pg_log_fatal("could not create symbolic link \"%s\": %m",
                          subdirloc);
-            exit(1);
-        }
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_log_fatal("symlinks are not supported on this platform");
 #endif
     }
     else
     {
         /* Without -X option, just make the subdirectory normally */
         if (mkdir(subdirloc, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m",
+            pg_log_fatal("could not create directory \"%s\": %m",
                          subdirloc);
-            exit(1);
-        }
     }

     free(subdirloc);
@@ -2735,15 +2633,12 @@ void
 warn_on_mount_point(int error)
 {
     if (error == 2)
-        fprintf(stderr,
-                _("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.");
     else if (error == 3)
-        fprintf(stderr,
-                _("It contains a lost+found directory, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a lost+found directory, perhaps due to it being a mount point.");

-    fprintf(stderr,
-            _("Using a mount point directly as the data directory is not recommended.\n"
-              "Create a subdirectory under the mount point.\n"));
+    pg_log_error_hint("Using a mount point directly as the data directory is not recommended.\n"
+                      "Create a subdirectory under the mount point.");
 }


@@ -2782,10 +2677,7 @@ initialize_data_directory(void)
          * pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
          */
         if (mkdir(path, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m", path);
-            exit(1);
-        }
+            pg_log_fatal("could not create directory \"%s\": %m", path);

         free(path);
     }
@@ -3047,8 +2939,7 @@ main(int argc, char *argv[])
                 break;
             default:
                 /* getopt_long already emitted a complaint */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -3068,8 +2959,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -3082,10 +2972,7 @@ main(int argc, char *argv[])

         /* must check that directory is readable */
         if (pg_check_dir(pg_data) <= 0)
-        {
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
-        }
+            pg_log_fatal("could not access directory \"%s\": %m", pg_data);

         fputs(_("syncing data to disk ... "), stdout);
         fflush(stdout);
@@ -3095,10 +2982,7 @@ main(int argc, char *argv[])
     }

     if (pwprompt && pwfilename)
-    {
-        pg_log_error("password prompt and password file cannot be specified together");
-        exit(1);
-    }
+        pg_log_fatal("password prompt and password file cannot be specified together");

     check_authmethod_unspecified(&authmethodlocal);
     check_authmethod_unspecified(&authmethodhost);
@@ -3120,15 +3004,9 @@ main(int argc, char *argv[])

         /* verify that wal segment size is valid */
         if (endptr == str_wal_segment_size_mb || *endptr != '\0')
-        {
-            pg_log_error("argument of --wal-segsize must be a number");
-            exit(1);
-        }
+            pg_log_fatal("argument of --wal-segsize must be a number");
         if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
-        {
-            pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-            exit(1);
-        }
+            pg_log_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
     }

     get_restricted_token();
@@ -3142,10 +3020,7 @@ main(int argc, char *argv[])
         username = effective_user;

     if (strncmp(username, "pg_", 3) == 0)
-    {
-        pg_log_error("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
-        exit(1);
-    }
+        pg_log_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);

     printf(_("The files belonging to this database system will be owned "
              "by user \"%s\".\n"
@@ -3188,8 +3063,8 @@ main(int argc, char *argv[])
     {
         printf("\n");
         pg_log_warning("enabling \"trust\" authentication for local connections");
-        fprintf(stderr, _("You can change this by editing pg_hba.conf or using the option -A, or\n"
-                          "--auth-local and --auth-host, the next time you run initdb.\n"));
+        pg_log_warning_hint("You can change this by editing pg_hba.conf or using the option -A, or "
+                            "--auth-local and --auth-host, the next time you run initdb.");
     }

     if (!noinstructions)
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 6607f72938..6672dd336d 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -202,9 +202,9 @@ static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,

 #define log_no_match(...) do { \
         if (opts.strict_names) \
-            pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+            pg_log_error(__VA_ARGS__); \
         else \
-            pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+            pg_log_warning(__VA_ARGS__); \
     } while(0)

 #define FREE_AND_SET_NULL(x) do { \
@@ -396,39 +396,24 @@ main(int argc, char *argv[])
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     opts.skip = "none";
                 else
-                {
-                    pg_log_error("invalid argument for option %s", "--skip");
-                    exit(1);
-                }
+                    pg_log_fatal("invalid argument for option %s", "--skip");
                 break;
             case 7:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid start block");
-                    exit(1);
-                }
+                    pg_log_fatal("invalid start block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("start block out of bounds");
-                    exit(1);
-                }
+                    pg_log_fatal("start block out of bounds");
                 opts.startblock = optval;
                 break;
             case 8:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid end block");
-                    exit(1);
-                }
+                    pg_log_fatal("invalid end block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("end block out of bounds");
-                    exit(1);
-                }
+                    pg_log_fatal("end block out of bounds");
                 opts.endblock = optval;
                 break;
             case 9:
@@ -450,18 +435,14 @@ main(int argc, char *argv[])
                     opts.install_schema = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr,
-                        _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (opts.endblock >= 0 && opts.endblock < opts.startblock)
-    {
-        pg_log_error("end block precedes start block");
-        exit(1);
-    }
+        pg_log_fatal("end block precedes start block");

     /*
      * A single non-option arguments specifies a database name or connection
@@ -477,7 +458,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -495,19 +476,13 @@ main(int argc, char *argv[])
     if (opts.alldb)
     {
         if (db != NULL)
-        {
-            pg_log_error("cannot specify a database name with --all");
-            exit(1);
-        }
+            pg_log_fatal("cannot specify a database name with --all");
         cparams.dbname = maintenance_db;
     }
     else if (db != NULL)
     {
         if (opts.dbpattern)
-        {
-            pg_log_error("cannot specify both a database name and database patterns");
-            exit(1);
-        }
+            pg_log_fatal("cannot specify both a database name and database patterns");
         cparams.dbname = db;
     }

@@ -593,7 +568,7 @@ main(int argc, char *argv[])
             /* Querying the catalog failed. */
             pg_log_error("database \"%s\": %s",
                          PQdb(conn), PQerrorMessage(conn));
-            pg_log_info("query was: %s", amcheck_sql);
+            pg_log_error_detail("Query was: %s", amcheck_sql);
             PQclear(result);
             disconnectDatabase(conn);
             exit(1);
@@ -669,8 +644,7 @@ main(int argc, char *argv[])
     {
         if (conn != NULL)
             disconnectDatabase(conn);
-        pg_log_error("no relations to check");
-        exit(1);
+        pg_log_fatal("no relations to check");
     }
     progress_report(reltotal, relprogress, pagestotal, pageschecked,
                     NULL, true, false);
@@ -919,7 +893,7 @@ run_command(ParallelSlot *slot, const char *sql)
         pg_log_error("error sending command to database \"%s\": %s",
                      PQdb(slot->connection),
                      PQerrorMessage(slot->connection));
-        pg_log_error("command was: %s", sql);
+        pg_log_error_detail("Command was: %s", sql);
         exit(1);
     }
 }
@@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
             pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
                            rel->datinfo->datname, rel->nspname, rel->relname, ntups);
             if (opts.verbose)
-                pg_log_info("query was: %s", rel->sql);
-            pg_log_warning("Are %s's and amcheck's versions compatible?",
-                           progname);
+                pg_log_warning_detail("Query was: %s", rel->sql);
+            pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
+                                progname);
             progress_since_last_stderr = false;
         }
     }
@@ -1648,7 +1622,7 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -1673,11 +1647,8 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
              */
             fatal = opts.strict_names;
             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected database pattern_id %d",
+                pg_log_fatal("internal error: received unexpected database pattern_id %d",
                              pattern_id);
-                exit(1);
-            }
             log_no_match("no connectable databases to check matching \"%s\"",
                          opts.include.data[pattern_id].pattern);
         }
@@ -2096,7 +2067,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -2136,11 +2107,8 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
              */

             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected relation pattern_id %d",
+                pg_log_fatal("internal error: received unexpected relation pattern_id %d",
                              pattern_id);
-                exit(1);
-            }

             opts.include.data[pattern_id].matched = true;
         }
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 6c3e7f4e01..b024b4a2ab 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -148,33 +148,21 @@ CleanupPriorWALFiles(void)

                 rc = unlink(WALFilePath);
                 if (rc != 0)
-                {
-                    pg_log_error("could not remove file \"%s\": %m",
+                    pg_log_fatal("could not remove file \"%s\": %m",
                                  WALFilePath);
-                    exit(1);
-                }
             }
         }

         if (errno)
-        {
-            pg_log_error("could not read archive location \"%s\": %m",
+            pg_log_fatal("could not read archive location \"%s\": %m",
                          archiveLocation);
-            exit(1);
-        }
         if (closedir(xldir))
-        {
-            pg_log_error("could not close archive location \"%s\": %m",
+            pg_log_fatal("could not close archive location \"%s\": %m",
                          archiveLocation);
-            exit(1);
-        }
     }
     else
-    {
-        pg_log_error("could not open archive location \"%s\": %m",
+        pg_log_fatal("could not open archive location \"%s\": %m",
                      archiveLocation);
-        exit(1);
-    }
 }

 /*
@@ -247,7 +235,7 @@ SetWALFileNameForCleanup(void)
     if (!fnameOK)
     {
         pg_log_error("invalid file name argument");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }
 }
@@ -321,9 +309,9 @@ main(int argc, char **argv)
                                                      * from xlogfile names */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(2);
-                break;
         }
     }

@@ -342,7 +330,7 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify archive location");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

@@ -354,14 +342,14 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify oldest kept WAL file");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

     if (optind < argc)
     {
         pg_log_error("too many command-line arguments");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

diff --git a/src/bin/pg_basebackup/bbstreamer_file.c b/src/bin/pg_basebackup/bbstreamer_file.c
index d721f87891..c0e5778f74 100644
--- a/src/bin/pg_basebackup/bbstreamer_file.c
+++ b/src/bin/pg_basebackup/bbstreamer_file.c
@@ -90,10 +90,7 @@ bbstreamer_plain_writer_new(char *pathname, FILE *file)
     {
         streamer->file = fopen(pathname, "wb");
         if (streamer->file == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", pathname);
-            exit(1);
-        }
+            pg_log_fatal("could not create file \"%s\": %m", pathname);
         streamer->should_close_file = true;
     }

@@ -121,9 +118,8 @@ bbstreamer_plain_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m",
+        pg_log_fatal("could not write to file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
     }
 }

@@ -139,11 +135,8 @@ bbstreamer_plain_writer_finalize(bbstreamer *streamer)
     mystreamer = (bbstreamer_plain_writer *) streamer;

     if (mystreamer->should_close_file && fclose(mystreamer->file) != 0)
-    {
-        pg_log_error("could not close file \"%s\": %m",
+        pg_log_fatal("could not close file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
-    }

     mystreamer->file = NULL;
     mystreamer->should_close_file = false;
@@ -262,9 +255,8 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
                 /* if write didn't set errno, assume problem is no disk space */
                 if (errno == 0)
                     errno = ENOSPC;
-                pg_log_error("could not write to file \"%s\": %m",
+                pg_log_fatal("could not write to file \"%s\": %m",
                              mystreamer->filename);
-                exit(1);
             }
             break;

@@ -280,8 +272,7 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while extracting archive");
-            exit(1);
+            pg_log_fatal("unexpected state while extracting archive");
     }
 }

@@ -304,20 +295,14 @@ extract_directory(const char *filename, mode_t mode)
                pg_str_endswith(filename, "/pg_xlog") ||
                pg_str_endswith(filename, "/archive_status")) &&
               errno == EEXIST))
-        {
-            pg_log_error("could not create directory \"%s\": %m",
+            pg_log_fatal("could not create directory \"%s\": %m",
                          filename);
-            exit(1);
-        }
     }

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on directory \"%s\": %m",
+        pg_log_fatal("could not set permissions on directory \"%s\": %m",
                      filename);
-        exit(1);
-    }
 #endif
 }

@@ -335,11 +320,8 @@ static void
 extract_link(const char *filename, const char *linktarget)
 {
     if (symlink(linktarget, filename) != 0)
-    {
-        pg_log_error("could not create symbolic link from \"%s\" to \"%s\": %m",
+        pg_log_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
                      filename, linktarget);
-        exit(1);
-    }
 }

 /*
@@ -354,18 +336,12 @@ create_file_for_extract(const char *filename, mode_t mode)

     file = fopen(filename, "wb");
     if (file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_log_fatal("could not create file \"%s\": %m", filename);

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on file \"%s\": %m",
+        pg_log_fatal("could not set permissions on file \"%s\": %m",
                      filename);
-        exit(1);
-    }
 #endif

     return file;
diff --git a/src/bin/pg_basebackup/bbstreamer_gzip.c b/src/bin/pg_basebackup/bbstreamer_gzip.c
index 894f857103..c51754a362 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -91,42 +91,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file, int compresslevel)
     {
         streamer->gzfile = gzopen(pathname, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not create compressed file \"%s\": %m",
+            pg_log_fatal("could not create compressed file \"%s\": %m",
                          pathname);
-            exit(1);
-        }
     }
     else
     {
         int            fd = dup(fileno(file));

         if (fd < 0)
-        {
-            pg_log_error("could not duplicate stdout: %m");
-            exit(1);
-        }
+            pg_log_fatal("could not duplicate stdout: %m");

         streamer->gzfile = gzdopen(fd, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not open output file: %m");
-            exit(1);
-        }
+            pg_log_fatal("could not open output file: %m");
     }

     if (gzsetparams(streamer->gzfile, compresslevel,
                     Z_DEFAULT_STRATEGY) != Z_OK)
-    {
-        pg_log_error("could not set compression level %d: %s",
+        pg_log_fatal("could not set compression level %d: %s",
                      compresslevel, get_gz_error(streamer->gzfile));
-        exit(1);
-    }

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_fatal("this build does not support compression");
 #endif
 }

@@ -152,9 +139,8 @@ bbstreamer_gzip_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to compressed file \"%s\": %s",
+        pg_log_fatal("could not write to compressed file \"%s\": %s",
                      mystreamer->pathname, get_gz_error(mystreamer->gzfile));
-        exit(1);
     }
 }

@@ -177,11 +163,8 @@ bbstreamer_gzip_writer_finalize(bbstreamer *streamer)

     errno = 0;                    /* in case gzclose() doesn't set it */
     if (gzclose(mystreamer->gzfile) != 0)
-    {
-        pg_log_error("could not close compressed file \"%s\": %m",
+        pg_log_fatal("could not close compressed file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
-    }

     mystreamer->gzfile = NULL;
 }
@@ -258,15 +241,11 @@ bbstreamer_gzip_decompressor_new(bbstreamer *next)
      * possible value for safety.
      */
     if (inflateInit2(zs, 15 + 16) != Z_OK)
-    {
-        pg_log_error("could not initialize compression library");
-        exit(1);
-    }
+        pg_log_fatal("could not initialize compression library");

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_inject.c b/src/bin/pg_basebackup/bbstreamer_inject.c
index 79c378d96e..612912f641 100644
--- a/src/bin/pg_basebackup/bbstreamer_inject.c
+++ b/src/bin/pg_basebackup/bbstreamer_inject.c
@@ -186,8 +186,7 @@ bbstreamer_recovery_injector_content(bbstreamer *streamer,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while injecting recovery settings");
-            exit(1);
+            pg_log_fatal("unexpected state while injecting recovery settings");
     }

     bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
diff --git a/src/bin/pg_basebackup/bbstreamer_lz4.c b/src/bin/pg_basebackup/bbstreamer_lz4.c
index f0bc226bf8..35380dbb02 100644
--- a/src/bin/pg_basebackup/bbstreamer_lz4.c
+++ b/src/bin/pg_basebackup/bbstreamer_lz4.c
@@ -109,8 +109,7 @@ bbstreamer_lz4_compressor_new(bbstreamer *next, int compresslevel)

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_fatal("this build does not support compression");
 #endif
 }

@@ -296,16 +295,12 @@ bbstreamer_lz4_decompressor_new(bbstreamer *next)
     /* Initialize internal stream state for decompression */
     ctxError = LZ4F_createDecompressionContext(&streamer->dctx, LZ4F_VERSION);
     if (LZ4F_isError(ctxError))
-    {
-        pg_log_error("could not initialize compression library: %s",
-                LZ4F_getErrorName(ctxError));
-        exit(1);
-    }
+        pg_log_fatal("could not initialize compression library: %s",
+                     LZ4F_getErrorName(ctxError));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_tar.c b/src/bin/pg_basebackup/bbstreamer_tar.c
index 6ab981156e..294ff97bdd 100644
--- a/src/bin/pg_basebackup/bbstreamer_tar.c
+++ b/src/bin/pg_basebackup/bbstreamer_tar.c
@@ -241,16 +241,12 @@ bbstreamer_tar_parser_content(bbstreamer *streamer, bbstreamer_member *member,
                  */
                 bbstreamer_buffer_bytes(streamer, &data, &len, len);
                 if (len > 2 * TAR_BLOCK_SIZE)
-                {
-                    pg_log_error("tar file trailer exceeds 2 blocks");
-                    exit(1);
-                }
+                    pg_log_fatal("tar file trailer exceeds 2 blocks");
                 return;

             default:
                 /* Shouldn't happen. */
-                pg_log_error("unexpected state while parsing tar archive");
-                exit(1);
+                pg_log_fatal("unexpected state while parsing tar archive");
         }
     }
 }
@@ -297,10 +293,7 @@ bbstreamer_tar_header(bbstreamer_tar_parser *mystreamer)
      */
     strlcpy(member->pathname, &buffer[0], MAXPGPATH);
     if (member->pathname[0] == '\0')
-    {
-        pg_log_error("tar member has empty name");
-        exit(1);
-    }
+        pg_log_fatal("tar member has empty name");
     member->size = read_tar_number(&buffer[124], 12);
     member->mode = read_tar_number(&buffer[100], 8);
     member->uid = read_tar_number(&buffer[108], 8);
@@ -332,10 +325,7 @@ bbstreamer_tar_parser_finalize(bbstreamer *streamer)
     if (mystreamer->next_context != BBSTREAMER_ARCHIVE_TRAILER &&
         (mystreamer->next_context != BBSTREAMER_MEMBER_HEADER ||
          mystreamer->base.bbs_buffer.len > 0))
-    {
-        pg_log_error("COPY stream ended before last file was finished");
-        exit(1);
-    }
+        pg_log_fatal("COPY stream ended before last file was finished");

     /* Send the archive trailer, even if empty. */
     bbstreamer_content(streamer->bbs_next, NULL,
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 08b07d5a06..eadb20c485 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -306,20 +306,14 @@ tablespace_list_append(const char *arg)
     for (arg_ptr = arg; *arg_ptr; arg_ptr++)
     {
         if (dst_ptr - dst >= MAXPGPATH)
-        {
-            pg_log_error("directory name too long");
-            exit(1);
-        }
+            pg_log_fatal("directory name too long");

         if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
             ;                    /* skip backslash escaping = */
         else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
         {
             if (*cell->new_dir)
-            {
-                pg_log_error("multiple \"=\" signs in tablespace mapping");
-                exit(1);
-            }
+                pg_log_fatal("multiple \"=\" signs in tablespace mapping");
             else
                 dst = dst_ptr = cell->new_dir;
         }
@@ -328,10 +322,7 @@ tablespace_list_append(const char *arg)
     }

     if (!*cell->old_dir || !*cell->new_dir)
-    {
-        pg_log_error("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
-        exit(1);
-    }
+        pg_log_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);

     /*
      * This check isn't absolutely necessary.  But all tablespaces are created
@@ -340,18 +331,12 @@ tablespace_list_append(const char *arg)
      * consistent with the new_dir check.
      */
     if (!is_absolute_path(cell->old_dir))
-    {
-        pg_log_error("old directory is not an absolute path in tablespace mapping: %s",
+        pg_log_fatal("old directory is not an absolute path in tablespace mapping: %s",
                      cell->old_dir);
-        exit(1);
-    }

     if (!is_absolute_path(cell->new_dir))
-    {
-        pg_log_error("new directory is not an absolute path in tablespace mapping: %s",
+        pg_log_fatal("new directory is not an absolute path in tablespace mapping: %s",
                      cell->new_dir);
-        exit(1);
-    }

     /*
      * Comparisons done with these values should involve similarly
@@ -466,17 +451,11 @@ reached_end_position(XLogRecPtr segendpos, uint32 timeline,
             MemSet(xlogend, 0, sizeof(xlogend));
             r = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
             if (r < 0)
-            {
-                pg_log_error("could not read from ready pipe: %m");
-                exit(1);
-            }
+                pg_log_fatal("could not read from ready pipe: %m");

             if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-            {
-                pg_log_error("could not parse write-ahead log location \"%s\"",
+                pg_log_fatal("could not parse write-ahead log location \"%s\"",
                              xlogend);
-                exit(1);
-            }
             xlogendptr = ((uint64) hi) << 32 | lo;
             has_xlogendptr = 1;

@@ -616,11 +595,8 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)

     /* Convert the starting position */
     if (sscanf(startpos, "%X/%X", &hi, &lo) != 2)
-    {
-        pg_log_error("could not parse write-ahead log location \"%s\"",
+        pg_log_fatal("could not parse write-ahead log location \"%s\"",
                      startpos);
-        exit(1);
-    }
     param->startptr = ((uint64) hi) << 32 | lo;
     /* Round off to even segment position */
     param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
@@ -628,10 +604,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #ifndef WIN32
     /* Create our background pipe */
     if (pipe(bgpipe) < 0)
-    {
-        pg_log_error("could not create pipe for background process: %m");
-        exit(1);
-    }
+        pg_log_fatal("could not create pipe for background process: %m");
 #endif

     /* Get a second connection */
@@ -686,10 +659,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
                  "pg_xlog" : "pg_wal");

         if (pg_mkdir_p(statusdir, pg_dir_create_mode) != 0 && errno != EEXIST)
-        {
-            pg_log_error("could not create directory \"%s\": %m", statusdir);
-            exit(1);
-        }
+            pg_log_fatal("could not create directory \"%s\": %m", statusdir);
     }

     /*
@@ -704,10 +674,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
         exit(LogStreamerMain(param));
     }
     else if (bgchild < 0)
-    {
-        pg_log_error("could not create background process: %m");
-        exit(1);
-    }
+        pg_log_fatal("could not create background process: %m");

     /*
      * Else we are in the parent process and all is well.
@@ -716,10 +683,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #else                            /* WIN32 */
     bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
     if (bgchild == 0)
-    {
-        pg_log_error("could not create background thread: %m");
-        exit(1);
-    }
+        pg_log_fatal("could not create background thread: %m");
 #endif
 }

@@ -739,10 +703,7 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
              * Does not exist, so create
              */
             if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
-            {
-                pg_log_error("could not create directory \"%s\": %m", dirname);
-                exit(1);
-            }
+                pg_log_fatal("could not create directory \"%s\": %m", dirname);
             if (created)
                 *created = true;
             return;
@@ -761,15 +722,13 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
             /*
              * Exists, not empty
              */
-            pg_log_error("directory \"%s\" exists but is not empty", dirname);
-            exit(1);
+            pg_log_fatal("directory \"%s\" exists but is not empty", dirname);
         case -1:

             /*
              * Access problem
              */
-            pg_log_error("could not access directory \"%s\": %m", dirname);
-            exit(1);
+            pg_log_fatal("could not access directory \"%s\": %m", dirname);
     }
 }

@@ -898,23 +857,16 @@ parse_max_rate(char *src)
     errno = 0;
     result = strtod(src, &after_num);
     if (src == after_num)
-    {
-        pg_log_error("transfer rate \"%s\" is not a valid value", src);
-        exit(1);
-    }
+        pg_log_fatal("transfer rate \"%s\" is not a valid value", src);
     if (errno != 0)
-    {
-        pg_log_error("invalid transfer rate \"%s\": %m", src);
-        exit(1);
-    }
+        pg_log_fatal("invalid transfer rate \"%s\": %m", src);

     if (result <= 0)
     {
         /*
          * Reject obviously wrong values here.
          */
-        pg_log_error("transfer rate must be greater than zero");
-        exit(1);
+        pg_log_fatal("transfer rate must be greater than zero");
     }

     /*
@@ -944,27 +896,18 @@ parse_max_rate(char *src)
         after_num++;

     if (*after_num != '\0')
-    {
-        pg_log_error("invalid --max-rate unit: \"%s\"", suffix);
-        exit(1);
-    }
+        pg_log_fatal("invalid --max-rate unit: \"%s\"", suffix);

     /* Valid integer? */
     if ((uint64) result != (uint64) ((uint32) result))
-    {
-        pg_log_error("transfer rate \"%s\" exceeds integer range", src);
-        exit(1);
-    }
+        pg_log_fatal("transfer rate \"%s\" exceeds integer range", src);

     /*
      * The range is checked on the server side too, but avoid the server
      * connection if a nonsensical value was passed.
      */
     if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
-    {
-        pg_log_error("transfer rate \"%s\" is out of range", src);
-        exit(1);
-    }
+        pg_log_fatal("transfer rate \"%s\" is out of range", src);

     return (int32) result;
 }
@@ -1071,10 +1014,7 @@ parse_compress_options(char *src, WalCompressionMethod *methodres,
     /* Check the contents after the colon separator. */
     sep++;
     if (*sep == '\0')
-    {
-        pg_log_error("no compression level defined for method %s", firstpart);
-        exit(1);
-    }
+        pg_log_fatal("no compression level defined for method %s", firstpart);

     /*
      * For any of the methods currently supported, the data after the
@@ -1100,11 +1040,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
     /* Get the COPY data stream. */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_COPY_OUT)
-    {
-        pg_log_error("could not get COPY data stream: %s",
+        pg_log_fatal("could not get COPY data stream: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     PQclear(res);

     /* Loop over chunks until done. */
@@ -1120,11 +1057,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
             break;
         }
         else if (r == -2)
-        {
-            pg_log_error("could not read COPY data: %s",
+            pg_log_fatal("could not read COPY data: %s",
                          PQerrorMessage(conn));
-            exit(1);
-        }

         (*callback) (r, copybuf, callback_data);

@@ -1187,13 +1121,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
     if (must_parse_archive && !is_tar && !is_tar_gz && !is_tar_lz4)
     {
         pg_log_error("unable to parse archive: %s", archive_name);
-        pg_log_info("only tar archives can be parsed");
+        pg_log_error_detail("Only tar archives can be parsed.");
         if (format == 'p')
-            pg_log_info("plain format requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
         if (inject_manifest)
-            pg_log_info("using - as the output directory requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
         if (writerecoveryconf)
-            pg_log_info("the -R option requires pg_basebackup to parse the archive");
+            pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
         exit(1);
     }

@@ -1398,10 +1332,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 /* Sanity check. */
                 if (state->manifest_buffer != NULL ||
                     state->manifest_file !=NULL)
-                {
-                    pg_log_error("archives should precede manifest");
-                    exit(1);
-                }
+                    pg_log_fatal("archives should precede manifest");

                 /* Parse the rest of the CopyData message. */
                 archive_name = GetCopyDataString(r, copybuf, &cursor);
@@ -1416,11 +1347,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 if (archive_name[0] == '\0' || archive_name[0] == '.' ||
                     strchr(archive_name, '/') != NULL ||
                     strchr(archive_name, '\\') != NULL)
-                {
-                    pg_log_error("invalid archive name: \"%s\"",
+                    pg_log_fatal("invalid archive name: \"%s\"",
                                  archive_name);
-                    exit(1);
-                }

                 /*
                  * An empty spclocation is treated as NULL. We expect this
@@ -1479,9 +1407,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                          */
                         if (errno == 0)
                             errno = ENOSPC;
-                        pg_log_error("could not write to file \"%s\": %m",
+                        pg_log_fatal("could not write to file \"%s\": %m",
                                      state->manifest_filename);
-                        exit(1);
                     }
                 }
                 else if (state->streamer != NULL)
@@ -1491,10 +1418,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                                        r - 1, BBSTREAMER_UNKNOWN);
                 }
                 else
-                {
-                    pg_log_error("unexpected payload data");
-                    exit(1);
-                }
+                    pg_log_fatal("unexpected payload data");
                 break;
             }

@@ -1547,11 +1471,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                         state->manifest_file =
                             fopen(state->manifest_filename, "wb");
                         if (state->manifest_file == NULL)
-                        {
-                            pg_log_error("could not create file \"%s\": %m",
+                            pg_log_fatal("could not create file \"%s\": %m",
                                          state->manifest_filename);
-                            exit(1);
-                        }
                     }
                 }
                 break;
@@ -1640,11 +1561,10 @@ static void
 ReportCopyDataParseError(size_t r, char *copybuf)
 {
     if (r == 0)
-        pg_log_error("empty COPY message");
+        pg_log_fatal("empty COPY message");
     else
-        pg_log_error("malformed COPY message of type %d, length %zu",
+        pg_log_fatal("malformed COPY message of type %d, length %zu",
                      copybuf[0], r);
-    exit(1);
 }

 /*
@@ -1689,10 +1609,7 @@ ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
         initPQExpBuffer(&buf);
         ReceiveBackupManifestInMemory(conn, &buf);
         if (PQExpBufferDataBroken(buf))
-        {
-            pg_log_error("out of memory");
-            exit(1);
-        }
+            pg_log_fatal("out of memory");

         /* Inject it into the output tarfile. */
         bbstreamer_inject_file(manifest_inject_streamer, "backup_manifest",
@@ -1762,10 +1679,7 @@ ReceiveBackupManifest(PGconn *conn)
              "%s/backup_manifest.tmp", basedir);
     state.file = fopen(state.filename, "wb");
     if (state.file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", state.filename);
-        exit(1);
-    }
+        pg_log_fatal("could not create file \"%s\": %m", state.filename);

     ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);

@@ -1786,8 +1700,7 @@ ReceiveBackupManifestChunk(size_t r, char *copybuf, void *callback_data)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m", state->filename);
-        exit(1);
+        pg_log_fatal("could not write to file \"%s\": %m", state->filename);
     }
 }

@@ -1846,9 +1759,8 @@ BaseBackup(void)
     {
         const char *serverver = PQparameterStatus(conn, "server_version");

-        pg_log_error("incompatible server version %s",
+        pg_log_fatal("incompatible server version %s",
                      serverver ? serverver : "'unknown'");
-        exit(1);
     }
     if (serverMajor >= 1500)
         use_new_option_syntax = true;
@@ -1931,16 +1843,10 @@ BaseBackup(void)
         char       *colon;

         if (serverMajor < 1500)
-        {
-            pg_log_error("backup targets are not supported by this server version");
-            exit(1);
-        }
+            pg_log_fatal("backup targets are not supported by this server version");

         if (writerecoveryconf)
-        {
-            pg_log_error("recovery configuration cannot be written when a backup target is used");
-            exit(1);
-        }
+            pg_log_fatal("recovery configuration cannot be written when a backup target is used");

         AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");

@@ -1969,10 +1875,7 @@ BaseBackup(void)
         char *compressmethodstr = NULL;

         if (!use_new_option_syntax)
-        {
-            pg_log_error("server does not support server-side compression");
-            exit(1);
-        }
+            pg_log_fatal("server does not support server-side compression");
         switch (compressmethod)
         {
             case COMPRESSION_GZIP:
@@ -2010,28 +1913,19 @@ BaseBackup(void)
         basebkp = psprintf("BASE_BACKUP %s", buf.data);

     if (PQsendQuery(conn, basebkp) == 0)
-    {
-        pg_log_error("could not send replication command \"%s\": %s",
+        pg_log_fatal("could not send replication command \"%s\": %s",
                      "BASE_BACKUP", PQerrorMessage(conn));
-        exit(1);
-    }

     /*
      * Get the starting WAL location
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not initiate base backup: %s",
+        pg_log_fatal("could not initiate base backup: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
+        pg_log_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
                      PQntuples(res), PQnfields(res), 1, 2);
-        exit(1);
-    }

     strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));

@@ -2059,16 +1953,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not get backup header: %s",
+        pg_log_fatal("could not get backup header: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) < 1)
-    {
-        pg_log_error("no data returned from server");
-        exit(1);
-    }
+        pg_log_fatal("no data returned from server");

     /*
      * Sum up the total size, for progress reporting
@@ -2103,11 +1991,8 @@ BaseBackup(void)
     writing_to_stdout = format == 't' && basedir != NULL &&
         strcmp(basedir, "-") == 0;
     if (writing_to_stdout && PQntuples(res) > 1)
-    {
-        pg_log_error("can only write single tablespace to stdout, database has %d",
+        pg_log_fatal("can only write single tablespace to stdout, database has %d",
                      PQntuples(res));
-        exit(1);
-    }

     /*
      * If we're streaming WAL, start the streaming session before we start
@@ -2183,16 +2068,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("backup failed: %s",
+        pg_log_fatal("backup failed: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("no write-ahead log end position returned from server");
-        exit(1);
-    }
+        pg_log_fatal("no write-ahead log end position returned from server");
     strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
     if (verbose && includewal != NO_WAL)
         pg_log_info("write-ahead log end point: %s", xlogend);
@@ -2247,20 +2126,11 @@ BaseBackup(void)
         /* Just wait for the background process to exit */
         r = waitpid(bgchild, &status, 0);
         if (r == (pid_t) -1)
-        {
-            pg_log_error("could not wait for child process: %m");
-            exit(1);
-        }
+            pg_log_fatal("could not wait for child process: %m");
         if (r != bgchild)
-        {
-            pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
-            exit(1);
-        }
+            pg_log_fatal("child %d died, expected %d", (int) r, (int) bgchild);
         if (status != 0)
-        {
-            pg_log_error("%s", wait_result_to_str(status));
-            exit(1);
-        }
+            pg_log_fatal("%s", wait_result_to_str(status));
         /* Exited normally, we're happy! */
 #else                            /* WIN32 */

@@ -2270,11 +2140,8 @@ BaseBackup(void)
          * it's there.
          */
         if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-        {
-            pg_log_error("could not parse write-ahead log location \"%s\"",
+            pg_log_fatal("could not parse write-ahead log location \"%s\"",
                          xlogend);
-            exit(1);
-        }
         xlogendptr = ((uint64) hi) << 32 | lo;
         InterlockedIncrement(&has_xlogendptr);

@@ -2283,21 +2150,16 @@ BaseBackup(void)
             WAIT_OBJECT_0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not wait for child thread: %m");
-            exit(1);
+            pg_log_fatal("could not wait for child thread: %m");
         }
         if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not get child thread exit status: %m");
-            exit(1);
+            pg_log_fatal("could not get child thread exit status: %m");
         }
         if (status != 0)
-        {
-            pg_log_error("child thread exited with error %u",
+            pg_log_fatal("child thread exited with error %u",
                          (unsigned int) status);
-            exit(1);
-        }
         /* Exited normally, we're happy */
 #endif
     }
@@ -2364,11 +2226,8 @@ BaseBackup(void)
         else
         {
             if (rename(tmp_filename, filename) != 0)
-            {
-                pg_log_error("could not rename file \"%s\" to \"%s\": %m",
+                pg_log_fatal("could not rename file \"%s\" to \"%s\": %m",
                              tmp_filename, filename);
-                exit(1);
-            }
         }
     }

@@ -2458,11 +2317,8 @@ main(int argc, char **argv)
                 else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
                     format = 't';
                 else
-                {
-                    pg_log_error("invalid output format \"%s\", must be \"plain\" or \"tar\"",
+                    pg_log_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 'r':
                 maxrate = parse_max_rate(optarg);
@@ -2505,11 +2361,8 @@ main(int argc, char **argv)
                     includewal = STREAM_WAL;
                 }
                 else
-                {
-                    pg_log_error("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
+                    pg_log_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 1:
                 xlog_dir = pg_strdup(optarg);
@@ -2542,11 +2395,8 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "spread") == 0)
                     fastcheckpoint = false;
                 else
-                {
-                    pg_log_error("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
+                    pg_log_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 'd':
                 connection_string = pg_strdup(optarg);
@@ -2595,12 +2445,8 @@ main(int argc, char **argv)
                 manifest_checksums = pg_strdup(optarg);
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -2612,8 +2458,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2635,8 +2480,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && format != '\0')
     {
         pg_log_error("cannot specify both format and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == '\0')
@@ -2648,15 +2492,13 @@ main(int argc, char **argv)
     if (basedir == NULL && backup_target == NULL)
     {
         pg_log_error("must specify output directory or backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (basedir != NULL && backup_target != NULL)
     {
         pg_log_error("cannot specify both output directory and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2681,8 +2523,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("client-side compression is not possible when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2692,8 +2533,7 @@ main(int argc, char **argv)
     if (format == 'p' && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("only tar mode backups can be compressed");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2703,23 +2543,20 @@ main(int argc, char **argv)
     if (backup_target != NULL && includewal == STREAM_WAL)
     {
         pg_log_error("WAL cannot be streamed when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)
     {
         pg_log_error("cannot stream write-ahead logs in tar mode to stdout");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (replication_slot && includewal != STREAM_WAL)
     {
         pg_log_error("replication slots can only be used with WAL streaming");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2731,8 +2568,7 @@ main(int argc, char **argv)
         if (replication_slot)
         {
             pg_log_error("--no-slot cannot be used with slot name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         temp_replication_slot = false;
@@ -2744,8 +2580,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s needs a slot to be specified using --slot",
                          "--create-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2753,8 +2588,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s and %s are incompatible options",
                          "--create-slot", "--no-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2767,15 +2601,13 @@ main(int argc, char **argv)
         if (backup_target != NULL)
         {
             pg_log_error("WAL directory location cannot be specified along with a backup target");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         if (format != 'p')
         {
             pg_log_error("WAL directory location can only be specified in plain mode");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2784,8 +2616,7 @@ main(int argc, char **argv)
         if (!is_absolute_path(xlog_dir))
         {
             pg_log_error("WAL directory location must be an absolute path");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2798,37 +2629,29 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use compression level with method %s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
         case COMPRESSION_GZIP:
             if (compresslevel > 9)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 9",
+                pg_log_fatal("compression level %d of method %s higher than maximum of 9",
                              compresslevel, "gzip");
-                exit(1);
-            }
             if (compressloc == COMPRESS_LOCATION_CLIENT)
             {
 #ifdef HAVE_LIBZ
                 if (compresslevel == 0)
                     compresslevel = Z_DEFAULT_COMPRESSION;
 #else
-                pg_log_error("this build does not support compression with %s",
+                pg_log_fatal("this build does not support compression with %s",
                              "gzip");
-                exit(1);
 #endif
             }
             break;
         case COMPRESSION_LZ4:
             if (compresslevel > 12)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 12",
+                pg_log_fatal("compression level %d of method %s higher than maximum of 12",
                              compresslevel, "lz4");
-                exit(1);
-            }
             break;
     }

@@ -2839,8 +2662,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--progress", "--no-estimate-size");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2851,8 +2673,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-checksums");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2860,8 +2681,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-force-encode");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2917,13 +2737,9 @@ main(int argc, char **argv)

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, linkloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m", linkloc);
-            exit(1);
-        }
+            pg_log_fatal("could not create symbolic link \"%s\": %m", linkloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_log_fatal("symlinks are not supported on this platform");
 #endif
         free(linkloc);
     }
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index ccb215c398..7e2a9b7b19 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -239,10 +239,7 @@ get_destination_dir(char *dest_folder)
     Assert(dest_folder != NULL);
     dir = opendir(dest_folder);
     if (dir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", basedir);

     return dir;
 }
@@ -256,10 +253,7 @@ close_destination_dir(DIR *dest_dir, char *dest_folder)
 {
     Assert(dest_dir != NULL && dest_folder != NULL);
     if (closedir(dest_dir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", dest_folder);
-        exit(1);
-    }
+        pg_log_fatal("could not close directory \"%s\": %m", dest_folder);
 }


@@ -322,10 +316,7 @@ FindStreamingStart(uint32 *tli)

             snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
             if (stat(fullpath, &statbuf) != 0)
-            {
-                pg_log_error("could not stat file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_log_fatal("could not stat file \"%s\": %m", fullpath);

             if (statbuf.st_size != WalSegSz)
             {
@@ -346,27 +337,20 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open compressed file \"%s\": %m",
+                pg_log_fatal("could not open compressed file \"%s\": %m",
                              fullpath);
-                exit(1);
-            }
             if (lseek(fd, (off_t) (-4), SEEK_END) < 0)
-            {
-                pg_log_error("could not seek in compressed file \"%s\": %m",
+                pg_log_fatal("could not seek in compressed file \"%s\": %m",
                              fullpath);
-                exit(1);
-            }
             r = read(fd, (char *) buf, sizeof(buf));
             if (r != sizeof(buf))
             {
                 if (r < 0)
-                    pg_log_error("could not read compressed file \"%s\": %m",
+                    pg_log_fatal("could not read compressed file \"%s\": %m",
                                  fullpath);
                 else
-                    pg_log_error("could not read compressed file \"%s\": read %d of %zu",
+                    pg_log_fatal("could not read compressed file \"%s\": read %d of %zu",
                                  fullpath, r, sizeof(buf));
-                exit(1);
             }

             close(fd);
@@ -399,18 +383,12 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_log_fatal("could not open file \"%s\": %m", fullpath);

             status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not create LZ4 decompression context: %s",
+                pg_log_fatal("could not create LZ4 decompression context: %s",
                              LZ4F_getErrorName(status));
-                exit(1);
-            }

             outbuf = pg_malloc0(LZ4_CHUNK_SZ);
             readbuf = pg_malloc0(LZ4_CHUNK_SZ);
@@ -421,10 +399,7 @@ FindStreamingStart(uint32 *tli)

                 r = read(fd, readbuf, LZ4_CHUNK_SZ);
                 if (r < 0)
-                {
-                    pg_log_error("could not read file \"%s\": %m", fullpath);
-                    exit(1);
-                }
+                    pg_log_fatal("could not read file \"%s\": %m", fullpath);

                 /* Done reading the file */
                 if (r == 0)
@@ -442,12 +417,9 @@ FindStreamingStart(uint32 *tli)
                     status = LZ4F_decompress(ctx, outbuf, &out_size,
                                              readp, &read_size, &dec_opt);
                     if (LZ4F_isError(status))
-                    {
-                        pg_log_error("could not decompress file \"%s\": %s",
+                        pg_log_fatal("could not decompress file \"%s\": %s",
                                      fullpath,
                                      LZ4F_getErrorName(status));
-                        exit(1);
-                    }

                     readp += read_size;
                     uncompressed_size += out_size;
@@ -468,11 +440,8 @@ FindStreamingStart(uint32 *tli)

             status = LZ4F_freeDecompressionContext(ctx);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not free LZ4 decompression context: %s",
+                pg_log_fatal("could not free LZ4 decompression context: %s",
                              LZ4F_getErrorName(status));
-                exit(1);
-            }

             if (uncompressed_size != WalSegSz)
             {
@@ -483,8 +452,8 @@ FindStreamingStart(uint32 *tli)
 #else
             pg_log_error("could not check file \"%s\"",
                          dirent->d_name);
-            pg_log_error("this build does not support compression with %s",
-                         "LZ4");
+            pg_log_error_detail("This build does not support compression with %s.",
+                                "LZ4");
             exit(1);
 #endif
         }
@@ -501,10 +470,7 @@ FindStreamingStart(uint32 *tli)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", basedir);

     close_destination_dir(dir, basedir);

@@ -752,10 +718,7 @@ main(int argc, char **argv)
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_log_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'n':
@@ -793,19 +756,12 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     compression_method = COMPRESSION_NONE;
                 else
-                {
-                    pg_log_error("invalid value \"%s\" for option %s",
+                    pg_log_fatal("invalid value \"%s\" for option %s",
                                  optarg, "--compression-method");
-                    exit(1);
-                }
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -817,16 +773,14 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && do_create_slot)
     {
         pg_log_error("cannot use --create-slot together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -835,16 +789,14 @@ main(int argc, char **argv)
         /* translator: second %s is an option name */
         pg_log_error("%s needs a slot to be specified using --slot",
                      do_drop_slot ? "--drop-slot" : "--create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (synchronous && !do_sync)
     {
         pg_log_error("cannot use --synchronous together with --no-sync");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -854,8 +806,7 @@ main(int argc, char **argv)
     if (basedir == NULL && !do_drop_slot && !do_create_slot)
     {
         pg_log_error("no target directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,8 +821,7 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
@@ -883,9 +833,8 @@ main(int argc, char **argv)
                 compresslevel = Z_DEFAULT_COMPRESSION;
             }
 #else
-            pg_log_error("this build does not support compression with %s",
+            pg_log_fatal("this build does not support compression with %s",
                          "gzip");
-            exit(1);
 #endif
             break;
         case COMPRESSION_LZ4:
@@ -894,14 +843,12 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "lz4");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
 #else
-            pg_log_error("this build does not support compression with %s",
+            pg_log_fatal("this build does not support compression with %s",
                          "LZ4");
-            exit(1);
 #endif
             break;
     }
@@ -947,11 +894,8 @@ main(int argc, char **argv)
      * be defined in this context.
      */
     if (db_name)
-    {
-        pg_log_error("replication connection using slot \"%s\" is unexpectedly database specific",
+        pg_log_fatal("replication connection using slot \"%s\" is unexpectedly database specific",
                      replication_slot);
-        exit(1);
-    }

     /*
      * Set umask so that directories/files are created with the same
@@ -1009,10 +953,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_log_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index cc35d16f32..3ce950263b 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -193,10 +193,7 @@ OutputFsync(TimestampTz now)
         return true;

     if (fsync(outfd) != 0)
-    {
         pg_log_fatal("could not fsync file \"%s\": %m", outfile);
-        exit(1);
-    }

     return true;
 }
@@ -780,18 +777,12 @@ main(int argc, char **argv)
 /* replication options */
             case 'I':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse start position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_log_fatal("could not parse start position \"%s\"", optarg);
                 startpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_log_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'o':
@@ -842,12 +833,8 @@ main(int argc, char **argv)
                 break;

             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -859,8 +846,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,64 +856,56 @@ main(int argc, char **argv)
     if (replication_slot == NULL)
     {
         pg_log_error("no slot specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_start_slot && outfile == NULL)
     {
         pg_log_error("no target file specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && dbname == NULL)
     {
         pg_log_error("no database specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && !do_create_slot && !do_start_slot)
     {
         pg_log_error("at least one action needs to be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && (do_create_slot || do_start_slot))
     {
         pg_log_error("cannot use --create-slot or --start together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (startpos != InvalidXLogRecPtr && (do_create_slot || do_drop_slot))
     {
         pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (endpos != InvalidXLogRecPtr && !do_start_slot)
     {
         pg_log_error("--endpos may only be specified with --start");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (two_phase && !do_create_slot)
     {
         pg_log_error("--two-phase may only be specified with --create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -958,10 +936,7 @@ main(int argc, char **argv)
         exit(1);

     if (db_name == NULL)
-    {
-        pg_log_error("could not establish database-specific replication connection");
-        exit(1);
-    }
+        pg_log_fatal("could not establish database-specific replication connection");

     /*
      * Set umask so that directories/files are created with the same
@@ -1011,10 +986,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_log_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c
index d39e4b11a1..84f1a5a37a 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
             /* fsync file in case of a previous crash */
             if (stream->walmethod->sync(f) != 0)
             {
-                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
+                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
                              fn, stream->walmethod->getlasterror());
                 stream->walmethod->close(f, CLOSE_UNLINK);
                 exit(1);
@@ -778,11 +778,8 @@ HandleCopyStream(PGconn *conn, StreamCtl *stream,
         if (stream->synchronous && lastFlushPosition < blockpos && walfile != NULL)
         {
             if (stream->walmethod->sync(walfile) != 0)
-            {
                 pg_log_fatal("could not fsync file \"%s\": %s",
                              current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
             lastFlushPosition = blockpos;

             /*
@@ -1030,11 +1027,8 @@ ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
              * shutdown of the server.
              */
             if (stream->walmethod->sync(walfile) != 0)
-            {
                 pg_log_fatal("could not fsync file \"%s\": %s",
                              current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
             lastFlushPosition = blockpos;
         }

diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4a6afd1a06..a3b067e4fe 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -88,10 +88,7 @@ GetConnection(void)
     {
         conn_opts = PQconninfoParse(connection_string, &err_msg);
         if (conn_opts == NULL)
-        {
-            pg_log_error("%s", err_msg);
-            exit(1);
-        }
+            pg_log_fatal("%s", err_msg);

         for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
         {
@@ -182,10 +179,7 @@ GetConnection(void)
          * and PQconnectdbParams returns NULL, we call exit(1) directly.
          */
         if (!tmpconn)
-        {
-            pg_log_error("could not connect to server");
-            exit(1);
-        }
+            pg_log_fatal("could not connect to server");

         /* If we need a password and -w wasn't given, loop back and get one */
         if (PQstatus(tmpconn) == CONNECTION_BAD &&
diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c
index a6d08c1270..9b2733a8d4 100644
--- a/src/bin/pg_basebackup/walmethods.c
+++ b/src/bin/pg_basebackup/walmethods.c
@@ -1197,7 +1197,6 @@ tar_close(Walfile f, WalCloseMethod method)
         /* XXX this seems pretty bogus; why is only this case fatal? */
         pg_log_fatal("could not fsync file \"%s\": %s",
                      tf->pathname, tar_getlasterror());
-        exit(1);
     }

     /* Clean up and done */
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 7e69475947..b25b984391 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -208,10 +208,7 @@ scan_file(const char *fn, int segmentno)
     f = open(fn, PG_BINARY | flags, 0);

     if (f < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", fn);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\": %m", fn);

     files_scanned++;

@@ -225,12 +222,11 @@ scan_file(const char *fn, int segmentno)
         if (r != BLCKSZ)
         {
             if (r < 0)
-                pg_log_error("could not read block %u in file \"%s\": %m",
+                pg_log_fatal("could not read block %u in file \"%s\": %m",
                              blockno, fn);
             else
-                pg_log_error("could not read block %u in file \"%s\": read %d of %d",
+                pg_log_fatal("could not read block %u in file \"%s\": read %d of %d",
                              blockno, fn, r, BLCKSZ);
-            exit(1);
         }
         blocks_scanned++;

@@ -275,22 +271,18 @@ scan_file(const char *fn, int segmentno)

             /* Seek back to beginning of block */
             if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
-            {
-                pg_log_error("seek failed for block %u in file \"%s\": %m", blockno, fn);
-                exit(1);
-            }
+                pg_log_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);

             /* Write block with checksum */
             w = write(f, buf.data, BLCKSZ);
             if (w != BLCKSZ)
             {
                 if (w < 0)
-                    pg_log_error("could not write block %u in file \"%s\": %m",
+                    pg_log_fatal("could not write block %u in file \"%s\": %m",
                                  blockno, fn);
                 else
-                    pg_log_error("could not write block %u in file \"%s\": wrote %d of %d",
+                    pg_log_fatal("could not write block %u in file \"%s\": wrote %d of %d",
                                  blockno, fn, w, BLCKSZ);
-                exit(1);
             }
         }

@@ -334,10 +326,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
     dir = opendir(path);
     if (!dir)
-    {
-        pg_log_error("could not open directory \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", path);
     while ((de = readdir(dir)) != NULL)
     {
         char        fn[MAXPGPATH];
@@ -361,10 +350,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)

         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
         if (lstat(fn, &st) < 0)
-        {
-            pg_log_error("could not stat file \"%s\": %m", fn);
-            exit(1);
-        }
+            pg_log_fatal("could not stat file \"%s\": %m", fn);
         if (S_ISREG(st.st_mode))
         {
             char        fnonly[MAXPGPATH];
@@ -388,11 +374,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                 *segmentpath++ = '\0';
                 segmentno = atoi(segmentpath);
                 if (segmentno == 0)
-                {
-                    pg_log_error("invalid segment number %d in file name \"%s\"",
+                    pg_log_fatal("invalid segment number %d in file name \"%s\"",
                                  segmentno, fn);
-                    exit(1);
-                }
             }

             forkpath = strchr(fnonly, '_');
@@ -440,11 +423,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);

                 if (lstat(tblspc_path, &tblspc_st) < 0)
-                {
-                    pg_log_error("could not stat file \"%s\": %m",
+                    pg_log_fatal("could not stat file \"%s\": %m",
                                  tblspc_path);
-                    exit(1);
-                }

                 /*
                  * Move backwards once as the scan needs to happen for the
@@ -539,7 +519,8 @@ main(int argc, char *argv[])
                 showprogress = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -555,7 +536,7 @@ main(int argc, char *argv[])
         if (DataDir == NULL)
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -565,8 +546,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -574,30 +554,23 @@ main(int argc, char *argv[])
     if (mode != PG_MODE_CHECK && only_filenode)
     {
         pg_log_error("option -f/--filenode can only be used with --check");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     /* Read the control file and check compatibility */
     ControlFile = get_controlfile(DataDir, &crc_ok);
     if (!crc_ok)
-    {
-        pg_log_error("pg_control CRC value is incorrect");
-        exit(1);
-    }
+        pg_log_fatal("pg_control CRC value is incorrect");

     if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
-    {
-        pg_log_error("cluster is not compatible with this version of pg_checksums");
-        exit(1);
-    }
+        pg_log_fatal("cluster is not compatible with this version of pg_checksums");

     if (ControlFile->blcksz != BLCKSZ)
     {
         pg_log_error("database cluster is not compatible");
-        fprintf(stderr, _("The database cluster was initialized with block size %u, but pg_checksums was compiled with
blocksize %u.\n"), 
-                ControlFile->blcksz, BLCKSZ);
+        pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled
withblock size %u.", 
+                            ControlFile->blcksz, BLCKSZ);
         exit(1);
     }

@@ -608,31 +581,19 @@ main(int argc, char *argv[])
      */
     if (ControlFile->state != DB_SHUTDOWNED &&
         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
-    {
-        pg_log_error("cluster must be shut down");
-        exit(1);
-    }
+        pg_log_fatal("cluster must be shut down");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_CHECK)
-    {
-        pg_log_error("data checksums are not enabled in cluster");
-        exit(1);
-    }
+        pg_log_fatal("data checksums are not enabled in cluster");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_DISABLE)
-    {
-        pg_log_error("data checksums are already disabled in cluster");
-        exit(1);
-    }
+        pg_log_fatal("data checksums are already disabled in cluster");

     if (ControlFile->data_checksum_version > 0 &&
         mode == PG_MODE_ENABLE)
-    {
-        pg_log_error("data checksums are already enabled in cluster");
-        exit(1);
-    }
+        pg_log_fatal("data checksums are already enabled in cluster");

     /* Operate on all files if checking or enabling checksums */
     if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d94..c390ec51ce 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -134,7 +134,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -152,15 +153,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b9a25442f5..f3f13394cf 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -1001,13 +1001,10 @@ findParentsByOid(TableInfo *self,

                 parent = findTableByOid(inhinfo[i].inhparent);
                 if (parent == NULL)
-                {
-                    pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
+                    pg_log_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
                                  inhinfo[i].inhparent,
                                  self->dobj.name,
                                  oid);
-                    exit_nicely(1);
-                }
                 self->parents[j++] = parent;
             }
         }
@@ -1043,10 +1040,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
             if (j > 0)
             {
                 if (argNum >= arraysize)
-                {
-                    pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
-                    exit_nicely(1);
-                }
+                    pg_log_fatal("could not parse numeric array \"%s\": too many numbers", str);
                 temp[j] = '\0';
                 array[argNum++] = atooid(temp);
                 j = 0;
@@ -1058,10 +1052,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
         {
             if (!(isdigit((unsigned char) s) || s == '-') ||
                 j >= sizeof(temp) - 1)
-            {
-                pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
-                exit_nicely(1);
-            }
+                pg_log_fatal("could not parse numeric array \"%s\": invalid character in number", str);
             temp[j++] = s;
         }
     }
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index bc5251be82..7cc9217143 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -250,10 +250,7 @@ init_parallel_dump_utils(void)
         /* Initialize socket access */
         err = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (err != 0)
-        {
-            pg_log_error("%s() failed: error code %d", "WSAStartup", err);
-            exit_nicely(1);
-        }
+            pg_log_fatal("%s() failed: error code %d", "WSAStartup", err);

         parallel_init_done = true;
     }
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..6f93fa5e36 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1736,34 +1736,34 @@ warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt,...)

         case STAGE_INITIALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while INITIALIZING:");
+                pg_log_info("while INITIALIZING:");
             break;

         case STAGE_PROCESSING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while PROCESSING TOC:");
+                pg_log_info("while PROCESSING TOC:");
             break;

         case STAGE_FINALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while FINALIZING:");
+                pg_log_info("while FINALIZING:");
             break;
     }
     if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
     {
-        pg_log_generic(PG_LOG_INFO, "from TOC entry %d; %u %u %s %s %s",
-                       AH->currentTE->dumpId,
-                       AH->currentTE->catalogId.tableoid,
-                       AH->currentTE->catalogId.oid,
-                       AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
-                       AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
-                       AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
+        pg_log_info("from TOC entry %d; %u %u %s %s %s",
+                    AH->currentTE->dumpId,
+                    AH->currentTE->catalogId.tableoid,
+                    AH->currentTE->catalogId.oid,
+                    AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
+                    AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
+                    AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
     }
     AH->lastErrorStage = AH->stage;
     AH->lastErrorTE = AH->currentTE;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, fmt, ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
     va_end(ap);

     if (AH->public.exit_on_error)
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 3184eda3e7..0289b0843c 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -50,9 +50,10 @@ _check_database_version(ArchiveHandle *AH)
         && (remoteversion < AH->public.minRemoteVersion ||
             remoteversion > AH->public.maxRemoteVersion))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
-        fatal("aborting because of server version mismatch");
+        pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
+        exit(1);
     }

     /*
@@ -261,7 +262,7 @@ GetConnection(Archive *AHX)
 static void
 notice_processor(void *arg, const char *message)
 {
-    pg_log_generic(PG_LOG_INFO, "%s", message);
+    pg_log_info("%s", message);
 }

 /* Like fatal(), but with a complaint about a particular query. */
@@ -270,7 +271,8 @@ die_on_query_failure(ArchiveHandle *AH, const char *query)
 {
     pg_log_error("query failed: %s",
                  PQerrorMessage(AH->connection));
-    fatal("query was: %s", query);
+    pg_log_error_detail("Query was: %s", query);
+    exit(1);
 }

 void
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 57140a5504..55ecd6f128 100644
--- a/src/bin/pg_dump/pg_backup_utils.c
+++ b/src/bin/pg_dump/pg_backup_utils.c
@@ -52,8 +52,7 @@ set_dump_section(const char *arg, int *dumpSections)
     else
     {
         pg_log_error("unrecognized section name: \"%s\"", arg);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }
 }
@@ -64,10 +63,7 @@ void
 on_exit_nicely(on_exit_nicely_callback function, void *arg)
 {
     if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
-    {
         pg_log_fatal("out of on_exit_nicely slots");
-        exit_nicely(1);
-    }
     on_exit_nicely_list[on_exit_nicely_index].function = function;
     on_exit_nicely_list[on_exit_nicely_index].arg = arg;
     on_exit_nicely_index++;
diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index 6ebc3afee4..19e2132f42 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -31,6 +31,13 @@ extern void set_dump_section(const char *arg, int *dumpSections);
 extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);
 extern void exit_nicely(int code) pg_attribute_noreturn();

-#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)
+/* In pg_dump, we modify pg_log_fatal to call exit_nicely instead of exit */
+#undef pg_log_fatal
+#define pg_log_fatal(...) do { \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit_nicely(1); \
+    } while(0)
+
+#define fatal(...) pg_log_fatal(__VA_ARGS__)

 #endif                            /* PG_BACKUP_UTILS_H */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e69dcf8a48..ca68bf8dbe 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -620,7 +620,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -637,8 +638,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -655,10 +655,7 @@ main(int argc, char **argv)
         dopt.sequence_data = 1;

     if (dopt.dataOnly && dopt.schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_log_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
         fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
@@ -667,10 +664,7 @@ main(int argc, char **argv)
         fatal("option --include-foreign-data is not supported with parallel backup");

     if (dopt.dataOnly && dopt.outputClean)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_log_fatal("options -c/--clean and -a/--data-only cannot be used together");

     if (dopt.if_exists && !dopt.outputClean)
         fatal("option --if-exists requires option -c/--clean");
@@ -2033,8 +2027,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     {
         /* copy data transfer failed */
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("The command was: %s", q->data);
         exit_nicely(1);
     }

@@ -2043,8 +2037,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("The command was: %s", q->data);
         exit_nicely(1);
     }
     PQclear(res);
@@ -3714,11 +3708,8 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
     else if (polinfo->polcmd == 'd')
         cmd = " FOR DELETE";
     else
-    {
-        pg_log_error("unexpected policy command type: %c",
+        pg_log_fatal("unexpected policy command type: %c",
                      polinfo->polcmd);
-        exit_nicely(1);
-    }

     query = createPQExpBuffer();
     delqry = createPQExpBuffer();
@@ -8407,7 +8398,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                                       "expected %d check constraints on table \"%s\" but found %d",
                                       tbinfo->ncheck),
                              tbinfo->ncheck, tbinfo->dobj.name, numcons);
-                pg_log_error("(The system catalogs might be corrupted.)");
+                pg_log_error_hint("The system catalogs might be corrupted.");
                 exit_nicely(1);
             }

@@ -16480,13 +16471,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+        pg_log_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
                               "query to get data of sequence \"%s\" returned %d rows (expected 1)",
                               PQntuples(res)),
                      tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }

     seqtype = PQgetvalue(res, 0, 0);
     startv = PQgetvalue(res, 0, 1);
@@ -16702,13 +16690,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+        pg_log_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
                               "query to get data of sequence \"%s\" returned %d rows (expected 1)",
                               PQntuples(res)),
                      tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }

     last = PQgetvalue(res, 0, 0);
     called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
@@ -16797,10 +16782,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
         else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
             appendPQExpBufferStr(query, "INSTEAD OF");
         else
-        {
-            pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
-            exit_nicely(1);
-        }
+            pg_log_fatal("unexpected tgtype value: %d", tginfo->tgtype);

         findx = 0;
         if (TRIGGER_FOR_INSERT(tginfo->tgtype))
@@ -16872,11 +16854,10 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
             if (p + tlen >= tgargs + lentgargs)
             {
                 /* hm, not found before end of bytea value... */
-                pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
+                pg_log_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
                              tginfo->tgargs,
                              tginfo->dobj.name,
                              tbinfo->dobj.name);
-                exit_nicely(1);
             }

             if (findx > 0)
@@ -17142,11 +17123,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

         if (PQntuples(res) != 1)
-        {
-            pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+            pg_log_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
                          rinfo->dobj.name, tbinfo->dobj.name);
-            exit_nicely(1);
-        }

         printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index d979f93b3d..9b28e7edff 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1233,9 +1233,9 @@ repairDependencyLoop(DumpableObject **loop,
                                 "there are circular foreign-key constraints among these tables:",
                                 nLoop));
         for (i = 0; i < nLoop; i++)
-            pg_log_generic(PG_LOG_INFO, "  %s", loop[i]->name);
-        pg_log_generic(PG_LOG_INFO, "You might not be able to restore the dump without using --disable-triggers or
temporarilydropping the constraints."); 
-        pg_log_generic(PG_LOG_INFO, "Consider using a full dump instead of a --data-only dump to avoid this
problem.");
+            pg_log_info("  %s", loop[i]->name);
+        pg_log_info("You might not be able to restore the dump without using --disable-triggers or temporarily
droppingthe constraints."); 
+        pg_log_info("Consider using a full dump instead of a --data-only dump to avoid this problem.");
         if (nLoop > 1)
             removeObjectDependency(loop[0], loop[1]->dumpId);
         else                    /* must be a self-dependency */
@@ -1253,7 +1253,7 @@ repairDependencyLoop(DumpableObject **loop,
         char        buf[1024];

         describeDumpableObject(loop[i], buf, sizeof(buf));
-        pg_log_generic(PG_LOG_INFO, "  %s", buf);
+        pg_log_info("  %s", buf);
     }

     if (nLoop > 1)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..5854ebbc6f 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -200,16 +200,15 @@ main(int argc, char *argv[])
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
+            pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
                          "same directory as \"%s\".\n"
                          "Check your installation.",
                          "pg_dump", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
+            pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
                          "but was not the same version as %s.\n"
                          "Check your installation.",
                          "pg_dump", full_path, progname);
-        exit_nicely(1);
     }

     pgdumpopts = createPQExpBuffer();
@@ -339,7 +338,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -349,8 +349,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -358,8 +357,7 @@ main(int argc, char *argv[])
         (globals_only || roles_only || tablespaces_only))
     {
         pg_log_error("option --exclude-database cannot be used together with -g/--globals-only, -r/--roles-only, or
-t/--tablespaces-only");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -367,30 +365,24 @@ main(int argc, char *argv[])
     if (globals_only && roles_only)
     {
         pg_log_error("options -g/--globals-only and -r/--roles-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (globals_only && tablespaces_only)
     {
         pg_log_error("options -g/--globals-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (if_exists && !output_clean)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_log_fatal("option --if-exists requires option -c/--clean");

     if (roles_only && tablespaces_only)
     {
         pg_log_error("options -r/--roles-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -451,10 +443,7 @@ main(int argc, char *argv[])
                                prompt_password, false);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", pgdb);
-            exit_nicely(1);
-        }
+            pg_log_fatal("could not connect to database \"%s\"", pgdb);
     }
     else
     {
@@ -468,8 +457,7 @@ main(int argc, char *argv[])
         {
             pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n"
                          "Please specify an alternative database.");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
     }
@@ -487,11 +475,8 @@ main(int argc, char *argv[])
     {
         OPF = fopen(filename, PG_BINARY_W);
         if (!OPF)
-        {
-            pg_log_error("could not open output file \"%s\": %m",
+            pg_log_fatal("could not open output file \"%s\": %m",
                          filename);
-            exit_nicely(1);
-        }
     }
     else
         OPF = stdout;
@@ -502,11 +487,8 @@ main(int argc, char *argv[])
     if (dumpencoding)
     {
         if (PQsetClientEncoding(conn, dumpencoding) < 0)
-        {
-            pg_log_error("invalid client encoding \"%s\" specified",
+            pg_log_fatal("invalid client encoding \"%s\" specified",
                          dumpencoding);
-            exit_nicely(1);
-        }
     }

     /*
@@ -1321,20 +1303,14 @@ dumpDatabases(PGconn *conn)

         ret = runPgDump(dbname, create_opts);
         if (ret != 0)
-        {
-            pg_log_error("pg_dump failed on database \"%s\", exiting", dbname);
-            exit_nicely(1);
-        }
+            pg_log_fatal("pg_dump failed on database \"%s\", exiting", dbname);

         if (filename)
         {
             OPF = fopen(filename, PG_BINARY_A);
             if (!OPF)
-            {
-                pg_log_error("could not re-open the output file \"%s\": %m",
+                pg_log_fatal("could not re-open the output file \"%s\": %m",
                              filename);
-                exit_nicely(1);
-            }
         }

     }
@@ -1470,10 +1446,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         {
             conn_opts = PQconninfoParse(connection_string, &err_msg);
             if (conn_opts == NULL)
-            {
-                pg_log_error("%s", err_msg);
-                exit_nicely(1);
-            }
+                pg_log_fatal("%s", err_msg);

             for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
             {
@@ -1540,10 +1513,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", dbname);
-            exit_nicely(1);
-        }
+            pg_log_fatal("could not connect to database \"%s\"", dbname);

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -1560,10 +1530,7 @@ connectDatabase(const char *dbname, const char *connection_string,
     if (PQstatus(conn) == CONNECTION_BAD)
     {
         if (fail_on_error)
-        {
-            pg_log_error("%s", PQerrorMessage(conn));
-            exit_nicely(1);
-        }
+            pg_log_fatal("%s", PQerrorMessage(conn));
         else
         {
             PQfinish(conn);
@@ -1589,17 +1556,11 @@ connectDatabase(const char *dbname, const char *connection_string,
     /* Check version */
     remoteversion_str = PQparameterStatus(conn, "server_version");
     if (!remoteversion_str)
-    {
-        pg_log_error("could not get server version");
-        exit_nicely(1);
-    }
+        pg_log_fatal("could not get server version");
     server_version = PQserverVersion(conn);
     if (server_version == 0)
-    {
-        pg_log_error("could not parse server version \"%s\"",
+        pg_log_fatal("could not parse server version \"%s\"",
                      remoteversion_str);
-        exit_nicely(1);
-    }

     my_version = PG_VERSION_NUM;

@@ -1611,9 +1572,9 @@ connectDatabase(const char *dbname, const char *connection_string,
         && (server_version < 90200 ||
             (server_version / 100) > (my_version / 100)))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
         pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
         exit_nicely(1);
     }

@@ -1675,7 +1636,7 @@ executeQuery(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
@@ -1698,7 +1659,7 @@ executeCommand(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 55bf1b6975..ae35578f14 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -287,7 +287,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -303,17 +304,13 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     /* Complain if neither -f nor -d was specified (except if dumping TOC) */
     if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
-    {
-        pg_log_error("one of -d/--dbname and -f/--file must be specified");
-        exit_nicely(1);
-    }
+        pg_log_fatal("one of -d/--dbname and -f/--file must be specified");

     /* Should get at most one of -d and -f, else user is confused */
     if (opts->cparams.dbname)
@@ -321,41 +318,28 @@ main(int argc, char **argv)
         if (opts->filename)
         {
             pg_log_error("options -d/--dbname and -f/--file cannot be used together");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
         opts->useDB = 1;
     }

     if (opts->dataOnly && opts->schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_log_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (opts->dataOnly && opts->dropSchema)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_log_fatal("options -c/--clean and -a/--data-only cannot be used together");

     /*
      * -C is not compatible with -1, because we can't create a database inside
      * a transaction block.
      */
     if (opts->createDB && opts->single_txn)
-    {
-        pg_log_error("options -C/--create and -1/--single-transaction cannot be used together");
-        exit_nicely(1);
-    }
+        pg_log_fatal("options -C/--create and -1/--single-transaction cannot be used together");

     /* Can't do single-txn mode with multiple connections */
     if (opts->single_txn && numWorkers > 1)
-    {
-        pg_log_error("cannot specify both --single-transaction and multiple jobs");
-        exit_nicely(1);
-    }
+        pg_log_fatal("cannot specify both --single-transaction and multiple jobs");

     opts->disable_triggers = disable_triggers;
     opts->enable_row_security = enable_row_security;
@@ -369,10 +353,7 @@ main(int argc, char **argv)
     opts->no_subscriptions = no_subscriptions;

     if (if_exists && !opts->dropSchema)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_log_fatal("option --if-exists requires option -c/--clean");
     opts->if_exists = if_exists;
     opts->strict_names = strict_names;

@@ -396,9 +377,8 @@ main(int argc, char **argv)
                 break;

             default:
-                pg_log_error("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
+                pg_log_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
                              opts->formatName);
-                exit_nicely(1);
         }
     }

diff --git a/src/bin/pg_dump/t/003_pg_dump_with_server.pl b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
index 528db179cb..c284866326 100644
--- a/src/bin/pg_dump/t/003_pg_dump_with_server.pl
+++ b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
@@ -30,7 +30,7 @@ my ($cmd, $stdout, $stderr, $result);

 command_fails_like(
     [ "pg_dump", '-p', $port, '--include-foreign-data=s0', 'postgres' ],
-    qr/foreign-data wrapper \"dummy\" has no handler\r?\npg_dump: error: query was:.*t0/,
+    qr/foreign-data wrapper \"dummy\" has no handler\r?\ndetail: Query was: .*t0/,
     "correctly fails to dump a foreign table from a dummy FDW");

 command_ok(
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 1eb4509fca..2cd6815bf1 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -161,14 +161,11 @@ main(int argc, char *argv[])
                     /*------
                       translator: the second %s is a command line argument (-e, etc) */
                     pg_log_error("invalid argument for option %s", "-e");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_xid_epoch == -1)
-                {
-                    pg_log_error("transaction ID epoch (-e) must not be -1");
-                    exit(1);
-                }
+                    pg_log_fatal("transaction ID epoch (-e) must not be -1");
                 break;

             case 'u':
@@ -177,14 +174,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-u");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_oldest_xid))
-                {
-                    pg_log_error("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_log_fatal("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
                 break;

             case 'x':
@@ -193,14 +187,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-x");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_xid))
-                {
-                    pg_log_error("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_log_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
                 break;

             case 'c':
@@ -209,30 +200,24 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 if (set_oldest_commit_ts_xid < 2 &&
                     set_oldest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_log_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");

                 if (set_newest_commit_ts_xid < 2 &&
                     set_newest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_log_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");
                 break;

             case 'o':
@@ -241,14 +226,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-o");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_oid == 0)
-                {
-                    pg_log_error("OID (-o) must not be 0");
-                    exit(1);
-                }
+                    pg_log_fatal("OID (-o) must not be 0");
                 break;

             case 'm':
@@ -257,7 +239,7 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -265,24 +247,18 @@ main(int argc, char *argv[])
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxid == 0)
-                {
-                    pg_log_error("multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_log_fatal("multitransaction ID (-m) must not be 0");

                 /*
                  * XXX It'd be nice to have more sanity checks here, e.g. so
                  * that oldest is not wrapped around w.r.t. nextMulti.
                  */
                 if (set_oldestmxid == 0)
-                {
-                    pg_log_error("oldest multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_log_fatal("oldest multitransaction ID (-m) must not be 0");
                 break;

             case 'O':
@@ -291,21 +267,18 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-O");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxoff == -1)
-                {
-                    pg_log_error("multitransaction offset (-O) must not be -1");
-                    exit(1);
-                }
+                    pg_log_fatal("multitransaction offset (-O) must not be -1");
                 break;

             case 'l':
                 if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
                 {
                     pg_log_error("invalid argument for option %s", "-l");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -320,19 +293,14 @@ main(int argc, char *argv[])
                 errno = 0;
                 set_wal_segsize = strtol(optarg, &endptr, 10) * 1024 * 1024;
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("argument of --wal-segsize must be a number");
-                    exit(1);
-                }
+                    pg_log_fatal("argument of --wal-segsize must be a number");
                 if (!IsValidWalSegSize(set_wal_segsize))
-                {
-                    pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-                    exit(1);
-                }
+                    pg_log_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -345,15 +313,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -367,8 +334,8 @@ main(int argc, char *argv[])
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        pg_log_info("You must run %s as the PostgreSQL superuser.",
-                    progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+                          progname);
         exit(1);
     }
 #endif
@@ -377,20 +344,14 @@ main(int argc, char *argv[])

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(DataDir))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
+        pg_log_fatal("could not read permissions of directory \"%s\": %m",
                      DataDir);
-        exit(1);
-    }

     umask(pg_mode_mask);

     if (chdir(DataDir) < 0)
-    {
-        pg_log_error("could not change directory to \"%s\": %m",
+        pg_log_fatal("could not change directory to \"%s\": %m",
                      DataDir);
-        exit(1);
-    }

     /* Check that data directory matches our server version */
     CheckDataVersion();
@@ -402,16 +363,13 @@ main(int argc, char *argv[])
     if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
     {
         if (errno != ENOENT)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
+            pg_log_fatal("could not open file \"%s\" for reading: %m",
                          "postmaster.pid");
-            exit(1);
-        }
     }
     else
     {
         pg_log_error("lock file \"%s\" exists", "postmaster.pid");
-        pg_log_info("Is a server running?  If not, delete the lock file and try again.");
+        pg_log_error_hint("Is a server running?  If not, delete the lock file and try again.");
         exit(1);
     }

@@ -557,20 +515,16 @@ CheckDataVersion(void)
     char        rawline[64];

     if ((ver_fd = fopen(ver_file, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m",
+        pg_log_fatal("could not open file \"%s\" for reading: %m",
                      ver_file);
-        exit(1);
-    }

     /* version number has to be the first line read */
     if (!fgets(rawline, sizeof(rawline), ver_fd))
     {
         if (!ferror(ver_fd))
-            pg_log_error("unexpected empty file \"%s\"", ver_file);
+            pg_log_fatal("unexpected empty file \"%s\"", ver_file);
         else
-            pg_log_error("could not read file \"%s\": %m", ver_file);
-        exit(1);
+            pg_log_fatal("could not read file \"%s\": %m", ver_file);
     }

     /* strip trailing newline and carriage return */
@@ -579,8 +533,8 @@ CheckDataVersion(void)
     if (strcmp(rawline, PG_MAJORVERSION) != 0)
     {
         pg_log_error("data directory is of wrong version");
-        pg_log_info("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
-                    ver_file, rawline, PG_MAJORVERSION);
+        pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version
\"%s\".",
+                            ver_file, rawline, PG_MAJORVERSION);
         exit(1);
     }

@@ -612,10 +566,10 @@ read_controlfile(void)
         pg_log_error("could not open file \"%s\" for reading: %m",
                      XLOG_CONTROL_FILE);
         if (errno == ENOENT)
-            pg_log_info("If you are sure the data directory path is correct, execute\n"
-                        "  touch %s\n"
-                        "and try again.",
-                        XLOG_CONTROL_FILE);
+            pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
+                              "  touch %s\n"
+                              "and try again.",
+                              XLOG_CONTROL_FILE);
         exit(1);
     }

@@ -624,10 +578,7 @@ read_controlfile(void)

     len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
     if (len < 0)
-    {
-        pg_log_error("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
-        exit(1);
-    }
+        pg_log_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
     close(fd);

     if (len >= sizeof(ControlFileData) &&
@@ -968,10 +919,7 @@ FindEndOfXLOG(void)
      */
     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1003,16 +951,10 @@ FindEndOfXLOG(void)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not close directory \"%s\": %m", XLOGDIR);

     /*
      * Finally, convert to new xlog seg size, and advance by one to ensure we
@@ -1036,10 +978,7 @@ KillExistingXLOG(void)

     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1048,24 +987,15 @@ KillExistingXLOG(void)
         {
             snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_log_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not close directory \"%s\": %m", XLOGDIR);
 }


@@ -1083,10 +1013,7 @@ KillExistingArchiveStatus(void)

     xldir = opendir(ARCHSTATDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1098,24 +1025,15 @@ KillExistingArchiveStatus(void)
         {
             snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_log_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
 }


@@ -1179,10 +1097,7 @@ WriteEmptyXLOG(void)
     fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
               pg_file_create_mode);
     if (fd < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\": %m", path);

     errno = 0;
     if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
@@ -1190,8 +1105,7 @@ WriteEmptyXLOG(void)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
+        pg_log_fatal("could not write file \"%s\": %m", path);
     }

     /* Fill the rest of the file with zeroes */
@@ -1203,16 +1117,12 @@ WriteEmptyXLOG(void)
         {
             if (errno == 0)
                 errno = ENOSPC;
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
+            pg_log_fatal("could not write file \"%s\": %m", path);
         }
     }

     if (fsync(fd) != 0)
-    {
-        pg_log_error("fsync error: %m");
-        exit(1);
-    }
+        pg_log_fatal("fsync error: %m");

     close(fd);
 }
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index efb82a4034..90d005e5f8 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -160,10 +160,6 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
-
             case 'c':
                 restore_wal = true;
                 break;
@@ -204,34 +200,39 @@ main(int argc, char **argv)
             case 4:
                 no_ensure_shutdown = true;
                 break;
+
+            default:
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+                exit(1);
         }
     }

     if (datadir_source == NULL && connstr_source == NULL)
     {
         pg_log_error("no source specified (--source-pgdata or --source-server)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_source != NULL && connstr_source != NULL)
     {
         pg_log_error("only one of --source-pgdata or --source-server can be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_target == NULL)
     {
         pg_log_error("no target data directory specified (--target-pgdata)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (writerecoveryconf && connstr_source == NULL)
     {
         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -239,7 +240,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -253,8 +254,8 @@ main(int argc, char **argv)
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
-                progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.\n",
+                          progname);
         exit(1);
     }
 #endif
@@ -263,11 +264,8 @@ main(int argc, char **argv)

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(datadir_target))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
+        pg_log_fatal("could not read permissions of directory \"%s\": %m",
                      datadir_target);
-        exit(1);
-    }

     umask(pg_mode_mask);

@@ -1035,16 +1033,15 @@ getRestoreCommand(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (rc == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
+            pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
                          "same directory as \"%s\".\n"
                          "Check your installation.",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
+            pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
                          "but was not the same version as %s.\n"
                          "Check your installation.",
                          "postgres", full_path, progname);
-        exit(1);
     }

     /*
@@ -1125,7 +1122,8 @@ ensureCleanShutdown(const char *argv0)
     if (system(cmd) != 0)
     {
         pg_log_error("postgres single-user mode in target cluster failed");
-        pg_fatal("Command was: %s", cmd);
+        pg_log_error_detail("Command was: %s", cmd);
+        exit(1);
     }
 }

diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 388870ce95..815ee36801 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -34,7 +34,7 @@ extern uint64 fetch_size;
 extern uint64 fetch_done;

 /* logging support */
-#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#define pg_fatal(...) pg_log_fatal(__VA_ARGS__)

 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c
index df8f82a50c..983388c92b 100644
--- a/src/bin/pg_rewind/timeline.c
+++ b/src/bin/pg_rewind/timeline.c
@@ -73,19 +73,19 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
         {
             /* expect a numeric timeline ID as first field of line */
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a numeric timeline ID.");
+            pg_log_error_detail("Expected a numeric timeline ID.");
             exit(1);
         }
         if (nfields != 3)
         {
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a write-ahead log switchpoint location.");
+            pg_log_error_detail("Expected a write-ahead log switchpoint location.");
             exit(1);
         }
         if (entries && tli <= lasttli)
         {
             pg_log_error("invalid data in history file: %s", fline);
-            pg_log_error("Timeline IDs must be in increasing sequence.");
+            pg_log_error_detail("Timeline IDs must be in increasing sequence.");
             exit(1);
         }

@@ -106,7 +106,7 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
     if (entries && targetTLI <= lasttli)
     {
         pg_log_error("invalid data in history file");
-        pg_log_error("Timeline IDs must be less than child timeline's ID.");
+        pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
         exit(1);
     }

diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index ddabf64c58..2e98fd3d1a 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -47,10 +47,7 @@ do { \
     alarm_triggered = false; \
     if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
         INVALID_HANDLE_VALUE) \
-    { \
-        pg_log_error("could not create thread for alarm"); \
-        exit(1); \
-    } \
+        pg_log_fatal("could not create thread for alarm"); \
     gettimeofday(&start_t, NULL); \
 } while (0)
 #endif
@@ -95,7 +92,7 @@ static int    pg_fsync_writethrough(int fd);
 #endif
 static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops);

-#define die(msg) do { pg_log_error("%s: %m", _(msg)); exit(1); } while(0)
+#define die(msg) pg_log_fatal("%s: %m", _(msg))


 int
@@ -186,24 +183,20 @@ handle_args(int argc, char *argv[])
                     errno != 0 || optval != (unsigned int) optval)
                 {
                     pg_log_error("invalid argument for option %s", "--secs-per-test");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 secs_per_test = (unsigned int) optval;
                 if (secs_per_test == 0)
-                {
-                    pg_log_error("%s must be in range %u..%u",
+                    pg_log_fatal("%s must be in range %u..%u",
                                  "--secs-per-test", 1, UINT_MAX);
-                    exit(1);
-                }
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -211,8 +204,7 @@ handle_args(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05cb520c11..a41a7ae044 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -252,8 +252,8 @@ main(int argc, char **argv)
                 canonicalize_path(wal_directory);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -261,9 +261,8 @@ main(int argc, char **argv)
     /* Get backup directory name */
     if (optind >= argc)
     {
-        pg_log_fatal("no backup directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error("no backup directory specified");
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     context.backup_directory = pstrdup(argv[optind++]);
@@ -272,10 +271,9 @@ main(int argc, char **argv)
     /* Complain if any arguments remain */
     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -304,7 +302,6 @@ main(int argc, char **argv)
                              "but was not the same version as %s.\n"
                              "Check your installation.",
                              "pg_waldump", full_path, "pg_verifybackup");
-            exit(1);
         }
     }

@@ -449,7 +446,7 @@ report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
@@ -840,7 +837,7 @@ report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     context->saw_any_error = true;
@@ -857,7 +854,7 @@ report_fatal_error(const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 118beb53d7..7890645161 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -192,7 +192,7 @@ sub test_fatal_error

     my ($test_name, $manifest_contents) = @_;

-    test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+    test_bad_manifest($test_name, qr/error: $test_name/, $manifest_contents);
     return;
 }

diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a6251e1a96..ae99778e3c 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -75,7 +75,7 @@ typedef struct XLogDumpStats
     Stats        record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
 } XLogDumpStats;

-#define fatal_error(...) do { pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)
+#define fatal_error(...) pg_log_fatal(__VA_ARGS__)

 /*
  * When sigint is called, just tell the system to exit at the next possible
@@ -1185,6 +1185,6 @@ main(int argc, char **argv)
     return EXIT_SUCCESS;

 bad_argument:
-    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     return EXIT_FAILURE;
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f166a77e3a..7680d7ff76 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1301,8 +1301,8 @@ executeStatement(PGconn *con, const char *sql)
     res = PQexec(con, sql);
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
-        pg_log_fatal("query failed: %s", PQerrorMessage(con));
-        pg_log_info("query was: %s", sql);
+        pg_log_error("query failed: %s", PQerrorMessage(con));
+        pg_log_error_detail("Query was: %s", sql);
         exit(1);
     }
     PQclear(res);
@@ -1318,7 +1318,7 @@ tryExecuteStatement(PGconn *con, const char *sql)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("%s", PQerrorMessage(con));
-        pg_log_info("(ignoring this error and continuing anyway)");
+        pg_log_error_detail("(ignoring this error and continuing anyway)");
     }
     PQclear(res);
 }
@@ -2651,7 +2651,6 @@ evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)
         default:
             /* internal error which should never occur */
             pg_log_fatal("unexpected enode type in evaluation: %d", expr->etype);
-            exit(1);
     }
 }

@@ -4192,10 +4191,7 @@ initGenerateDataClientSide(PGconn *con)
     res = PQexec(con, copy_statement);

     if (PQresultStatus(res) != PGRES_COPY_IN)
-    {
         pg_log_fatal("unexpected copy in result: %s", PQerrorMessage(con));
-        exit(1);
-    }
     PQclear(res);

     start = pg_time_now();
@@ -4209,10 +4205,7 @@ initGenerateDataClientSide(PGconn *con)
                           INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",
                           j, k / naccounts + 1, 0);
         if (PQputline(con, sql.data))
-        {
             pg_log_fatal("PQputline failed");
-            exit(1);
-        }

         if (CancelRequested)
             break;
@@ -4254,15 +4247,9 @@ initGenerateDataClientSide(PGconn *con)
         fputc('\n', stderr);    /* Need to move to next line */

     if (PQputline(con, "\\.\n"))
-    {
         pg_log_fatal("very last PQputline failed");
-        exit(1);
-    }
     if (PQendcopy(con))
-    {
         pg_log_fatal("PQendcopy failed");
-        exit(1);
-    }

     termPQExpBuffer(&sql);

@@ -4402,17 +4389,14 @@ static void
 checkInitSteps(const char *initialize_steps)
 {
     if (initialize_steps[0] == '\0')
-    {
         pg_log_fatal("no initialization steps specified");
-        exit(1);
-    }

     for (const char *step = initialize_steps; *step != '\0'; step++)
     {
         if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
         {
-            pg_log_fatal("unrecognized initialization step \"%c\"", *step);
-            pg_log_info("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
+            pg_log_error("unrecognized initialization step \"%c\"", *step);
+            pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
             exit(1);
         }
     }
@@ -4433,10 +4417,7 @@ runInitSteps(const char *initialize_steps)
     initPQExpBuffer(&stats);

     if ((con = doConnect()) == NULL)
-    {
         pg_log_fatal("could not create connection for initialization");
-        exit(1);
-    }

     setup_cancel_handler(NULL);
     SetCancelConn(con);
@@ -4479,7 +4460,7 @@ runInitSteps(const char *initialize_steps)
             case ' ':
                 break;            /* ignore */
             default:
-                pg_log_fatal("unrecognized initialization step \"%c\"", *step);
+                pg_log_error("unrecognized initialization step \"%c\"", *step);
                 PQfinish(con);
                 exit(1);
         }
@@ -4523,21 +4504,18 @@ GetTableInfo(PGconn *con, bool scale_given)
     {
         char       *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);

-        pg_log_fatal("could not count number of branches: %s", PQerrorMessage(con));
+        pg_log_error("could not count number of branches: %s", PQerrorMessage(con));

         if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
-            pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\"",
-                        PQdb(con));
+            pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
+                              PQdb(con));

         exit(1);
     }
     scale = atoi(PQgetvalue(res, 0, 0));
     if (scale < 0)
-    {
         pg_log_fatal("invalid count(*) from pgbench_branches: \"%s\"",
                      PQgetvalue(res, 0, 0));
-        exit(1);
-    }
     PQclear(res);

     /* warn if we override user-given -s switch */
@@ -4584,8 +4562,8 @@ GetTableInfo(PGconn *con, bool scale_given)
          * This case is unlikely as pgbench already found "pgbench_branches"
          * above to compute the scale.
          */
-        pg_log_fatal("no pgbench_accounts table found in search_path");
-        pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
+        pg_log_error("no pgbench_accounts table found in search_path");
+        pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
         exit(1);
     }
     else                        /* PQntupes(res) == 1 */
@@ -4608,7 +4586,6 @@ GetTableInfo(PGconn *con, bool scale_given)
             {
                 /* possibly a newer version with new partition method */
                 pg_log_fatal("unexpected partition method: \"%s\"", ps);
-                exit(1);
             }
         }

@@ -4700,7 +4677,7 @@ syntax_error(const char *source, int lineno,
     if (command != NULL)
         appendPQExpBuffer(&buf, " in command \"%s\"", command);

-    pg_log_fatal("%s", buf.data);
+    pg_log_error("%s", buf.data);

     termPQExpBuffer(&buf);

@@ -5050,7 +5027,6 @@ ConditionError(const char *desc, int cmdn, const char *msg)
 {
     pg_log_fatal("condition error in script \"%s\" command %d: %s",
                  desc, cmdn, msg);
-    exit(1);
 }

 /*
@@ -5286,18 +5262,12 @@ process_file(const char *filename, int weight)
     if (strcmp(filename, "-") == 0)
         fd = stdin;
     else if ((fd = fopen(filename, "r")) == NULL)
-    {
         pg_log_fatal("could not open file \"%s\": %m", filename);
-        exit(1);
-    }

     buf = read_file_contents(fd);

     if (ferror(fd))
-    {
         pg_log_fatal("could not read file \"%s\": %m", filename);
-        exit(1);
-    }

     if (fd != stdin)
         fclose(fd);
@@ -5350,9 +5320,9 @@ findBuiltin(const char *name)

     /* error cases */
     if (found == 0)
-        pg_log_fatal("no builtin script found for name \"%s\"", name);
+        pg_log_error("no builtin script found for name \"%s\"", name);
     else                        /* found > 1 */
-        pg_log_fatal("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
+        pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);

     listAvailableScripts();
     exit(1);
@@ -5384,16 +5354,10 @@ parseScriptWeight(const char *option, char **script)
         errno = 0;
         wtmp = strtol(sep + 1, &badp, 10);
         if (errno != 0 || badp == sep + 1 || *badp != '\0')
-        {
             pg_log_fatal("invalid weight specification: %s", sep);
-            exit(1);
-        }
         if (wtmp > INT_MAX || wtmp < 0)
-        {
             pg_log_fatal("weight specification out of range (0 .. %d): %lld",
                          INT_MAX, (long long) wtmp);
-            exit(1);
-        }
         weight = wtmp;
     }
     else
@@ -5410,16 +5374,10 @@ static void
 addScript(ParsedScript script)
 {
     if (script.commands == NULL || script.commands[0] == NULL)
-    {
         pg_log_fatal("empty command list for script \"%s\"", script.desc);
-        exit(1);
-    }

     if (num_scripts >= MAX_SCRIPTS)
-    {
         pg_log_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
-        exit(1);
-    }

     CheckConditional(script);

@@ -5735,7 +5693,7 @@ set_random_seed(const char *seed)
         if (sscanf(seed, "%lu%c", &ulseed, &garbage) != 1)
         {
             pg_log_error("unrecognized random seed option \"%s\"", seed);
-            pg_log_info("Expecting an unsigned integer, \"time\" or \"rand\"");
+            pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
             return false;
         }
         iseed = (uint64) ulseed;
@@ -5866,10 +5824,7 @@ main(int argc, char **argv)

     /* set random seed early, because it may be used while parsing scripts. */
     if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
-    {
         pg_log_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
-        exit(1);
-    }

     while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) !=
-1)
     {
@@ -5916,15 +5871,12 @@ main(int argc, char **argv)
 #else                            /* but BSD doesn't ... */
                 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
 #endif                            /* RLIMIT_NOFILE */
-                {
                     pg_log_fatal("getrlimit failed: %m");
-                    exit(1);
-                }
                 if (rlim.rlim_cur < nclients + 3)
                 {
-                    pg_log_fatal("need at least %d open files, but system limit is %ld",
+                    pg_log_error("need at least %d open files, but system limit is %ld",
                                  nclients + 3, (long) rlim.rlim_cur);
-                    pg_log_info("Reduce number of clients, or use limit/ulimit to increase the system limit.");
+                    pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
                     exit(1);
                 }
 #endif                            /* HAVE_GETRLIMIT */
@@ -5938,10 +5890,7 @@ main(int argc, char **argv)
                 }
 #ifndef ENABLE_THREAD_SAFETY
                 if (nthreads != 1)
-                {
                     pg_log_fatal("threads are not supported on this platform; use -j1");
-                    exit(1);
-                }
 #endif                            /* !ENABLE_THREAD_SAFETY */
                 break;
             case 'C':
@@ -6014,10 +5963,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
-                    {
                         pg_log_fatal("invalid variable definition: \"%s\"", optarg);
-                        exit(1);
-                    }

                     *p++ = '\0';
                     if (!putVariable(&state[0], "option", optarg, p))
@@ -6036,10 +5982,7 @@ main(int argc, char **argv)
                     if (strcmp(optarg, QUERYMODE[querymode]) == 0)
                         break;
                 if (querymode >= NUM_QUERYMODE)
-                {
                     pg_log_fatal("invalid query mode (-M): \"%s\"", optarg);
-                    exit(1);
-                }
                 break;
             case 'P':
                 benchmarking_option_set = true;
@@ -6055,10 +5998,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if (throttle_value <= 0.0)
-                    {
                         pg_log_fatal("invalid rate limit: \"%s\"", optarg);
-                        exit(1);
-                    }
                     /* Invert rate limit into per-transaction delay in usec */
                     throttle_delay = 1000000.0 / throttle_value;
                 }
@@ -6068,10 +6008,7 @@ main(int argc, char **argv)
                     double        limit_ms = atof(optarg);

                     if (limit_ms <= 0.0)
-                    {
                         pg_log_fatal("invalid latency limit: \"%s\"", optarg);
-                        exit(1);
-                    }
                     benchmarking_option_set = true;
                     latency_limit = (int64) (limit_ms * 1000);
                 }
@@ -6092,10 +6029,7 @@ main(int argc, char **argv)
                 benchmarking_option_set = true;
                 sample_rate = atof(optarg);
                 if (sample_rate <= 0.0 || sample_rate > 1.0)
-                {
                     pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
-                    exit(1);
-                }
                 break;
             case 5:                /* aggregate-interval */
                 benchmarking_option_set = true;
@@ -6118,10 +6052,7 @@ main(int argc, char **argv)
             case 9:                /* random-seed */
                 benchmarking_option_set = true;
                 if (!set_random_seed(optarg))
-                {
                     pg_log_fatal("error while setting random seed from --random-seed option");
-                    exit(1);
-                }
                 break;
             case 10:            /* list */
                 {
@@ -6144,16 +6075,13 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "hash") == 0)
                     partition_method = PART_HASH;
                 else
-                {
                     pg_log_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -6179,10 +6107,7 @@ main(int argc, char **argv)
     }

     if (total_weight == 0 && !is_init_mode)
-    {
         pg_log_fatal("total script weight must not be zero");
-        exit(1);
-    }

     /* show per script stats if several scripts are used */
     if (num_scripts > 1)
@@ -6217,25 +6142,19 @@ main(int argc, char **argv)

     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (is_init_mode)
     {
         if (benchmarking_option_set)
-        {
             pg_log_fatal("some of the specified options cannot be used in initialization (-i) mode");
-            exit(1);
-        }

         if (partitions == 0 && partition_method != PART_NONE)
-        {
             pg_log_fatal("--partition-method requires greater than zero --partitions");
-            exit(1);
-        }

         /* set default method */
         if (partitions > 0 && partition_method == PART_NONE)
@@ -6271,17 +6190,11 @@ main(int argc, char **argv)
     else
     {
         if (initialization_option_set)
-        {
             pg_log_fatal("some of the specified options cannot be used in benchmarking mode");
-            exit(1);
-        }
     }

     if (nxacts > 0 && duration > 0)
-    {
         pg_log_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
-        exit(1);
-    }

     /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
     if (nxacts <= 0 && duration <= 0)
@@ -6289,47 +6202,26 @@ main(int argc, char **argv)

     /* --sampling-rate may be used only with -l */
     if (sample_rate > 0.0 && !use_log)
-    {
         pg_log_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
-        exit(1);
-    }

     /* --sampling-rate may not be used with --aggregate-interval */
     if (sample_rate > 0.0 && agg_interval > 0)
-    {
         pg_log_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same
time");
-        exit(1);
-    }

     if (agg_interval > 0 && !use_log)
-    {
         pg_log_fatal("log aggregation is allowed only when actually logging transactions");
-        exit(1);
-    }

     if (!use_log && logfile_prefix)
-    {
         pg_log_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
-        exit(1);
-    }

     if (duration > 0 && agg_interval > duration)
-    {
         pg_log_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)",
agg_interval,duration); 
-        exit(1);
-    }

     if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
-    {
         pg_log_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
-        exit(1);
-    }

     if (progress_timestamp && progress == 0)
-    {
         pg_log_fatal("--progress-timestamp is allowed only under --progress");
-        exit(1);
-    }

     /*
      * save main process id in the global variable because process id will be
@@ -6378,10 +6270,7 @@ main(int argc, char **argv)
     /* opening connection... */
     con = doConnect();
     if (con == NULL)
-    {
         pg_log_fatal("could not create connection for setup");
-        exit(1);
-    }

     /* report pgbench and server versions */
     printVersion(con);
@@ -6487,10 +6376,7 @@ main(int argc, char **argv)

     errno = THREAD_BARRIER_INIT(&barrier, nthreads);
     if (errno != 0)
-    {
         pg_log_fatal("could not initialize barrier: %m");
-        exit(1);
-    }

 #ifdef ENABLE_THREAD_SAFETY
     /* start all threads but thread 0 which is executed directly later */
@@ -6502,10 +6388,7 @@ main(int argc, char **argv)
         errno = THREAD_CREATE(&thread->thread, threadRun, thread);

         if (errno != 0)
-        {
             pg_log_fatal("could not create thread: %m");
-            exit(1);
-        }
     }
 #else
     Assert(nthreads == 1);
@@ -6569,7 +6452,7 @@ main(int argc, char **argv)
     THREAD_BARRIER_DESTROY(&barrier);

     if (exit_code != 0)
-        pg_log_fatal("Run was aborted; the above results are incomplete.");
+        pg_log_error("Run was aborted; the above results are incomplete.");

     return exit_code;
 }
@@ -6603,10 +6486,7 @@ threadRun(void *arg)
         thread->logfile = fopen(logpath, "w");

         if (thread->logfile == NULL)
-        {
             pg_log_fatal("could not open logfile \"%s\": %m", logpath);
-            exit(1);
-        }
     }

     /* explicitly initialize the state machines */
@@ -6633,7 +6513,6 @@ threadRun(void *arg)
                 /* coldly abort on initial connection failure */
                 pg_log_fatal("could not create connection for client %d",
                              state[i].id);
-                exit(1);
             }
         }
     }
@@ -6901,10 +6780,7 @@ setalarm(int seconds)
         !CreateTimerQueueTimer(&timer, queue,
                                win32_timer_callback, NULL, seconds * 1000, 0,
                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-    {
         pg_log_fatal("failed to set timer");
-        exit(1);
-    }
 }

 #endif                            /* WIN32 */
@@ -7049,7 +6925,6 @@ add_socket_to_set(socket_set *sa, int fd, int idx)
          * complicating the API to make it less grotty.
          */
         pg_log_fatal("too many client connections for select()");
-        exit(1);
     }
     FD_SET(fd, &sa->fds);
     if (fd > sa->maxfd)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 292cff5df9..1380a95b3e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -232,7 +232,7 @@ HandleSlashCmds(PsqlScanState scan_state,
     {
         pg_log_error("invalid command \\%s", cmd);
         if (pset.cur_cmd_interactive)
-            pg_log_info("Try \\? for help.");
+            pg_log_error_hint("Try \\? for help.");
         status = PSQL_CMD_ERROR;
     }

diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index d65b9a124f..f9c0a2a4d2 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -302,7 +302,7 @@ CheckConnection(void)
     {
         if (!pset.cur_cmd_interactive)
         {
-            pg_log_fatal("connection to server was lost");
+            pg_log_error("connection to server was lost");
             exit(EXIT_BADCONN);
         }

diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..a5b0fa7f49 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -58,10 +58,7 @@ usage(unsigned short int pager)
     {
         user = get_user_name(&errstr);
         if (!user)
-        {
             pg_log_fatal("%s", errstr);
-            exit(EXIT_FAILURE);
-        }
     }

     /*
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index e5c976fc4f..168fbb35c4 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -77,10 +77,7 @@ MainLoop(FILE *source)
     if (PQExpBufferBroken(query_buf) ||
         PQExpBufferBroken(previous_buf) ||
         PQExpBufferBroken(history_buf))
-    {
-        pg_log_error("out of memory");
-        exit(EXIT_FAILURE);
-    }
+        pg_log_fatal("out of memory");

     /* main loop to get queries and execute them */
     while (successResult == EXIT_SUCCESS)
@@ -398,10 +395,7 @@ MainLoop(FILE *source)
             prompt_status = prompt_tmp;

             if (PQExpBufferBroken(query_buf))
-            {
-                pg_log_error("out of memory");
-                exit(EXIT_FAILURE);
-            }
+                pg_log_fatal("out of memory");

             /*
              * Increase statement line number counter for each linebreak added
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index be9dec749d..596635eac1 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -216,10 +216,7 @@ main(int argc, char *argv[])

     /* Bail out if -1 was specified but will be ignored. */
     if (options.single_txn && options.actions.head == NULL)
-    {
         pg_log_fatal("-1 can only be used in non-interactive mode");
-        exit(EXIT_FAILURE);
-    }

     if (!pset.popt.topt.fieldSep.separator &&
         !pset.popt.topt.fieldSep.separator_zero)
@@ -342,11 +339,8 @@ main(int argc, char *argv[])
     {
         pset.logfile = fopen(options.logfilename, "a");
         if (!pset.logfile)
-        {
             pg_log_fatal("could not open log file \"%s\": %m",
                          options.logfilename);
-            exit(EXIT_FAILURE);
-        }
     }

     if (!options.no_psqlrc)
@@ -607,10 +601,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                     }

                     if (!result)
-                    {
                         pg_log_fatal("could not set printing parameter \"%s\"", value);
-                        exit(EXIT_FAILURE);
-                    }

                     free(value);
                     break;
@@ -716,10 +707,10 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                 break;
             default:
         unknown_option:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        pset.progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.",
+                                  pset.progname);
                 exit(EXIT_FAILURE);
-                break;
         }
     }

@@ -781,10 +772,7 @@ process_psqlrc(char *argv0)
     char       *envrc = getenv("PSQLRC");

     if (find_my_exec(argv0, my_exec_path) < 0)
-    {
         pg_log_fatal("could not find own program executable");
-        exit(EXIT_FAILURE);
-    }

     get_etc_path(my_exec_path, etc_path);

diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 4c97bd41d7..b33e5a4db9 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -109,7 +109,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -128,7 +129,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -144,16 +145,10 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot cluster all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot cluster all databases and a specific one at the same time");

         if (tables.head != NULL)
-        {
-            pg_log_error("cannot cluster specific table(s) in all databases");
-            exit(1);
-        }
+            pg_log_fatal("cannot cluster specific table(s) in all databases");

         cparams.dbname = maintenance_db;

diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index b0c6805bc9..d1cfe1787a 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -120,7 +120,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -139,22 +140,16 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 2]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

     if (locale)
     {
         if (lc_ctype)
-        {
-            pg_log_error("only one of --locale and --lc-ctype can be specified");
-            exit(1);
-        }
+            pg_log_fatal("only one of --locale and --lc-ctype can be specified");
         if (lc_collate)
-        {
-            pg_log_error("only one of --locale and --lc-collate can be specified");
-            exit(1);
-        }
+            pg_log_fatal("only one of --locale and --lc-collate can be specified");
         lc_ctype = locale;
         lc_collate = locale;
     }
@@ -162,10 +157,7 @@ main(int argc, char *argv[])
     if (encoding)
     {
         if (pg_char_to_encoding(encoding) < 0)
-        {
-            pg_log_error("\"%s\" is not a valid encoding name", encoding);
-            exit(1);
-        }
+            pg_log_fatal("\"%s\" is not a valid encoding name", encoding);
     }

     if (dbname == NULL)
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index d6ce04a809..b5cabc33f0 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -166,7 +166,8 @@ main(int argc, char *argv[])
                 interactive = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -181,7 +182,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -274,11 +275,8 @@ main(int argc, char *argv[])
                                                    newuser,
                                                    NULL);
         if (!encrypted_password)
-        {
-            pg_log_error("password encryption failed: %s",
+            pg_log_fatal("password encryption failed: %s",
                          PQerrorMessage(conn));
-            exit(1);
-        }
         appendStringLiteralConn(&sql, encrypted_password, conn);
         PQfreemem(encrypted_password);
     }
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index 7e321dd11b..afc00dac78 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -100,7 +100,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -109,7 +110,7 @@ main(int argc, char *argv[])
     {
         case 0:
             pg_log_error("missing required argument database name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         case 1:
             dbname = argv[optind];
@@ -117,7 +118,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index dfe4a5088c..82c1f35ab2 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -91,7 +91,8 @@ main(int argc, char *argv[])
                 /* this covers the long options */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -106,7 +107,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -119,7 +120,7 @@ main(int argc, char *argv[])
         else
         {
             pg_log_error("missing required argument role name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c
index a7653b3eaf..1aa834742d 100644
--- a/src/bin/scripts/pg_isready.c
+++ b/src/bin/scripts/pg_isready.c
@@ -93,7 +93,8 @@ main(int argc, char **argv)
                 pguser = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);

                 /*
                  * We need to make sure we don't return 1 here because someone
@@ -107,7 +108,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);

         /*
          * We need to make sure we don't return 1 here because someone
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index c292d43203..f8bfda1404 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -170,7 +170,8 @@ main(int argc, char *argv[])
                 tablespace = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -189,7 +190,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -205,30 +206,15 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot reindex all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex all databases and a specific one at the same time");
         if (syscatalog)
-        {
-            pg_log_error("cannot reindex all databases and system catalogs at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex all databases and system catalogs at the same time");
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) in all databases");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific schema(s) in all databases");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) in all databases");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific table(s) in all databases");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) in all databases");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific index(es) in all databases");

         cparams.dbname = maintenance_db;

@@ -238,26 +224,14 @@ main(int argc, char *argv[])
     else if (syscatalog)
     {
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific schema(s) and system catalogs at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific table(s) and system catalogs at the same time");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot reindex specific index(es) and system catalogs at the same time");

         if (concurrentCons > 1)
-        {
-            pg_log_error("cannot use multiple jobs to reindex system catalogs");
-            exit(1);
-        }
+            pg_log_fatal("cannot use multiple jobs to reindex system catalogs");

         if (dbname == NULL)
         {
@@ -283,10 +257,7 @@ main(int argc, char *argv[])
          * depending on the same relation.
          */
         if (concurrentCons > 1 && indexes.head != NULL)
-        {
-            pg_log_error("cannot use multiple jobs to reindex indexes");
-            exit(1);
-        }
+            pg_log_fatal("cannot use multiple jobs to reindex indexes");

         if (dbname == NULL)
         {
@@ -349,17 +320,15 @@ reindex_one_database(ConnParams *cparams, ReindexType type,
     if (concurrently && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "concurrently", "12");
-        exit(1);
     }

     if (tablespace && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "tablespace", "14");
-        exit(1);
     }

     if (!parallel)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 4f6917fd39..f19969d6a3 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -237,7 +237,8 @@ main(int argc, char *argv[])
                 vacopts.process_toast = false;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -256,54 +257,33 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (vacopts.analyze_only)
     {
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "full");
-            exit(1);
-        }
         if (vacopts.freeze)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "freeze");
-            exit(1);
-        }
         if (vacopts.disable_page_skipping)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "disable-page-skipping");
-            exit(1);
-        }
         if (vacopts.no_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-index-cleanup");
-            exit(1);
-        }
         if (vacopts.force_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "force-index-cleanup");
-            exit(1);
-        }
         if (!vacopts.do_truncate)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-truncate");
-            exit(1);
-        }
         if (!vacopts.process_toast)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-process-toast");
-            exit(1);
-        }
         /* allow 'and_analyze' with 'analyze_only' */
     }

@@ -311,26 +291,17 @@ main(int argc, char *argv[])
     if (vacopts.parallel_workers >= 0)
     {
         if (vacopts.analyze_only)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "parallel");
-            exit(1);
-        }
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing full vacuum",
+            pg_log_fatal("cannot use the \"%s\" option when performing full vacuum",
                          "parallel");
-            exit(1);
-        }
     }

     /* Prohibit --no-index-cleanup and --force-index-cleanup together */
     if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
-    {
-        pg_log_error("cannot use the \"%s\" option with the \"%s\" option",
+        pg_log_fatal("cannot use the \"%s\" option with the \"%s\" option",
                      "no-index-cleanup", "force-index-cleanup");
-        exit(1);
-    }

     /* fill cparams except for dbname, which is set below */
     cparams.pghost = host;
@@ -348,15 +319,9 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot vacuum all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_log_fatal("cannot vacuum all databases and a specific one at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot vacuum specific table(s) in all databases");
-            exit(1);
-        }
+            pg_log_fatal("cannot vacuum specific table(s) in all databases");

         cparams.dbname = maintenance_db;

@@ -457,71 +422,56 @@ vacuum_one_database(ConnParams *cparams,
     if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "disable-page-skipping", "9.6");
-        exit(1);
     }

     if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-index-cleanup", "12");
-        exit(1);
     }

     if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "force-index-cleanup", "12");
-        exit(1);
     }

     if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-truncate", "12");
-        exit(1);
     }

     if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-process-toast", "14");
-        exit(1);
     }

     if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "skip-locked", "12");
-        exit(1);
     }

     if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--min-xid-age", "9.6");
-        exit(1);
-    }

     if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--min-mxid-age", "9.6");
-        exit(1);
-    }

     if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--parallel", "13");
-        exit(1);
-    }

     if (!quiet)
     {
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7138068633..19d308ad1f 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -300,7 +300,7 @@ fsync_fname(const char *fname, bool isdir)
      */
     if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL)))
     {
-        pg_log_fatal("could not fsync file \"%s\": %m", fname);
+        pg_log_error("could not fsync file \"%s\": %m", fname);
         (void) close(fd);
         exit(EXIT_FAILURE);
     }
@@ -370,7 +370,7 @@ durable_rename(const char *oldfile, const char *newfile)
     {
         if (fsync(fd) != 0)
         {
-            pg_log_fatal("could not fsync file \"%s\": %m", newfile);
+            pg_log_error("could not fsync file \"%s\": %m", newfile);
             close(fd);
             exit(EXIT_FAILURE);
         }
@@ -448,7 +448,7 @@ get_dirent_type(const char *path,
         {
             result = PGFILETYPE_ERROR;
 #ifdef FRONTEND
-            pg_log_generic(elevel, "could not stat file \"%s\": %m", path);
+            pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
 #else
             ereport(elevel,
                     (errcode_for_file_access(),
diff --git a/src/common/logging.c b/src/common/logging.c
index 9a076bb812..18d6669f27 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -151,6 +151,9 @@ pg_logging_init(const char *argv0)
     }
 }

+/*
+ * Change the logging flags.
+ */
 void
 pg_logging_config(int new_flags)
 {
@@ -194,17 +197,19 @@ pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)
 }

 void
-pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
+pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+               const char *pg_restrict fmt,...)
 {
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(level, fmt, ap);
+    pg_log_generic_v(level, part, fmt, ap);
     va_end(ap);
 }

 void
-pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
+pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                 const char *pg_restrict fmt, va_list ap)
 {
     int            save_errno = errno;
     const char *filename = NULL;
@@ -232,7 +237,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     fmt = _(fmt);

-    if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+    if (part == PG_LOG_PRIMARY &&
+        (!(log_flags & PG_LOG_FLAG_TERSE) || filename))
     {
         if (sgr_locus)
             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
@@ -251,30 +257,34 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     if (!(log_flags & PG_LOG_FLAG_TERSE))
     {
-        switch (level)
+        switch (part)
         {
-            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);
+            case PG_LOG_PRIMARY:
+                switch (level)
+                {
+                    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;
+                }
                 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);
+            case PG_LOG_DETAIL:
+                fprintf(stderr, _("detail: "));
                 break;
-            default:
+            case PG_LOG_HINT:
+                fprintf(stderr, _("hint: "));
                 break;
         }
     }
diff --git a/src/common/restricted_token.c b/src/common/restricted_token.c
index 48b1ce0585..8f3bfdf2db 100644
--- a/src/common/restricted_token.c
+++ b/src/common/restricted_token.c
@@ -190,10 +190,7 @@ get_restricted_token(void)
             WaitForSingleObject(pi.hProcess, INFINITE);

             if (!GetExitCodeProcess(pi.hProcess, &x))
-            {
-                pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError());
-                exit(1);
-            }
+                pg_log_fatal("could not get exit code from subprocess: error code %lu", GetLastError());
             exit(x);
         }
         pg_free(cmdline);
diff --git a/src/fe_utils/archive.c b/src/fe_utils/archive.c
index 361c1c25ea..90f2e5bb6a 100644
--- a/src/fe_utils/archive.c
+++ b/src/fe_utils/archive.c
@@ -49,10 +49,7 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
     xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
                                          xlogfname, NULL);
     if (xlogRestoreCmd == NULL)
-    {
         pg_log_fatal("cannot use restore_command with %%r placeholder");
-        exit(1);
-    }

     /*
      * Execute restore_command, which should copy the missing file from
@@ -70,22 +67,16 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         if (stat(xlogpath, &stat_buf) == 0)
         {
             if (expectedSize > 0 && stat_buf.st_size != expectedSize)
-            {
                 pg_log_fatal("unexpected file size for \"%s\": %lld instead of %lld",
                              xlogfname, (long long int) stat_buf.st_size,
                              (long long int) expectedSize);
-                exit(1);
-            }
             else
             {
                 int            xlogfd = open(xlogpath, O_RDONLY | PG_BINARY, 0);

                 if (xlogfd < 0)
-                {
                     pg_log_fatal("could not open file \"%s\" restored from archive: %m",
                                  xlogpath);
-                    exit(1);
-                }
                 else
                     return xlogfd;
             }
@@ -93,11 +84,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         else
         {
             if (errno != ENOENT)
-            {
                 pg_log_fatal("could not stat file \"%s\": %m",
                              xlogpath);
-                exit(1);
-            }
         }
     }

@@ -108,11 +96,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
      * fatal too.
      */
     if (wait_result_is_any_signal(rc, true))
-    {
         pg_log_fatal("restore_command failed: %s",
                      wait_result_to_str(rc));
-        exit(1);
-    }

     /*
      * The file is not available, so just let the caller decide what to do
diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
index a30c66f13a..f95d2d5987 100644
--- a/src/fe_utils/connect_utils.c
+++ b/src/fe_utils/connect_utils.c
@@ -88,11 +88,8 @@ connectDatabase(const ConnParams *cparams, const char *progname,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s: out of memory",
+            pg_log_fatal("could not connect to database %s: out of memory",
                          cparams->dbname);
-            exit(1);
-        }

         /*
          * No luck?  Trying asking (again) for a password.
@@ -117,8 +114,7 @@ connectDatabase(const ConnParams *cparams, const char *progname,
             PQfinish(conn);
             return NULL;
         }
-        pg_log_error("%s", PQerrorMessage(conn));
-        exit(1);
+        pg_log_fatal("%s", PQerrorMessage(conn));
     }

     /* Start strict; callers may override this. */
diff --git a/src/fe_utils/parallel_slot.c b/src/fe_utils/parallel_slot.c
index 5896a8a6ca..987a4350fe 100644
--- a/src/fe_utils/parallel_slot.c
+++ b/src/fe_utils/parallel_slot.c
@@ -298,10 +298,7 @@ connect_slot(ParallelSlotArray *sa, int slotno, const char *dbname)
     sa->cparams->override_dbname = old_override;

     if (PQsocket(slot->connection) >= FD_SETSIZE)
-    {
         pg_log_fatal("too many jobs for this platform");
-        exit(1);
-    }

     /* Setup the connection using the supplied command, if any. */
     if (sa->initcmd)
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c
index 0b31b33f17..2fc6e2405b 100644
--- a/src/fe_utils/query_utils.c
+++ b/src/fe_utils/query_utils.c
@@ -31,7 +31,7 @@ executeQuery(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
@@ -56,7 +56,7 @@ executeCommand(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
index 9407e76bba..265fe166da 100644
--- a/src/fe_utils/recovery_gen.c
+++ b/src/fe_utils/recovery_gen.c
@@ -31,10 +31,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     contents = createPQExpBuffer();
     if (!contents)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");

     /*
      * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
@@ -45,10 +42,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     connOptions = PQconninfo(pgconn);
     if (connOptions == NULL)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");

     initPQExpBuffer(&conninfo_buf);
     for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
@@ -73,10 +67,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
         appendConnStrVal(&conninfo_buf, opt->val);
     }
     if (PQExpBufferDataBroken(conninfo_buf))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");

     /*
      * Escape the connection string, so that it can be put in the config file.
@@ -96,10 +87,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
     }

     if (PQExpBufferBroken(contents))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");

     PQconninfoFree(connOptions);

@@ -130,16 +118,10 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)

     cf = fopen(filename, use_recovery_conf ? "w" : "a");
     if (cf == NULL)
-    {
-        pg_log_error("could not open file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\": %m", filename);

     if (fwrite(contents->data, contents->len, 1, cf) != 1)
-    {
-        pg_log_error("could not write to file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_log_fatal("could not write to file \"%s\": %m", filename);

     fclose(cf);

@@ -148,10 +130,7 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
         snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
         cf = fopen(filename, "w");
         if (cf == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", filename);
-            exit(1);
-        }
+            pg_log_fatal("could not create file \"%s\": %m", filename);

         fclose(cf);
     }
@@ -167,9 +146,6 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");
     return result;
 }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index 43cc79afa8..74a315837a 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
 enum pg_log_level
 {
     /*
-     * Not initialized yet
+     * Not initialized yet (not to be used as an actual message log level).
      */
     PG_LOG_NOTSET = 0,

@@ -43,20 +43,42 @@ enum pg_log_level
     PG_LOG_ERROR,

     /*
-     * Severe errors that cause program termination.  (One-shot programs may
-     * chose to label even fatal errors as merely "errors".  The distinction
-     * is up to the program.)
-     */
-    PG_LOG_FATAL,
-
-    /*
-     * Turn all logging off.
+     * Turn all logging off (not to be used as an actual message log level).
      */
     PG_LOG_OFF,
 };

+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
 extern enum pg_log_level __pg_log_level;

+/*
+ * A log message can have several parts.  The primary message is required,
+ * others are optional.  When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+    /*
+     * The primary message.  Try to keep it to one line; follow the backend's
+     * style guideline for primary messages.
+     */
+    PG_LOG_PRIMARY,
+
+    /*
+     * Additional detail.  Follow the backend's style guideline for detail
+     * messages.
+     */
+    PG_LOG_DETAIL,
+
+    /*
+     * Hint (not guaranteed correct) about how to fix the problem.  Follow the
+     * backend's style guideline for hint messages.
+     */
+    PG_LOG_HINT,
+};
+
 /*
  * Kind of a hack to be able to produce the psql output exactly as required by
  * the regression tests.
@@ -70,27 +92,85 @@ void        pg_logging_increase_verbosity(void);
 void        pg_logging_set_pre_callback(void (*cb) (void));
 void        pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));

-void        pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void        pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2,
0);
+void        pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+                           const char *pg_restrict fmt,...)
+            pg_attribute_printf(3, 4);
+void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                             const char *pg_restrict fmt, va_list ap)
+            pg_attribute_printf(3, 0);

-#define pg_log_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

-#define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_error_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
-        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_detail(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_hint(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+    } while(0)
+
+/*
+ * A common special case: pg_log_error() and immediately exit(1).  There is
+ * no situation where it makes sense to suppress the message, so we ignore
+ * __pg_log_level.
+ */
+#define pg_log_fatal(...) do { \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit(1); \
     } while(0)

 #endif                            /* COMMON_LOGGING_H */

Re: Frontend error logging style

От
Andres Freund
Дата:
Hi,

On 2022-02-22 18:23:19 -0500, Tom Lane wrote:
> Robert Haas <robertmhaas@gmail.com> writes:
> > On Fri, Nov 19, 2021 at 5:17 AM Peter Eisentraut
> > <peter.eisentraut@enterprisedb.com> wrote:
> >> If people want to do that kind of thing (I'm undecided whether the
> >> complexity is worth it), then make it a different API.  The pg_log_*
> >> calls are for writing formatted output.  They normalized existing
> >> hand-coded patterns at the time.  We can wrap another API on top of them
> >> that does flow control and output.  The pg_log_* stuff is more on the
> >> level of syslog(), which also just outputs stuff.  Nobody is suggesting
> >> that syslog(LOG_EMERG) should exit the program automatically.  But you
> >> can wrap higher-level APIs such as ereport() on top of that that might
> >> do that.

That'd maybe be a convincing argument in a project that didn't have
elog(FATAL).


> > Yeah, that might be a way forward.
> 
> This conversation seems to have tailed off without full resolution,
> but I observe that pretty much everyone except Peter is on board
> with defining pg_log_fatal as pg_log_error + exit(1).  So I think
> we should just do that, unless Peter wants to produce a finished
> alternative proposal.

What about adding a pg_fatal() that's pg_log_fatal() + exit()? That keeps
pg_log_* stuff "log only", but adds something adjacent enough to hopefully
reduce future misunderstandings?

To further decrease the chance of such mistakes, we could rename
pg_log_fatal() to something else. Or add a a pg_log_fatal() function that's
marked pg_nodiscard(), so that each callsite explicitly has to be (void)
pg_log_fatal().


> I've now gone through and fleshed out the patch I sketched upthread.
> This exercise convinced me that we absolutely should do something
> very like this, because
> 
> (1) As it stands, this patch removes a net of just about 1000 lines
> of code.  So we clearly need *something*.

> (2) The savings would be even more, except that people have invented
> macros for pg_log_error + exit(1) in at least four places already.
> (None of them quite the same of course.)  Here I've just fixed those
> macro definitions to use pg_log_fatal.  In the name of consistency, we
> should probably get rid of those macros in favor of using pg_log_fatal
> directly; but I've not done so here, since this patch is eyewateringly
> long and boring already.

Agreed.


I don't care that much about the naming here.


For a moment I was wondering whether there'd be a benefit for having the "pure
logging" stuff be in a different static library than the erroring variant. So
that e.g. libpq's check for exit() doesn't run into trouble. But then I
remembered that libpq doesn't link against common...

Greetings,

Andres Freund



Re: Frontend error logging style

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> What about adding a pg_fatal() that's pg_log_fatal() + exit()? That keeps
> pg_log_* stuff "log only", but adds something adjacent enough to hopefully
> reduce future misunderstandings?

I'd be okay with that, except that pg_upgrade already has a pg_fatal
(because it has its *own* logging system, just in case you thought
this wasn't enough of a mess yet).  I'm in favor of aligning
pg_upgrade's logging with the rest, but I'd hoped to leave that for
later.  Making the names collide would be bad even as a short-term
thing, I fear.

Looks like libpq_pipeline.c has its own pg_fatal, too.

I'm not against choosing some name other than pg_log_fatal, but that
particular suggestion has got conflicts.  Got any other ideas?

            regards, tom lane



Re: Frontend error logging style

От
Andres Freund
Дата:
On 2022-02-22 22:44:25 -0500, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > What about adding a pg_fatal() that's pg_log_fatal() + exit()? That keeps
> > pg_log_* stuff "log only", but adds something adjacent enough to hopefully
> > reduce future misunderstandings?
> 
> I'd be okay with that, except that pg_upgrade already has a pg_fatal
> (because it has its *own* logging system, just in case you thought
> this wasn't enough of a mess yet).  I'm in favor of aligning
> pg_upgrade's logging with the rest, but I'd hoped to leave that for
> later.  Making the names collide would be bad even as a short-term
> thing, I fear.

I guess we could name pg_upgrade's out of the way...


> I'm not against choosing some name other than pg_log_fatal, but that
> particular suggestion has got conflicts.  Got any other ideas?

Maybe pg_fatal_exit(), pg_exit_fatal() or pg_fatal_error()?



Re: Frontend error logging style

От
Tom Lane
Дата:
I wrote:
> Andres Freund <andres@anarazel.de> writes:
>> What about adding a pg_fatal() that's pg_log_fatal() + exit()? That keeps
>> pg_log_* stuff "log only", but adds something adjacent enough to hopefully
>> reduce future misunderstandings?

> I'd be okay with that, except that pg_upgrade already has a pg_fatal
> (because it has its *own* logging system, just in case you thought
> this wasn't enough of a mess yet).

Wait a moment.  After looking closer, I realize that pg_upgrade's
pg_fatal could trivially be turned into a macro; and the other two
existing definitions already are macros.  That would remove the risk
of link-time symbol collisions that I was worried about.  As a bonus,
it'd substantially reduce the number of changes needed to make
pg_upgrade use logging.c, whenever somebody wants to make that happen.

So I now propose modifying yesterday's patch thus:

* Reinstantiate the PG_LOG_FATAL enum value, add support macros
pg_log_fatal, pg_log_fatal_hint, pg_log_fatal_detail.

* Define pg_fatal as pg_log_fatal + exit(1).  (This would essentially
move pg_rewind's definition into logging.h.  pg_upgrade will
define it slightly differently, but the semantics end up the same.)

* Adjust call sites to match.

I do like this idea because it would not break any existing code
that expects pg_log_fatal to return.  There is likely to be some
of that in outstanding patches, and this approach would merely
render it less-than-idiomatic rather than outright broken.

Updating the patch is going to be a bit tedious, so I'm not
going to do it without buy-in that this solution would be
okay to commit.  Any objections?

            regards, tom lane



Re: Frontend error logging style

От
Andres Freund
Дата:
Hi,

On 2022-02-23 10:30:02 -0500, Tom Lane wrote:
> So I now propose modifying yesterday's patch thus:
> 
> * Reinstantiate the PG_LOG_FATAL enum value, add support macros
> pg_log_fatal, pg_log_fatal_hint, pg_log_fatal_detail.
> 
> * Define pg_fatal as pg_log_fatal + exit(1).  (This would essentially
> move pg_rewind's definition into logging.h.  pg_upgrade will
> define it slightly differently, but the semantics end up the same.)
> 
> * Adjust call sites to match.

+1


> I do like this idea because it would not break any existing code
> that expects pg_log_fatal to return.  There is likely to be some
> of that in outstanding patches, and this approach would merely
> render it less-than-idiomatic rather than outright broken.
> 
> Updating the patch is going to be a bit tedious, so I'm not
> going to do it without buy-in that this solution would be
> okay to commit.  Any objections?

+1

Greetings,

Andres Freund



Re: Frontend error logging style

От
"Euler Taveira"
Дата:
On Wed, Feb 23, 2022, at 12:30 PM, Tom Lane wrote:
Updating the patch is going to be a bit tedious, so I'm not
going to do it without buy-in that this solution would be
okay to commit.  Any objections?
No. I was confused by the multiple log functions spread in the client programs
while I was working on pg_subscriber [1]. Your proposal looks good to me.



--
Euler Taveira

Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 23.02.22 00:23, Tom Lane wrote:
> This conversation seems to have tailed off without full resolution,
> but I observe that pretty much everyone except Peter is on board
> with defining pg_log_fatal as pg_log_error + exit(1).  So I think
> we should just do that, unless Peter wants to produce a finished
> alternative proposal.
> 
> I've now gone through and fleshed out the patch I sketched upthread.

This patch already illustrates a couple of things that are wrong with 
this approach:

- It doesn't allow any other way of exiting.  For example, in pg_dump, 
you have removed a few exit_nicely() calls.  It's not clear why that is 
valid or whether it would always be valid for all call sites.

- It doesn't allow other exit codes.  For example, in psql, you have 
changed a pg_log_fatal() call to pg_log_error() because it needed 
another exit code.  This slides us right back into that annoying 
situation where in the backend we have to log error messages using 
elog(LOG) because the flow control is tangled up with the log level.

My suggestion is to just get rid of pg_log_fatal() and replace them all 
with pg_log_error().




Re: Frontend error logging style

От
Andres Freund
Дата:
Hi,

On 2022-02-24 14:06:18 +0100, Peter Eisentraut wrote:
> My suggestion is to just get rid of pg_log_fatal() and replace them all with
> pg_log_error().

-1. This ignores that already several places came up with their slightly
different versions of fatal exit handlers. We don't gain anything by not
standardizing on one notion of a fatal error wrapper.

Greetings,

Andres Freund



Re: Frontend error logging style

От
Tom Lane
Дата:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
> This patch already illustrates a couple of things that are wrong with 
> this approach:

All of these objections are a bit moot with my followup proposal, no?

> - It doesn't allow any other way of exiting.  For example, in pg_dump, 
> you have removed a few exit_nicely() calls.  It's not clear why that is 
> valid or whether it would always be valid for all call sites.

As the patch stood, I'd hacked it so that pg_log_fatal called
exit_nicely() not exit() within pg_dump.  I agree that's not a great
solution, but I think that the correct fix is to get rid of exit_nicely
in favor of doing the requisite cleanup in an atexit hook.  pg_dump
is already at great hazard of somebody calling exit() not exit_nicely(),
either through failure to pay attention to that undocumented convention,
or because some code it imports from someplace like src/common calls
exit() directly.  You need not look any further than pg_malloc()
to see that there are live problems there.

I figured this could be addressed in a separate patch, though,
because to the extent that missing the exit-nicely callbacks is
a real bug, it's already a bug.

> My suggestion is to just get rid of pg_log_fatal() and replace them all 
> with pg_log_error().

I'm on board with dropping the separate FATAL log level if there's
consensus to do so; I think it adds more confusion than anything else.
I still want to have something like pg_fatal(), though, because it's
impossible to deny the usefulness of such an abbreviation.  It's already
been reinvented three or four times independently.

Independently of either of those points, I still want to make the changes
I proposed w.r.t. making explicit concepts of "detail" and "hint"
addendums.  What we have right now in the frontend code is an impossible
mishmash of three or four ways of filling that lack, all inconsistent.

            regards, tom lane



Re: Frontend error logging style

От
Tom Lane
Дата:
I wrote:
> I'm on board with dropping the separate FATAL log level if there's
> consensus to do so; I think it adds more confusion than anything else.

I feel that the reasonable alternatives are either to drop the FATAL
log level, or try to make it actually mean something by consistently
using it for errors that are indeed fatal.  I had a go at doing the
latter, but eventually concluded that that way madness lies.  It's
not too hard to use "pg_log_fatal" when there's an exit(1) right
after it, but there are quite a lot of cases where a subroutine
reports an error and returns a failure code to its caller, whereupon
the caller exits.  Either the subroutine has to make an unwarranted
assumption about what its callers will do, or we need to make an API
change to allow the subroutine itself to exit(), or we are going to
present a user experience that is inconsistently different depending
on internal implementation details.

Just to add insult to injury, once I'd gotten done with the easy
part (use pg_log_fatal where there's an adjacent exit()), a whole
lot of TAP tests fell over, because they were expecting "error:"
not "fatal:".  The reverse direction of s/fatal/error/g only breaks
one TAP test, which says a lot about how many places are troubling
to make this distinction now.

I conclude that we ought to drop the separate FATAL level and just
use ERROR instead.

The attached revision does that, standardizes on pg_fatal() as the
abbreviation for pg_log_error() + exit(1), and invents detail/hint
features as per previous discussion.

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 65cce49993..a62a5eedb1 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -182,16 +182,17 @@ get_opts(int argc, char **argv, struct options *my_opts)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (optind < argc)
     {
-        fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
-                progname, argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error("too many command-line arguments (first is \"%s\")",
+                     argv[optind]);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
 }
@@ -328,11 +329,8 @@ sql_conn(struct options *my_opts)
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s",
-                         my_opts->dbname);
-            exit(1);
-        }
+            pg_fatal("could not connect to database %s",
+                     my_opts->dbname);

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -359,7 +357,7 @@ sql_conn(struct options *my_opts)
                      PQerrorMessage(conn));
         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }
     PQclear(res);

@@ -390,11 +388,11 @@ sql_exec(PGconn *conn, const char *todo, bool quiet)
     if (!res || PQresultStatus(res) > 2)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", todo);
+        pg_log_error_detail("Query was: %s", todo);

         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }

     /* get the number of fields */
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index d15edca454..b7c8f2c805 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -492,19 +492,13 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
             case 'h':
                 param.pg_host = pg_strdup(optarg);
                 break;
             case 'l':
                 param.transaction_limit = strtol(optarg, NULL, 10);
                 if (param.transaction_limit < 0)
-                {
-                    pg_log_error("transaction limit must not be negative (0 disables)");
-                    exit(1);
-                }
+                    pg_fatal("transaction limit must not be negative (0 disables)");
                 break;
             case 'n':
                 param.dry_run = 1;
@@ -513,10 +507,7 @@ main(int argc, char **argv)
             case 'p':
                 port = strtol(optarg, NULL, 10);
                 if ((port < 1) || (port > 65535))
-                {
-                    pg_log_error("invalid port number: %s", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid port number: %s", optarg);
                 param.pg_port = pg_strdup(optarg);
                 break;
             case 'U':
@@ -532,7 +523,8 @@ main(int argc, char **argv)
                 param.pg_prompt = TRI_YES;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -541,7 +533,7 @@ main(int argc, char **argv)
     if (optind >= argc)
     {
         pg_log_error("missing required argument: database name");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 97f15971e2..4dea2ccd52 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -329,10 +329,7 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");
     return result;
 }

@@ -462,10 +459,7 @@ readfile(const char *path)
     int            n;

     if ((infile = fopen(path, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m", path);

     initStringInfo(&line);

@@ -506,24 +500,15 @@ writefile(char *path, char **lines)
     char      **line;

     if ((out_file = fopen(path, "w")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     for (line = lines; *line != NULL; line++)
     {
         if (fputs(*line, out_file) < 0)
-        {
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
-        }
+            pg_fatal("could not write file \"%s\": %m", path);
         free(*line);
     }
     if (fclose(out_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
 }

 /*
@@ -609,9 +594,7 @@ get_id(void)
     if (geteuid() == 0)            /* 0 is root's uid */
     {
         pg_log_error("cannot be run as root");
-        fprintf(stderr,
-                _("Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n"
-                  "own the server process.\n"));
+        pg_log_error_hint("Please log in (using, e.g., \"su\") as the (unprivileged) user that will own the server
process.");
         exit(1);
     }
 #endif
@@ -643,9 +626,8 @@ get_encoding_id(const char *encoding_name)
         if ((enc = pg_valid_server_encoding(encoding_name)) >= 0)
             return enc;
     }
-    pg_log_error("\"%s\" is not a valid server encoding name",
-                 encoding_name ? encoding_name : "(null)");
-    exit(1);
+    pg_fatal("\"%s\" is not a valid server encoding name",
+             encoding_name ? encoding_name : "(null)");
 }

 /*
@@ -789,25 +771,19 @@ check_input(char *path)
         if (errno == ENOENT)
         {
             pg_log_error("file \"%s\" does not exist", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         else
         {
             pg_log_error("could not access file \"%s\": %m", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         exit(1);
     }
     if (!S_ISREG(statbuf.st_mode))
     {
         pg_log_error("file \"%s\" is not a regular file", path);
-        fprintf(stderr,
-                _("This might mean you have a corrupted installation or identified\n"
-                  "the wrong directory with the invocation option -L.\n"));
+        pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with
theinvocation option -L."); 
         exit(1);
     }
 }
@@ -828,16 +804,10 @@ write_version_file(const char *extrapath)
         path = psprintf("%s/%s/PG_VERSION", pg_data, extrapath);

     if ((version_file = fopen(path, PG_BINARY_W)) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     if (fprintf(version_file, "%s\n", PG_MAJORVERSION) < 0 ||
         fclose(version_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -854,15 +824,9 @@ set_null_conf(void)
     path = psprintf("%s/postgresql.conf", pg_data);
     conf_file = fopen(path, PG_BINARY_W);
     if (conf_file == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     if (fclose(conf_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -1216,10 +1180,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     /*
      * create the automatic configuration file to store the configuration
@@ -1235,10 +1196,7 @@ setup_config(void)

     writefile(path, autoconflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1321,10 +1279,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1336,10 +1291,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1373,9 +1325,8 @@ bootstrap_template1(void)
     {
         pg_log_error("input file \"%s\" does not belong to PostgreSQL %s",
                      bki_file, PG_VERSION);
-        fprintf(stderr,
-                _("Check your installation or specify the correct path "
-                  "using the option -L.\n"));
+        pg_log_error_hint("Check your installation or specify the correct path "
+                          "using the option -L.");
         exit(1);
     }

@@ -1495,21 +1446,17 @@ get_su_pwd(void)
         FILE       *pwf = fopen(pwfilename, "r");

         if (!pwf)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
-                         pwfilename);
-            exit(1);
-        }
+            pg_fatal("could not open file \"%s\" for reading: %m",
+                     pwfilename);
         pwd1 = pg_get_line(pwf, NULL);
         if (!pwd1)
         {
             if (ferror(pwf))
-                pg_log_error("could not read password from file \"%s\": %m",
-                             pwfilename);
+                pg_fatal("could not read password from file \"%s\": %m",
+                         pwfilename);
             else
-                pg_log_error("password file \"%s\" is empty",
-                             pwfilename);
-            exit(1);
+                pg_fatal("password file \"%s\" is empty",
+                         pwfilename);
         }
         fclose(pwf);

@@ -2049,10 +1996,7 @@ check_locale_name(int category, const char *locale, char **canonname)

     save = setlocale(category, NULL);
     if (!save)
-    {
-        pg_log_error("setlocale() failed");
-        exit(1);
-    }
+        pg_fatal("setlocale() failed");

     /* save may be pointing at a modifiable scratch variable, so copy it. */
     save = pg_strdup(save);
@@ -2070,17 +2014,14 @@ check_locale_name(int category, const char *locale, char **canonname)

     /* restore old value. */
     if (!setlocale(category, save))
-    {
-        pg_log_error("failed to restore old locale \"%s\"", save);
-        exit(1);
-    }
+        pg_fatal("failed to restore old locale \"%s\"", save);
     free(save);

     /* complain if locale wasn't valid */
     if (res == NULL)
     {
         if (*locale)
-            pg_log_error("invalid locale name \"%s\"", locale);
+            pg_fatal("invalid locale name \"%s\"", locale);
         else
         {
             /*
@@ -2091,9 +2032,8 @@ check_locale_name(int category, const char *locale, char **canonname)
              * setlocale's behavior is implementation-specific, it's hard to
              * be sure what it didn't like.  Print a safe generic message.
              */
-            pg_log_error("invalid locale settings; check LANG and LC_* environment variables");
+            pg_fatal("invalid locale settings; check LANG and LC_* environment variables");
         }
-        exit(1);
     }
 }

@@ -2119,15 +2059,14 @@ check_locale_encoding(const char *locale, int user_enc)
           user_enc == PG_SQL_ASCII))
     {
         pg_log_error("encoding mismatch");
-        fprintf(stderr,
-                _("The encoding you selected (%s) and the encoding that the\n"
-                  "selected locale uses (%s) do not match.  This would lead to\n"
-                  "misbehavior in various character string processing functions.\n"
-                  "Rerun %s and either do not specify an encoding explicitly,\n"
-                  "or choose a matching combination.\n"),
-                pg_encoding_to_char(user_enc),
-                pg_encoding_to_char(locale_enc),
-                progname);
+        pg_log_error_detail("The encoding you selected (%s) and the encoding that the "
+                            "selected locale uses (%s) do not match. This would lead to "
+                            "misbehavior in various character string processing functions.",
+                            pg_encoding_to_char(user_enc),
+                            pg_encoding_to_char(locale_enc));
+        pg_log_error_hint("Rerun %s and either do not specify an encoding explicitly, "
+                          "or choose a matching combination.",
+                          progname);
         return false;
     }
     return true;
@@ -2259,9 +2198,8 @@ check_authmethod_valid(const char *authmethod, const char *const *valid_methods,
                 return;
     }

-    pg_log_error("invalid authentication method \"%s\" for \"%s\" connections",
-                 authmethod, conntype);
-    exit(1);
+    pg_fatal("invalid authentication method \"%s\" for \"%s\" connections",
+             authmethod, conntype);
 }

 static void
@@ -2274,10 +2212,7 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
          strcmp(authmethodhost, "password") == 0 ||
          strcmp(authmethodhost, "scram-sha-256") == 0) &&
         !(pwprompt || pwfilename))
-    {
-        pg_log_error("must specify a password for the superuser to enable password authentication");
-        exit(1);
-    }
+        pg_fatal("must specify a password for the superuser to enable password authentication");
 }


@@ -2297,10 +2232,9 @@ setup_pgdata(void)
         else
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr,
-                    _("You must identify the directory where the data for this database system\n"
-                      "will reside.  Do this with either the invocation option -D or the\n"
-                      "environment variable PGDATA.\n"));
+            pg_log_error_hint("You must identify the directory where the data for this database system "
+                              "will reside.  Do this with either the invocation option -D or the "
+                              "environment variable PGDATA.");
             exit(1);
         }
     }
@@ -2315,10 +2249,7 @@ setup_pgdata(void)
      * have embedded spaces.
      */
     if (setenv("PGDATA", pg_data, 1) != 0)
-    {
-        pg_log_error("could not set environment");
-        exit(1);
-    }
+        pg_fatal("could not set environment");
 }


@@ -2336,15 +2267,13 @@ setup_bin_paths(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" was found by \"%s\" "
+                         "but was not the same version as %s",
                          "postgres", full_path, progname);
+        pg_log_error_hint("Check your installation.");
         exit(1);
     }

@@ -2359,10 +2288,7 @@ setup_bin_paths(const char *argv0)
         get_share_path(backend_exec, share_path);
     }
     else if (!is_absolute_path(share_path))
-    {
-        pg_log_error("input file location must be an absolute path");
-        exit(1);
-    }
+        pg_fatal("input file location must be an absolute path");

     canonicalize_path(share_path);
 }
@@ -2406,9 +2332,8 @@ setup_locale_encoding(void)
             /* Couldn't recognize the locale's codeset */
             pg_log_error("could not find suitable encoding for locale \"%s\"",
                          lc_ctype);
-            fprintf(stderr, _("Rerun %s with the -E option.\n"), progname);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Rerun %s with the -E option.", progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         else if (!pg_valid_server_encoding_id(ctype_enc))
@@ -2427,10 +2352,10 @@ setup_locale_encoding(void)
 #else
             pg_log_error("locale \"%s\" requires unsupported encoding \"%s\"",
                          lc_ctype, pg_encoding_to_char(ctype_enc));
-            fprintf(stderr,
-                    _("Encoding \"%s\" is not allowed as a server-side encoding.\n"
-                      "Rerun %s with a different locale selection.\n"),
-                    pg_encoding_to_char(ctype_enc), progname);
+            pg_log_error_detail("Encoding \"%s\" is not allowed as a server-side encoding.",
+                                pg_encoding_to_char(ctype_enc));
+            pg_log_error_hint("Rerun %s with a different locale selection.",
+                              progname);
             exit(1);
 #endif
         }
@@ -2573,10 +2498,7 @@ create_data_directory(void)
             fflush(stdout);

             if (pg_mkdir_p(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not create directory \"%s\": %m", pg_data);
-                exit(1);
-            }
+                pg_fatal("could not create directory \"%s\": %m", pg_data);
             else
                 check_ok();

@@ -2590,11 +2512,8 @@ create_data_directory(void)
             fflush(stdout);

             if (chmod(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not change permissions of directory \"%s\": %m",
-                             pg_data);
-                exit(1);
-            }
+                pg_fatal("could not change permissions of directory \"%s\": %m",
+                         pg_data);
             else
                 check_ok();

@@ -2609,17 +2528,15 @@ create_data_directory(void)
             if (ret != 4)
                 warn_on_mount_point(ret);
             else
-                fprintf(stderr,
-                        _("If you want to create a new database system, either remove or empty\n"
-                          "the directory \"%s\" or run %s\n"
-                          "with an argument other than \"%s\".\n"),
-                        pg_data, progname, pg_data);
+                pg_log_error_hint("If you want to create a new database system, either remove or empty "
+                                  "the directory \"%s\" or run %s "
+                                  "with an argument other than \"%s\".",
+                                  pg_data, progname, pg_data);
             exit(1);            /* no further message needed */

         default:
             /* Trouble accessing directory */
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
+            pg_fatal("could not access directory \"%s\": %m", pg_data);
     }
 }

@@ -2640,10 +2557,7 @@ create_xlog_or_symlink(void)
         /* clean up xlog directory name, check it's absolute */
         canonicalize_path(xlog_dir);
         if (!is_absolute_path(xlog_dir))
-        {
-            pg_log_error("WAL directory location must be an absolute path");
-            exit(1);
-        }
+            pg_fatal("WAL directory location must be an absolute path");

         /* check if the specified xlog directory exists/is empty */
         switch ((ret = pg_check_dir(xlog_dir)))
@@ -2655,11 +2569,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (pg_mkdir_p(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not create directory \"%s\": %m",
-                                 xlog_dir);
-                    exit(1);
-                }
+                    pg_fatal("could not create directory \"%s\": %m",
+                             xlog_dir);
                 else
                     check_ok();

@@ -2673,11 +2584,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (chmod(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not change permissions of directory \"%s\": %m",
-                                 xlog_dir);
-                    exit(1);
-                }
+                    pg_fatal("could not change permissions of directory \"%s\": %m",
+                             xlog_dir);
                 else
                     check_ok();

@@ -2692,39 +2600,29 @@ create_xlog_or_symlink(void)
                 if (ret != 4)
                     warn_on_mount_point(ret);
                 else
-                    fprintf(stderr,
-                            _("If you want to store the WAL there, either remove or empty the directory\n"
-                              "\"%s\".\n"),
-                            xlog_dir);
+                    pg_log_error_hint("If you want to store the WAL there, either remove or empty the directory
\"%s\".",
+                                      xlog_dir);
                 exit(1);

             default:
                 /* Trouble accessing directory */
-                pg_log_error("could not access directory \"%s\": %m", xlog_dir);
-                exit(1);
+                pg_fatal("could not access directory \"%s\": %m", xlog_dir);
         }

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, subdirloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m",
-                         subdirloc);
-            exit(1);
-        }
+            pg_fatal("could not create symbolic link \"%s\": %m",
+                     subdirloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_fatal("symlinks are not supported on this platform");
 #endif
     }
     else
     {
         /* Without -X option, just make the subdirectory normally */
         if (mkdir(subdirloc, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m",
-                         subdirloc);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m",
+                     subdirloc);
     }

     free(subdirloc);
@@ -2735,15 +2633,12 @@ void
 warn_on_mount_point(int error)
 {
     if (error == 2)
-        fprintf(stderr,
-                _("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.");
     else if (error == 3)
-        fprintf(stderr,
-                _("It contains a lost+found directory, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a lost+found directory, perhaps due to it being a mount point.");

-    fprintf(stderr,
-            _("Using a mount point directly as the data directory is not recommended.\n"
-              "Create a subdirectory under the mount point.\n"));
+    pg_log_error_hint("Using a mount point directly as the data directory is not recommended.\n"
+                      "Create a subdirectory under the mount point.");
 }


@@ -2782,10 +2677,7 @@ initialize_data_directory(void)
          * pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
          */
         if (mkdir(path, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m", path);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m", path);

         free(path);
     }
@@ -3047,8 +2939,7 @@ main(int argc, char *argv[])
                 break;
             default:
                 /* getopt_long already emitted a complaint */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -3068,8 +2959,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -3082,10 +2972,7 @@ main(int argc, char *argv[])

         /* must check that directory is readable */
         if (pg_check_dir(pg_data) <= 0)
-        {
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
-        }
+            pg_fatal("could not access directory \"%s\": %m", pg_data);

         fputs(_("syncing data to disk ... "), stdout);
         fflush(stdout);
@@ -3095,10 +2982,7 @@ main(int argc, char *argv[])
     }

     if (pwprompt && pwfilename)
-    {
-        pg_log_error("password prompt and password file cannot be specified together");
-        exit(1);
-    }
+        pg_fatal("password prompt and password file cannot be specified together");

     check_authmethod_unspecified(&authmethodlocal);
     check_authmethod_unspecified(&authmethodhost);
@@ -3120,15 +3004,9 @@ main(int argc, char *argv[])

         /* verify that wal segment size is valid */
         if (endptr == str_wal_segment_size_mb || *endptr != '\0')
-        {
-            pg_log_error("argument of --wal-segsize must be a number");
-            exit(1);
-        }
+            pg_fatal("argument of --wal-segsize must be a number");
         if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
-        {
-            pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-            exit(1);
-        }
+            pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
     }

     get_restricted_token();
@@ -3142,10 +3020,7 @@ main(int argc, char *argv[])
         username = effective_user;

     if (strncmp(username, "pg_", 3) == 0)
-    {
-        pg_log_error("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
-        exit(1);
-    }
+        pg_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);

     printf(_("The files belonging to this database system will be owned "
              "by user \"%s\".\n"
@@ -3188,8 +3063,8 @@ main(int argc, char *argv[])
     {
         printf("\n");
         pg_log_warning("enabling \"trust\" authentication for local connections");
-        fprintf(stderr, _("You can change this by editing pg_hba.conf or using the option -A, or\n"
-                          "--auth-local and --auth-host, the next time you run initdb.\n"));
+        pg_log_warning_hint("You can change this by editing pg_hba.conf or using the option -A, or "
+                            "--auth-local and --auth-host, the next time you run initdb.");
     }

     if (!noinstructions)
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 6607f72938..90471e096d 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -202,9 +202,9 @@ static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,

 #define log_no_match(...) do { \
         if (opts.strict_names) \
-            pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+            pg_log_error(__VA_ARGS__); \
         else \
-            pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+            pg_log_warning(__VA_ARGS__); \
     } while(0)

 #define FREE_AND_SET_NULL(x) do { \
@@ -396,39 +396,24 @@ main(int argc, char *argv[])
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     opts.skip = "none";
                 else
-                {
-                    pg_log_error("invalid argument for option %s", "--skip");
-                    exit(1);
-                }
+                    pg_fatal("invalid argument for option %s", "--skip");
                 break;
             case 7:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid start block");
-                    exit(1);
-                }
+                    pg_fatal("invalid start block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("start block out of bounds");
-                    exit(1);
-                }
+                    pg_fatal("start block out of bounds");
                 opts.startblock = optval;
                 break;
             case 8:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid end block");
-                    exit(1);
-                }
+                    pg_fatal("invalid end block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("end block out of bounds");
-                    exit(1);
-                }
+                    pg_fatal("end block out of bounds");
                 opts.endblock = optval;
                 break;
             case 9:
@@ -450,18 +435,14 @@ main(int argc, char *argv[])
                     opts.install_schema = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr,
-                        _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (opts.endblock >= 0 && opts.endblock < opts.startblock)
-    {
-        pg_log_error("end block precedes start block");
-        exit(1);
-    }
+        pg_fatal("end block precedes start block");

     /*
      * A single non-option arguments specifies a database name or connection
@@ -477,7 +458,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -495,19 +476,13 @@ main(int argc, char *argv[])
     if (opts.alldb)
     {
         if (db != NULL)
-        {
-            pg_log_error("cannot specify a database name with --all");
-            exit(1);
-        }
+            pg_fatal("cannot specify a database name with --all");
         cparams.dbname = maintenance_db;
     }
     else if (db != NULL)
     {
         if (opts.dbpattern)
-        {
-            pg_log_error("cannot specify both a database name and database patterns");
-            exit(1);
-        }
+            pg_fatal("cannot specify both a database name and database patterns");
         cparams.dbname = db;
     }

@@ -535,7 +510,7 @@ main(int argc, char *argv[])
     {
         if (conn != NULL)
             disconnectDatabase(conn);
-        pg_log_error("no databases to check");
+        pg_log_warning("no databases to check");
         exit(0);
     }

@@ -593,7 +568,7 @@ main(int argc, char *argv[])
             /* Querying the catalog failed. */
             pg_log_error("database \"%s\": %s",
                          PQdb(conn), PQerrorMessage(conn));
-            pg_log_info("query was: %s", amcheck_sql);
+            pg_log_error_detail("Query was: %s", amcheck_sql);
             PQclear(result);
             disconnectDatabase(conn);
             exit(1);
@@ -669,8 +644,7 @@ main(int argc, char *argv[])
     {
         if (conn != NULL)
             disconnectDatabase(conn);
-        pg_log_error("no relations to check");
-        exit(1);
+        pg_fatal("no relations to check");
     }
     progress_report(reltotal, relprogress, pagestotal, pageschecked,
                     NULL, true, false);
@@ -919,7 +893,7 @@ run_command(ParallelSlot *slot, const char *sql)
         pg_log_error("error sending command to database \"%s\": %s",
                      PQdb(slot->connection),
                      PQerrorMessage(slot->connection));
-        pg_log_error("command was: %s", sql);
+        pg_log_error_detail("Command was: %s", sql);
         exit(1);
     }
 }
@@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
             pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
                            rel->datinfo->datname, rel->nspname, rel->relname, ntups);
             if (opts.verbose)
-                pg_log_info("query was: %s", rel->sql);
-            pg_log_warning("Are %s's and amcheck's versions compatible?",
-                           progname);
+                pg_log_warning_detail("Query was: %s", rel->sql);
+            pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
+                                progname);
             progress_since_last_stderr = false;
         }
     }
@@ -1648,7 +1622,7 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -1673,11 +1647,8 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
              */
             fatal = opts.strict_names;
             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected database pattern_id %d",
-                             pattern_id);
-                exit(1);
-            }
+                pg_fatal("internal error: received unexpected database pattern_id %d",
+                         pattern_id);
             log_no_match("no connectable databases to check matching \"%s\"",
                          opts.include.data[pattern_id].pattern);
         }
@@ -2096,7 +2067,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -2136,11 +2107,8 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
              */

             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected relation pattern_id %d",
-                             pattern_id);
-                exit(1);
-            }
+                pg_fatal("internal error: received unexpected relation pattern_id %d",
+                         pattern_id);

             opts.include.data[pattern_id].matched = true;
         }
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 6c3e7f4e01..064cbb222f 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -148,33 +148,21 @@ CleanupPriorWALFiles(void)

                 rc = unlink(WALFilePath);
                 if (rc != 0)
-                {
-                    pg_log_error("could not remove file \"%s\": %m",
-                                 WALFilePath);
-                    exit(1);
-                }
+                    pg_fatal("could not remove file \"%s\": %m",
+                             WALFilePath);
             }
         }

         if (errno)
-        {
-            pg_log_error("could not read archive location \"%s\": %m",
-                         archiveLocation);
-            exit(1);
-        }
+            pg_fatal("could not read archive location \"%s\": %m",
+                     archiveLocation);
         if (closedir(xldir))
-        {
-            pg_log_error("could not close archive location \"%s\": %m",
-                         archiveLocation);
-            exit(1);
-        }
-    }
-    else
-    {
-        pg_log_error("could not open archive location \"%s\": %m",
+            pg_fatal("could not close archive location \"%s\": %m",
                      archiveLocation);
-        exit(1);
     }
+    else
+        pg_fatal("could not open archive location \"%s\": %m",
+                 archiveLocation);
 }

 /*
@@ -247,7 +235,7 @@ SetWALFileNameForCleanup(void)
     if (!fnameOK)
     {
         pg_log_error("invalid file name argument");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }
 }
@@ -321,9 +309,9 @@ main(int argc, char **argv)
                                                      * from xlogfile names */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(2);
-                break;
         }
     }

@@ -342,7 +330,7 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify archive location");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

@@ -354,14 +342,14 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify oldest kept WAL file");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

     if (optind < argc)
     {
         pg_log_error("too many command-line arguments");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

diff --git a/src/bin/pg_basebackup/bbstreamer_file.c b/src/bin/pg_basebackup/bbstreamer_file.c
index d721f87891..393e9f340c 100644
--- a/src/bin/pg_basebackup/bbstreamer_file.c
+++ b/src/bin/pg_basebackup/bbstreamer_file.c
@@ -90,10 +90,7 @@ bbstreamer_plain_writer_new(char *pathname, FILE *file)
     {
         streamer->file = fopen(pathname, "wb");
         if (streamer->file == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", pathname);
-            exit(1);
-        }
+            pg_fatal("could not create file \"%s\": %m", pathname);
         streamer->should_close_file = true;
     }

@@ -121,9 +118,8 @@ bbstreamer_plain_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
+        pg_fatal("could not write to file \"%s\": %m",
+                 mystreamer->pathname);
     }
 }

@@ -139,11 +135,8 @@ bbstreamer_plain_writer_finalize(bbstreamer *streamer)
     mystreamer = (bbstreamer_plain_writer *) streamer;

     if (mystreamer->should_close_file && fclose(mystreamer->file) != 0)
-    {
-        pg_log_error("could not close file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
-    }
+        pg_fatal("could not close file \"%s\": %m",
+                 mystreamer->pathname);

     mystreamer->file = NULL;
     mystreamer->should_close_file = false;
@@ -262,9 +255,8 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
                 /* if write didn't set errno, assume problem is no disk space */
                 if (errno == 0)
                     errno = ENOSPC;
-                pg_log_error("could not write to file \"%s\": %m",
-                             mystreamer->filename);
-                exit(1);
+                pg_fatal("could not write to file \"%s\": %m",
+                         mystreamer->filename);
             }
             break;

@@ -280,8 +272,7 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while extracting archive");
-            exit(1);
+            pg_fatal("unexpected state while extracting archive");
     }
 }

@@ -304,20 +295,14 @@ extract_directory(const char *filename, mode_t mode)
                pg_str_endswith(filename, "/pg_xlog") ||
                pg_str_endswith(filename, "/archive_status")) &&
               errno == EEXIST))
-        {
-            pg_log_error("could not create directory \"%s\": %m",
-                         filename);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m",
+                     filename);
     }

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on directory \"%s\": %m",
-                     filename);
-        exit(1);
-    }
+        pg_fatal("could not set permissions on directory \"%s\": %m",
+                 filename);
 #endif
 }

@@ -335,11 +320,8 @@ static void
 extract_link(const char *filename, const char *linktarget)
 {
     if (symlink(linktarget, filename) != 0)
-    {
-        pg_log_error("could not create symbolic link from \"%s\" to \"%s\": %m",
-                     filename, linktarget);
-        exit(1);
-    }
+        pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
+                 filename, linktarget);
 }

 /*
@@ -354,18 +336,12 @@ create_file_for_extract(const char *filename, mode_t mode)

     file = fopen(filename, "wb");
     if (file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not create file \"%s\": %m", filename);

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on file \"%s\": %m",
-                     filename);
-        exit(1);
-    }
+        pg_fatal("could not set permissions on file \"%s\": %m",
+                 filename);
 #endif

     return file;
diff --git a/src/bin/pg_basebackup/bbstreamer_gzip.c b/src/bin/pg_basebackup/bbstreamer_gzip.c
index 894f857103..52fcde9ed8 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -91,42 +91,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file, int compresslevel)
     {
         streamer->gzfile = gzopen(pathname, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not create compressed file \"%s\": %m",
-                         pathname);
-            exit(1);
-        }
+            pg_fatal("could not create compressed file \"%s\": %m",
+                     pathname);
     }
     else
     {
         int            fd = dup(fileno(file));

         if (fd < 0)
-        {
-            pg_log_error("could not duplicate stdout: %m");
-            exit(1);
-        }
+            pg_fatal("could not duplicate stdout: %m");

         streamer->gzfile = gzdopen(fd, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not open output file: %m");
-            exit(1);
-        }
+            pg_fatal("could not open output file: %m");
     }

     if (gzsetparams(streamer->gzfile, compresslevel,
                     Z_DEFAULT_STRATEGY) != Z_OK)
-    {
-        pg_log_error("could not set compression level %d: %s",
-                     compresslevel, get_gz_error(streamer->gzfile));
-        exit(1);
-    }
+        pg_fatal("could not set compression level %d: %s",
+                 compresslevel, get_gz_error(streamer->gzfile));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

@@ -152,9 +139,8 @@ bbstreamer_gzip_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to compressed file \"%s\": %s",
-                     mystreamer->pathname, get_gz_error(mystreamer->gzfile));
-        exit(1);
+        pg_fatal("could not write to compressed file \"%s\": %s",
+                 mystreamer->pathname, get_gz_error(mystreamer->gzfile));
     }
 }

@@ -177,11 +163,8 @@ bbstreamer_gzip_writer_finalize(bbstreamer *streamer)

     errno = 0;                    /* in case gzclose() doesn't set it */
     if (gzclose(mystreamer->gzfile) != 0)
-    {
-        pg_log_error("could not close compressed file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
-    }
+        pg_fatal("could not close compressed file \"%s\": %m",
+                 mystreamer->pathname);

     mystreamer->gzfile = NULL;
 }
@@ -258,15 +241,11 @@ bbstreamer_gzip_decompressor_new(bbstreamer *next)
      * possible value for safety.
      */
     if (inflateInit2(zs, 15 + 16) != Z_OK)
-    {
-        pg_log_error("could not initialize compression library");
-        exit(1);
-    }
+        pg_fatal("could not initialize compression library");

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_inject.c b/src/bin/pg_basebackup/bbstreamer_inject.c
index 79c378d96e..cc804f1091 100644
--- a/src/bin/pg_basebackup/bbstreamer_inject.c
+++ b/src/bin/pg_basebackup/bbstreamer_inject.c
@@ -186,8 +186,7 @@ bbstreamer_recovery_injector_content(bbstreamer *streamer,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while injecting recovery settings");
-            exit(1);
+            pg_fatal("unexpected state while injecting recovery settings");
     }

     bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
diff --git a/src/bin/pg_basebackup/bbstreamer_lz4.c b/src/bin/pg_basebackup/bbstreamer_lz4.c
index f0bc226bf8..8416713994 100644
--- a/src/bin/pg_basebackup/bbstreamer_lz4.c
+++ b/src/bin/pg_basebackup/bbstreamer_lz4.c
@@ -104,13 +104,12 @@ bbstreamer_lz4_compressor_new(bbstreamer *next, int compresslevel)

     ctxError = LZ4F_createCompressionContext(&streamer->cctx, LZ4F_VERSION);
     if (LZ4F_isError(ctxError))
-            pg_log_error("could not create lz4 compression context: %s",
-                         LZ4F_getErrorName(ctxError));
+        pg_log_error("could not create lz4 compression context: %s",
+                     LZ4F_getErrorName(ctxError));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

@@ -296,16 +295,12 @@ bbstreamer_lz4_decompressor_new(bbstreamer *next)
     /* Initialize internal stream state for decompression */
     ctxError = LZ4F_createDecompressionContext(&streamer->dctx, LZ4F_VERSION);
     if (LZ4F_isError(ctxError))
-    {
-        pg_log_error("could not initialize compression library: %s",
-                LZ4F_getErrorName(ctxError));
-        exit(1);
-    }
+        pg_fatal("could not initialize compression library: %s",
+                 LZ4F_getErrorName(ctxError));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_tar.c b/src/bin/pg_basebackup/bbstreamer_tar.c
index 6ab981156e..fcbad579df 100644
--- a/src/bin/pg_basebackup/bbstreamer_tar.c
+++ b/src/bin/pg_basebackup/bbstreamer_tar.c
@@ -241,16 +241,12 @@ bbstreamer_tar_parser_content(bbstreamer *streamer, bbstreamer_member *member,
                  */
                 bbstreamer_buffer_bytes(streamer, &data, &len, len);
                 if (len > 2 * TAR_BLOCK_SIZE)
-                {
-                    pg_log_error("tar file trailer exceeds 2 blocks");
-                    exit(1);
-                }
+                    pg_fatal("tar file trailer exceeds 2 blocks");
                 return;

             default:
                 /* Shouldn't happen. */
-                pg_log_error("unexpected state while parsing tar archive");
-                exit(1);
+                pg_fatal("unexpected state while parsing tar archive");
         }
     }
 }
@@ -297,10 +293,7 @@ bbstreamer_tar_header(bbstreamer_tar_parser *mystreamer)
      */
     strlcpy(member->pathname, &buffer[0], MAXPGPATH);
     if (member->pathname[0] == '\0')
-    {
-        pg_log_error("tar member has empty name");
-        exit(1);
-    }
+        pg_fatal("tar member has empty name");
     member->size = read_tar_number(&buffer[124], 12);
     member->mode = read_tar_number(&buffer[100], 8);
     member->uid = read_tar_number(&buffer[108], 8);
@@ -332,10 +325,7 @@ bbstreamer_tar_parser_finalize(bbstreamer *streamer)
     if (mystreamer->next_context != BBSTREAMER_ARCHIVE_TRAILER &&
         (mystreamer->next_context != BBSTREAMER_MEMBER_HEADER ||
          mystreamer->base.bbs_buffer.len > 0))
-    {
-        pg_log_error("COPY stream ended before last file was finished");
-        exit(1);
-    }
+        pg_fatal("COPY stream ended before last file was finished");

     /* Send the archive trailer, even if empty. */
     bbstreamer_content(streamer->bbs_next, NULL,
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index c1ed7aeeee..b589b3bbb6 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -320,20 +320,14 @@ tablespace_list_append(const char *arg)
     for (arg_ptr = arg; *arg_ptr; arg_ptr++)
     {
         if (dst_ptr - dst >= MAXPGPATH)
-        {
-            pg_log_error("directory name too long");
-            exit(1);
-        }
+            pg_fatal("directory name too long");

         if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
             ;                    /* skip backslash escaping = */
         else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
         {
             if (*cell->new_dir)
-            {
-                pg_log_error("multiple \"=\" signs in tablespace mapping");
-                exit(1);
-            }
+                pg_fatal("multiple \"=\" signs in tablespace mapping");
             else
                 dst = dst_ptr = cell->new_dir;
         }
@@ -342,10 +336,7 @@ tablespace_list_append(const char *arg)
     }

     if (!*cell->old_dir || !*cell->new_dir)
-    {
-        pg_log_error("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
-        exit(1);
-    }
+        pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);

     /*
      * This check isn't absolutely necessary.  But all tablespaces are created
@@ -354,18 +345,12 @@ tablespace_list_append(const char *arg)
      * consistent with the new_dir check.
      */
     if (!is_absolute_path(cell->old_dir))
-    {
-        pg_log_error("old directory is not an absolute path in tablespace mapping: %s",
-                     cell->old_dir);
-        exit(1);
-    }
+        pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
+                 cell->old_dir);

     if (!is_absolute_path(cell->new_dir))
-    {
-        pg_log_error("new directory is not an absolute path in tablespace mapping: %s",
-                     cell->new_dir);
-        exit(1);
-    }
+        pg_fatal("new directory is not an absolute path in tablespace mapping: %s",
+                 cell->new_dir);

     /*
      * Comparisons done with these values should involve similarly
@@ -480,17 +465,11 @@ reached_end_position(XLogRecPtr segendpos, uint32 timeline,
             MemSet(xlogend, 0, sizeof(xlogend));
             r = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
             if (r < 0)
-            {
-                pg_log_error("could not read from ready pipe: %m");
-                exit(1);
-            }
+                pg_fatal("could not read from ready pipe: %m");

             if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-            {
-                pg_log_error("could not parse write-ahead log location \"%s\"",
-                             xlogend);
-                exit(1);
-            }
+                pg_fatal("could not parse write-ahead log location \"%s\"",
+                         xlogend);
             xlogendptr = ((uint64) hi) << 32 | lo;
             has_xlogendptr = 1;

@@ -641,11 +620,8 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)

     /* Convert the starting position */
     if (sscanf(startpos, "%X/%X", &hi, &lo) != 2)
-    {
-        pg_log_error("could not parse write-ahead log location \"%s\"",
-                     startpos);
-        exit(1);
-    }
+        pg_fatal("could not parse write-ahead log location \"%s\"",
+                 startpos);
     param->startptr = ((uint64) hi) << 32 | lo;
     /* Round off to even segment position */
     param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
@@ -653,10 +629,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #ifndef WIN32
     /* Create our background pipe */
     if (pipe(bgpipe) < 0)
-    {
-        pg_log_error("could not create pipe for background process: %m");
-        exit(1);
-    }
+        pg_fatal("could not create pipe for background process: %m");
 #endif

     /* Get a second connection */
@@ -711,10 +684,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
                  "pg_xlog" : "pg_wal");

         if (pg_mkdir_p(statusdir, pg_dir_create_mode) != 0 && errno != EEXIST)
-        {
-            pg_log_error("could not create directory \"%s\": %m", statusdir);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m", statusdir);
     }

     /*
@@ -737,10 +707,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
         exit(ret);
     }
     else if (bgchild < 0)
-    {
-        pg_log_error("could not create background process: %m");
-        exit(1);
-    }
+        pg_fatal("could not create background process: %m");

     /*
      * Else we are in the parent process and all is well.
@@ -749,10 +716,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #else                            /* WIN32 */
     bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
     if (bgchild == 0)
-    {
-        pg_log_error("could not create background thread: %m");
-        exit(1);
-    }
+        pg_fatal("could not create background thread: %m");
 #endif
 }

@@ -772,10 +736,7 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
              * Does not exist, so create
              */
             if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
-            {
-                pg_log_error("could not create directory \"%s\": %m", dirname);
-                exit(1);
-            }
+                pg_fatal("could not create directory \"%s\": %m", dirname);
             if (created)
                 *created = true;
             return;
@@ -794,15 +755,13 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
             /*
              * Exists, not empty
              */
-            pg_log_error("directory \"%s\" exists but is not empty", dirname);
-            exit(1);
+            pg_fatal("directory \"%s\" exists but is not empty", dirname);
         case -1:

             /*
              * Access problem
              */
-            pg_log_error("could not access directory \"%s\": %m", dirname);
-            exit(1);
+            pg_fatal("could not access directory \"%s\": %m", dirname);
     }
 }

@@ -931,23 +890,16 @@ parse_max_rate(char *src)
     errno = 0;
     result = strtod(src, &after_num);
     if (src == after_num)
-    {
-        pg_log_error("transfer rate \"%s\" is not a valid value", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" is not a valid value", src);
     if (errno != 0)
-    {
-        pg_log_error("invalid transfer rate \"%s\": %m", src);
-        exit(1);
-    }
+        pg_fatal("invalid transfer rate \"%s\": %m", src);

     if (result <= 0)
     {
         /*
          * Reject obviously wrong values here.
          */
-        pg_log_error("transfer rate must be greater than zero");
-        exit(1);
+        pg_fatal("transfer rate must be greater than zero");
     }

     /*
@@ -977,27 +929,18 @@ parse_max_rate(char *src)
         after_num++;

     if (*after_num != '\0')
-    {
-        pg_log_error("invalid --max-rate unit: \"%s\"", suffix);
-        exit(1);
-    }
+        pg_fatal("invalid --max-rate unit: \"%s\"", suffix);

     /* Valid integer? */
     if ((uint64) result != (uint64) ((uint32) result))
-    {
-        pg_log_error("transfer rate \"%s\" exceeds integer range", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" exceeds integer range", src);

     /*
      * The range is checked on the server side too, but avoid the server
      * connection if a nonsensical value was passed.
      */
     if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
-    {
-        pg_log_error("transfer rate \"%s\" is out of range", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" is out of range", src);

     return (int32) result;
 }
@@ -1104,10 +1047,7 @@ parse_compress_options(char *src, WalCompressionMethod *methodres,
     /* Check the contents after the colon separator. */
     sep++;
     if (*sep == '\0')
-    {
-        pg_log_error("no compression level defined for method %s", firstpart);
-        exit(1);
-    }
+        pg_fatal("no compression level defined for method %s", firstpart);

     /*
      * For any of the methods currently supported, the data after the
@@ -1133,11 +1073,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
     /* Get the COPY data stream. */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_COPY_OUT)
-    {
-        pg_log_error("could not get COPY data stream: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not get COPY data stream: %s",
+                 PQerrorMessage(conn));
     PQclear(res);

     /* Loop over chunks until done. */
@@ -1153,11 +1090,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
             break;
         }
         else if (r == -2)
-        {
-            pg_log_error("could not read COPY data: %s",
-                         PQerrorMessage(conn));
-            exit(1);
-        }
+            pg_fatal("could not read COPY data: %s",
+                     PQerrorMessage(conn));

         if (bgchild_exited)
         {
@@ -1226,13 +1160,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
     if (must_parse_archive && !is_tar && !is_tar_gz && !is_tar_lz4)
     {
         pg_log_error("unable to parse archive: %s", archive_name);
-        pg_log_info("only tar archives can be parsed");
+        pg_log_error_detail("Only tar archives can be parsed.");
         if (format == 'p')
-            pg_log_info("plain format requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
         if (inject_manifest)
-            pg_log_info("using - as the output directory requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
         if (writerecoveryconf)
-            pg_log_info("the -R option requires pg_basebackup to parse the archive");
+            pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
         exit(1);
     }

@@ -1437,10 +1371,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 /* Sanity check. */
                 if (state->manifest_buffer != NULL ||
                     state->manifest_file !=NULL)
-                {
-                    pg_log_error("archives should precede manifest");
-                    exit(1);
-                }
+                    pg_fatal("archives should precede manifest");

                 /* Parse the rest of the CopyData message. */
                 archive_name = GetCopyDataString(r, copybuf, &cursor);
@@ -1455,11 +1386,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 if (archive_name[0] == '\0' || archive_name[0] == '.' ||
                     strchr(archive_name, '/') != NULL ||
                     strchr(archive_name, '\\') != NULL)
-                {
-                    pg_log_error("invalid archive name: \"%s\"",
-                                 archive_name);
-                    exit(1);
-                }
+                    pg_fatal("invalid archive name: \"%s\"",
+                             archive_name);

                 /*
                  * An empty spclocation is treated as NULL. We expect this
@@ -1518,9 +1446,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                          */
                         if (errno == 0)
                             errno = ENOSPC;
-                        pg_log_error("could not write to file \"%s\": %m",
-                                     state->manifest_filename);
-                        exit(1);
+                        pg_fatal("could not write to file \"%s\": %m",
+                                 state->manifest_filename);
                     }
                 }
                 else if (state->streamer != NULL)
@@ -1530,10 +1457,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                                        r - 1, BBSTREAMER_UNKNOWN);
                 }
                 else
-                {
-                    pg_log_error("unexpected payload data");
-                    exit(1);
-                }
+                    pg_fatal("unexpected payload data");
                 break;
             }

@@ -1586,11 +1510,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                         state->manifest_file =
                             fopen(state->manifest_filename, "wb");
                         if (state->manifest_file == NULL)
-                        {
-                            pg_log_error("could not create file \"%s\": %m",
-                                         state->manifest_filename);
-                            exit(1);
-                        }
+                            pg_fatal("could not create file \"%s\": %m",
+                                     state->manifest_filename);
                     }
                 }
                 break;
@@ -1679,11 +1600,10 @@ static void
 ReportCopyDataParseError(size_t r, char *copybuf)
 {
     if (r == 0)
-        pg_log_error("empty COPY message");
+        pg_fatal("empty COPY message");
     else
-        pg_log_error("malformed COPY message of type %d, length %zu",
-                     copybuf[0], r);
-    exit(1);
+        pg_fatal("malformed COPY message of type %d, length %zu",
+                 copybuf[0], r);
 }

 /*
@@ -1728,10 +1648,7 @@ ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
         initPQExpBuffer(&buf);
         ReceiveBackupManifestInMemory(conn, &buf);
         if (PQExpBufferDataBroken(buf))
-        {
-            pg_log_error("out of memory");
-            exit(1);
-        }
+            pg_fatal("out of memory");

         /* Inject it into the output tarfile. */
         bbstreamer_inject_file(manifest_inject_streamer, "backup_manifest",
@@ -1801,10 +1718,7 @@ ReceiveBackupManifest(PGconn *conn)
              "%s/backup_manifest.tmp", basedir);
     state.file = fopen(state.filename, "wb");
     if (state.file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", state.filename);
-        exit(1);
-    }
+        pg_fatal("could not create file \"%s\": %m", state.filename);

     ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);

@@ -1825,8 +1739,7 @@ ReceiveBackupManifestChunk(size_t r, char *copybuf, void *callback_data)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m", state->filename);
-        exit(1);
+        pg_fatal("could not write to file \"%s\": %m", state->filename);
     }
 }

@@ -1885,9 +1798,8 @@ BaseBackup(void)
     {
         const char *serverver = PQparameterStatus(conn, "server_version");

-        pg_log_error("incompatible server version %s",
-                     serverver ? serverver : "'unknown'");
-        exit(1);
+        pg_fatal("incompatible server version %s",
+                 serverver ? serverver : "'unknown'");
     }
     if (serverMajor >= 1500)
         use_new_option_syntax = true;
@@ -1970,16 +1882,10 @@ BaseBackup(void)
         char       *colon;

         if (serverMajor < 1500)
-        {
-            pg_log_error("backup targets are not supported by this server version");
-            exit(1);
-        }
+            pg_fatal("backup targets are not supported by this server version");

         if (writerecoveryconf)
-        {
-            pg_log_error("recovery configuration cannot be written when a backup target is used");
-            exit(1);
-        }
+            pg_fatal("recovery configuration cannot be written when a backup target is used");

         AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");

@@ -2008,10 +1914,7 @@ BaseBackup(void)
         char *compressmethodstr = NULL;

         if (!use_new_option_syntax)
-        {
-            pg_log_error("server does not support server-side compression");
-            exit(1);
-        }
+            pg_fatal("server does not support server-side compression");
         switch (compressmethod)
         {
             case COMPRESSION_GZIP:
@@ -2049,28 +1952,19 @@ BaseBackup(void)
         basebkp = psprintf("BASE_BACKUP %s", buf.data);

     if (PQsendQuery(conn, basebkp) == 0)
-    {
-        pg_log_error("could not send replication command \"%s\": %s",
-                     "BASE_BACKUP", PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not send replication command \"%s\": %s",
+                 "BASE_BACKUP", PQerrorMessage(conn));

     /*
      * Get the starting WAL location
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not initiate base backup: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not initiate base backup: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
-                     PQntuples(res), PQnfields(res), 1, 2);
-        exit(1);
-    }
+        pg_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d
rowsand %d fields", 
+                 PQntuples(res), PQnfields(res), 1, 2);

     strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));

@@ -2098,16 +1992,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not get backup header: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not get backup header: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) < 1)
-    {
-        pg_log_error("no data returned from server");
-        exit(1);
-    }
+        pg_fatal("no data returned from server");

     /*
      * Sum up the total size, for progress reporting
@@ -2142,11 +2030,8 @@ BaseBackup(void)
     writing_to_stdout = format == 't' && basedir != NULL &&
         strcmp(basedir, "-") == 0;
     if (writing_to_stdout && PQntuples(res) > 1)
-    {
-        pg_log_error("can only write single tablespace to stdout, database has %d",
-                     PQntuples(res));
-        exit(1);
-    }
+        pg_fatal("can only write single tablespace to stdout, database has %d",
+                 PQntuples(res));

     /*
      * If we're streaming WAL, start the streaming session before we start
@@ -2222,16 +2107,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("backup failed: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("backup failed: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("no write-ahead log end position returned from server");
-        exit(1);
-    }
+        pg_fatal("no write-ahead log end position returned from server");
     strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
     if (verbose && includewal != NO_WAL)
         pg_log_info("write-ahead log end point: %s", xlogend);
@@ -2286,20 +2165,11 @@ BaseBackup(void)
         /* Just wait for the background process to exit */
         r = waitpid(bgchild, &status, 0);
         if (r == (pid_t) -1)
-        {
-            pg_log_error("could not wait for child process: %m");
-            exit(1);
-        }
+            pg_fatal("could not wait for child process: %m");
         if (r != bgchild)
-        {
-            pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
-            exit(1);
-        }
+            pg_fatal("child %d died, expected %d", (int) r, (int) bgchild);
         if (status != 0)
-        {
-            pg_log_error("%s", wait_result_to_str(status));
-            exit(1);
-        }
+            pg_fatal("%s", wait_result_to_str(status));
         /* Exited normally, we're happy! */
 #else                            /* WIN32 */

@@ -2309,11 +2179,8 @@ BaseBackup(void)
          * it's there.
          */
         if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-        {
-            pg_log_error("could not parse write-ahead log location \"%s\"",
-                         xlogend);
-            exit(1);
-        }
+            pg_fatal("could not parse write-ahead log location \"%s\"",
+                     xlogend);
         xlogendptr = ((uint64) hi) << 32 | lo;
         InterlockedIncrement(&has_xlogendptr);

@@ -2322,21 +2189,16 @@ BaseBackup(void)
             WAIT_OBJECT_0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not wait for child thread: %m");
-            exit(1);
+            pg_fatal("could not wait for child thread: %m");
         }
         if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not get child thread exit status: %m");
-            exit(1);
+            pg_fatal("could not get child thread exit status: %m");
         }
         if (status != 0)
-        {
-            pg_log_error("child thread exited with error %u",
-                         (unsigned int) status);
-            exit(1);
-        }
+            pg_fatal("child thread exited with error %u",
+                     (unsigned int) status);
         /* Exited normally, we're happy */
 #endif
     }
@@ -2403,11 +2265,8 @@ BaseBackup(void)
         else
         {
             if (rename(tmp_filename, filename) != 0)
-            {
-                pg_log_error("could not rename file \"%s\" to \"%s\": %m",
-                             tmp_filename, filename);
-                exit(1);
-            }
+                pg_fatal("could not rename file \"%s\" to \"%s\": %m",
+                         tmp_filename, filename);
         }
     }

@@ -2497,11 +2356,8 @@ main(int argc, char **argv)
                 else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
                     format = 't';
                 else
-                {
-                    pg_log_error("invalid output format \"%s\", must be \"plain\" or \"tar\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
+                             optarg);
                 break;
             case 'r':
                 maxrate = parse_max_rate(optarg);
@@ -2544,11 +2400,8 @@ main(int argc, char **argv)
                     includewal = STREAM_WAL;
                 }
                 else
-                {
-                    pg_log_error("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
+                             optarg);
                 break;
             case 1:
                 xlog_dir = pg_strdup(optarg);
@@ -2581,11 +2434,8 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "spread") == 0)
                     fastcheckpoint = false;
                 else
-                {
-                    pg_log_error("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
+                             optarg);
                 break;
             case 'd':
                 connection_string = pg_strdup(optarg);
@@ -2634,12 +2484,8 @@ main(int argc, char **argv)
                 manifest_checksums = pg_strdup(optarg);
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -2651,8 +2497,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2674,8 +2519,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && format != '\0')
     {
         pg_log_error("cannot specify both format and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == '\0')
@@ -2687,15 +2531,13 @@ main(int argc, char **argv)
     if (basedir == NULL && backup_target == NULL)
     {
         pg_log_error("must specify output directory or backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (basedir != NULL && backup_target != NULL)
     {
         pg_log_error("cannot specify both output directory and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2720,8 +2562,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("client-side compression is not possible when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2731,8 +2572,7 @@ main(int argc, char **argv)
     if (format == 'p' && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("only tar mode backups can be compressed");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2742,23 +2582,20 @@ main(int argc, char **argv)
     if (backup_target != NULL && includewal == STREAM_WAL)
     {
         pg_log_error("WAL cannot be streamed when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)
     {
         pg_log_error("cannot stream write-ahead logs in tar mode to stdout");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (replication_slot && includewal != STREAM_WAL)
     {
         pg_log_error("replication slots can only be used with WAL streaming");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2770,8 +2607,7 @@ main(int argc, char **argv)
         if (replication_slot)
         {
             pg_log_error("--no-slot cannot be used with slot name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         temp_replication_slot = false;
@@ -2783,8 +2619,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s needs a slot to be specified using --slot",
                          "--create-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2792,8 +2627,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s and %s are incompatible options",
                          "--create-slot", "--no-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2806,15 +2640,13 @@ main(int argc, char **argv)
         if (backup_target != NULL)
         {
             pg_log_error("WAL directory location cannot be specified along with a backup target");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         if (format != 'p')
         {
             pg_log_error("WAL directory location can only be specified in plain mode");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2823,8 +2655,7 @@ main(int argc, char **argv)
         if (!is_absolute_path(xlog_dir))
         {
             pg_log_error("WAL directory location must be an absolute path");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2837,37 +2668,29 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use compression level with method %s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
         case COMPRESSION_GZIP:
             if (compresslevel > 9)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 9",
-                             compresslevel, "gzip");
-                exit(1);
-            }
+                pg_fatal("compression level %d of method %s higher than maximum of 9",
+                         compresslevel, "gzip");
             if (compressloc == COMPRESS_LOCATION_CLIENT)
             {
 #ifdef HAVE_LIBZ
                 if (compresslevel == 0)
                     compresslevel = Z_DEFAULT_COMPRESSION;
 #else
-                pg_log_error("this build does not support compression with %s",
-                             "gzip");
-                exit(1);
+                pg_fatal("this build does not support compression with %s",
+                         "gzip");
 #endif
             }
             break;
         case COMPRESSION_LZ4:
             if (compresslevel > 12)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 12",
-                             compresslevel, "lz4");
-                exit(1);
-            }
+                pg_fatal("compression level %d of method %s higher than maximum of 12",
+                         compresslevel, "lz4");
             break;
     }

@@ -2878,8 +2701,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--progress", "--no-estimate-size");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2890,8 +2712,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-checksums");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2899,8 +2720,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-force-encode");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2968,13 +2788,9 @@ main(int argc, char **argv)

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, linkloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m", linkloc);
-            exit(1);
-        }
+            pg_fatal("could not create symbolic link \"%s\": %m", linkloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_fatal("symlinks are not supported on this platform");
 #endif
         free(linkloc);
     }
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index ccb215c398..f1dc85447e 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -239,10 +239,7 @@ get_destination_dir(char *dest_folder)
     Assert(dest_folder != NULL);
     dir = opendir(dest_folder);
     if (dir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", basedir);

     return dir;
 }
@@ -256,10 +253,7 @@ close_destination_dir(DIR *dest_dir, char *dest_folder)
 {
     Assert(dest_dir != NULL && dest_folder != NULL);
     if (closedir(dest_dir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", dest_folder);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", dest_folder);
 }


@@ -322,10 +316,7 @@ FindStreamingStart(uint32 *tli)

             snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
             if (stat(fullpath, &statbuf) != 0)
-            {
-                pg_log_error("could not stat file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_fatal("could not stat file \"%s\": %m", fullpath);

             if (statbuf.st_size != WalSegSz)
             {
@@ -346,27 +337,20 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open compressed file \"%s\": %m",
-                             fullpath);
-                exit(1);
-            }
+                pg_fatal("could not open compressed file \"%s\": %m",
+                         fullpath);
             if (lseek(fd, (off_t) (-4), SEEK_END) < 0)
-            {
-                pg_log_error("could not seek in compressed file \"%s\": %m",
-                             fullpath);
-                exit(1);
-            }
+                pg_fatal("could not seek in compressed file \"%s\": %m",
+                         fullpath);
             r = read(fd, (char *) buf, sizeof(buf));
             if (r != sizeof(buf))
             {
                 if (r < 0)
-                    pg_log_error("could not read compressed file \"%s\": %m",
-                                 fullpath);
+                    pg_fatal("could not read compressed file \"%s\": %m",
+                             fullpath);
                 else
-                    pg_log_error("could not read compressed file \"%s\": read %d of %zu",
-                                 fullpath, r, sizeof(buf));
-                exit(1);
+                    pg_fatal("could not read compressed file \"%s\": read %d of %zu",
+                             fullpath, r, sizeof(buf));
             }

             close(fd);
@@ -399,18 +383,12 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_fatal("could not open file \"%s\": %m", fullpath);

             status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not create LZ4 decompression context: %s",
-                             LZ4F_getErrorName(status));
-                exit(1);
-            }
+                pg_fatal("could not create LZ4 decompression context: %s",
+                         LZ4F_getErrorName(status));

             outbuf = pg_malloc0(LZ4_CHUNK_SZ);
             readbuf = pg_malloc0(LZ4_CHUNK_SZ);
@@ -421,10 +399,7 @@ FindStreamingStart(uint32 *tli)

                 r = read(fd, readbuf, LZ4_CHUNK_SZ);
                 if (r < 0)
-                {
-                    pg_log_error("could not read file \"%s\": %m", fullpath);
-                    exit(1);
-                }
+                    pg_fatal("could not read file \"%s\": %m", fullpath);

                 /* Done reading the file */
                 if (r == 0)
@@ -442,12 +417,9 @@ FindStreamingStart(uint32 *tli)
                     status = LZ4F_decompress(ctx, outbuf, &out_size,
                                              readp, &read_size, &dec_opt);
                     if (LZ4F_isError(status))
-                    {
-                        pg_log_error("could not decompress file \"%s\": %s",
-                                     fullpath,
-                                     LZ4F_getErrorName(status));
-                        exit(1);
-                    }
+                        pg_fatal("could not decompress file \"%s\": %s",
+                                 fullpath,
+                                 LZ4F_getErrorName(status));

                     readp += read_size;
                     uncompressed_size += out_size;
@@ -468,11 +440,8 @@ FindStreamingStart(uint32 *tli)

             status = LZ4F_freeDecompressionContext(ctx);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not free LZ4 decompression context: %s",
-                             LZ4F_getErrorName(status));
-                exit(1);
-            }
+                pg_fatal("could not free LZ4 decompression context: %s",
+                         LZ4F_getErrorName(status));

             if (uncompressed_size != WalSegSz)
             {
@@ -483,8 +452,8 @@ FindStreamingStart(uint32 *tli)
 #else
             pg_log_error("could not check file \"%s\"",
                          dirent->d_name);
-            pg_log_error("this build does not support compression with %s",
-                         "LZ4");
+            pg_log_error_detail("This build does not support compression with %s.",
+                                "LZ4");
             exit(1);
 #endif
         }
@@ -501,10 +470,7 @@ FindStreamingStart(uint32 *tli)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", basedir);

     close_destination_dir(dir, basedir);

@@ -752,10 +718,7 @@ main(int argc, char **argv)
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'n':
@@ -793,19 +756,12 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     compression_method = COMPRESSION_NONE;
                 else
-                {
-                    pg_log_error("invalid value \"%s\" for option %s",
-                                 optarg, "--compression-method");
-                    exit(1);
-                }
+                    pg_fatal("invalid value \"%s\" for option %s",
+                             optarg, "--compression-method");
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -817,16 +773,14 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && do_create_slot)
     {
         pg_log_error("cannot use --create-slot together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -835,16 +789,14 @@ main(int argc, char **argv)
         /* translator: second %s is an option name */
         pg_log_error("%s needs a slot to be specified using --slot",
                      do_drop_slot ? "--drop-slot" : "--create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (synchronous && !do_sync)
     {
         pg_log_error("cannot use --synchronous together with --no-sync");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -854,8 +806,7 @@ main(int argc, char **argv)
     if (basedir == NULL && !do_drop_slot && !do_create_slot)
     {
         pg_log_error("no target directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,8 +821,7 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
@@ -883,9 +833,8 @@ main(int argc, char **argv)
                 compresslevel = Z_DEFAULT_COMPRESSION;
             }
 #else
-            pg_log_error("this build does not support compression with %s",
-                         "gzip");
-            exit(1);
+            pg_fatal("this build does not support compression with %s",
+                     "gzip");
 #endif
             break;
         case COMPRESSION_LZ4:
@@ -894,14 +843,12 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "lz4");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
 #else
-            pg_log_error("this build does not support compression with %s",
-                         "LZ4");
-            exit(1);
+            pg_fatal("this build does not support compression with %s",
+                     "LZ4");
 #endif
             break;
     }
@@ -947,11 +894,8 @@ main(int argc, char **argv)
      * be defined in this context.
      */
     if (db_name)
-    {
-        pg_log_error("replication connection using slot \"%s\" is unexpectedly database specific",
-                     replication_slot);
-        exit(1);
-    }
+        pg_fatal("replication connection using slot \"%s\" is unexpectedly database specific",
+                 replication_slot);

     /*
      * Set umask so that directories/files are created with the same
@@ -1009,10 +953,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index cc35d16f32..b59ff23f61 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -193,10 +193,7 @@ OutputFsync(TimestampTz now)
         return true;

     if (fsync(outfd) != 0)
-    {
-        pg_log_fatal("could not fsync file \"%s\": %m", outfile);
-        exit(1);
-    }
+        pg_fatal("could not fsync file \"%s\": %m", outfile);

     return true;
 }
@@ -780,18 +777,12 @@ main(int argc, char **argv)
 /* replication options */
             case 'I':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse start position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse start position \"%s\"", optarg);
                 startpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'o':
@@ -842,12 +833,8 @@ main(int argc, char **argv)
                 break;

             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -859,8 +846,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,64 +856,56 @@ main(int argc, char **argv)
     if (replication_slot == NULL)
     {
         pg_log_error("no slot specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_start_slot && outfile == NULL)
     {
         pg_log_error("no target file specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && dbname == NULL)
     {
         pg_log_error("no database specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && !do_create_slot && !do_start_slot)
     {
         pg_log_error("at least one action needs to be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && (do_create_slot || do_start_slot))
     {
         pg_log_error("cannot use --create-slot or --start together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (startpos != InvalidXLogRecPtr && (do_create_slot || do_drop_slot))
     {
         pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (endpos != InvalidXLogRecPtr && !do_start_slot)
     {
         pg_log_error("--endpos may only be specified with --start");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (two_phase && !do_create_slot)
     {
         pg_log_error("--two-phase may only be specified with --create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -958,10 +936,7 @@ main(int argc, char **argv)
         exit(1);

     if (db_name == NULL)
-    {
-        pg_log_error("could not establish database-specific replication connection");
-        exit(1);
-    }
+        pg_fatal("could not establish database-specific replication connection");

     /*
      * Set umask so that directories/files are created with the same
@@ -1011,10 +986,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c
index d39e4b11a1..42d50931d3 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
             /* fsync file in case of a previous crash */
             if (stream->walmethod->sync(f) != 0)
             {
-                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
+                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
                              fn, stream->walmethod->getlasterror());
                 stream->walmethod->close(f, CLOSE_UNLINK);
                 exit(1);
@@ -778,11 +778,8 @@ HandleCopyStream(PGconn *conn, StreamCtl *stream,
         if (stream->synchronous && lastFlushPosition < blockpos && walfile != NULL)
         {
             if (stream->walmethod->sync(walfile) != 0)
-            {
-                pg_log_fatal("could not fsync file \"%s\": %s",
-                             current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
+                pg_fatal("could not fsync file \"%s\": %s",
+                         current_walfile_name, stream->walmethod->getlasterror());
             lastFlushPosition = blockpos;

             /*
@@ -1030,11 +1027,8 @@ ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
              * shutdown of the server.
              */
             if (stream->walmethod->sync(walfile) != 0)
-            {
-                pg_log_fatal("could not fsync file \"%s\": %s",
-                             current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
+                pg_fatal("could not fsync file \"%s\": %s",
+                         current_walfile_name, stream->walmethod->getlasterror());
             lastFlushPosition = blockpos;
         }

diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4a6afd1a06..86c0493a94 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -88,10 +88,7 @@ GetConnection(void)
     {
         conn_opts = PQconninfoParse(connection_string, &err_msg);
         if (conn_opts == NULL)
-        {
-            pg_log_error("%s", err_msg);
-            exit(1);
-        }
+            pg_fatal("%s", err_msg);

         for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
         {
@@ -182,10 +179,7 @@ GetConnection(void)
          * and PQconnectdbParams returns NULL, we call exit(1) directly.
          */
         if (!tmpconn)
-        {
-            pg_log_error("could not connect to server");
-            exit(1);
-        }
+            pg_fatal("could not connect to server");

         /* If we need a password and -w wasn't given, loop back and get one */
         if (PQstatus(tmpconn) == CONNECTION_BAD &&
diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c
index a6d08c1270..d824bea053 100644
--- a/src/bin/pg_basebackup/walmethods.c
+++ b/src/bin/pg_basebackup/walmethods.c
@@ -1195,9 +1195,8 @@ tar_close(Walfile f, WalCloseMethod method)
     if (tar_sync(f) < 0)
     {
         /* XXX this seems pretty bogus; why is only this case fatal? */
-        pg_log_fatal("could not fsync file \"%s\": %s",
-                     tf->pathname, tar_getlasterror());
-        exit(1);
+        pg_fatal("could not fsync file \"%s\": %s",
+                 tf->pathname, tar_getlasterror());
     }

     /* Clean up and done */
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 7e69475947..22220a5b33 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -208,10 +208,7 @@ scan_file(const char *fn, int segmentno)
     f = open(fn, PG_BINARY | flags, 0);

     if (f < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", fn);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", fn);

     files_scanned++;

@@ -225,12 +222,11 @@ scan_file(const char *fn, int segmentno)
         if (r != BLCKSZ)
         {
             if (r < 0)
-                pg_log_error("could not read block %u in file \"%s\": %m",
-                             blockno, fn);
+                pg_fatal("could not read block %u in file \"%s\": %m",
+                         blockno, fn);
             else
-                pg_log_error("could not read block %u in file \"%s\": read %d of %d",
-                             blockno, fn, r, BLCKSZ);
-            exit(1);
+                pg_fatal("could not read block %u in file \"%s\": read %d of %d",
+                         blockno, fn, r, BLCKSZ);
         }
         blocks_scanned++;

@@ -275,22 +271,18 @@ scan_file(const char *fn, int segmentno)

             /* Seek back to beginning of block */
             if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
-            {
-                pg_log_error("seek failed for block %u in file \"%s\": %m", blockno, fn);
-                exit(1);
-            }
+                pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);

             /* Write block with checksum */
             w = write(f, buf.data, BLCKSZ);
             if (w != BLCKSZ)
             {
                 if (w < 0)
-                    pg_log_error("could not write block %u in file \"%s\": %m",
-                                 blockno, fn);
+                    pg_fatal("could not write block %u in file \"%s\": %m",
+                             blockno, fn);
                 else
-                    pg_log_error("could not write block %u in file \"%s\": wrote %d of %d",
-                                 blockno, fn, w, BLCKSZ);
-                exit(1);
+                    pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
+                             blockno, fn, w, BLCKSZ);
             }
         }

@@ -334,10 +326,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
     dir = opendir(path);
     if (!dir)
-    {
-        pg_log_error("could not open directory \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", path);
     while ((de = readdir(dir)) != NULL)
     {
         char        fn[MAXPGPATH];
@@ -361,10 +350,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)

         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
         if (lstat(fn, &st) < 0)
-        {
-            pg_log_error("could not stat file \"%s\": %m", fn);
-            exit(1);
-        }
+            pg_fatal("could not stat file \"%s\": %m", fn);
         if (S_ISREG(st.st_mode))
         {
             char        fnonly[MAXPGPATH];
@@ -388,11 +374,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                 *segmentpath++ = '\0';
                 segmentno = atoi(segmentpath);
                 if (segmentno == 0)
-                {
-                    pg_log_error("invalid segment number %d in file name \"%s\"",
-                                 segmentno, fn);
-                    exit(1);
-                }
+                    pg_fatal("invalid segment number %d in file name \"%s\"",
+                             segmentno, fn);
             }

             forkpath = strchr(fnonly, '_');
@@ -440,11 +423,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);

                 if (lstat(tblspc_path, &tblspc_st) < 0)
-                {
-                    pg_log_error("could not stat file \"%s\": %m",
-                                 tblspc_path);
-                    exit(1);
-                }
+                    pg_fatal("could not stat file \"%s\": %m",
+                             tblspc_path);

                 /*
                  * Move backwards once as the scan needs to happen for the
@@ -539,7 +519,8 @@ main(int argc, char *argv[])
                 showprogress = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -555,7 +536,7 @@ main(int argc, char *argv[])
         if (DataDir == NULL)
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -565,8 +546,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -574,30 +554,23 @@ main(int argc, char *argv[])
     if (mode != PG_MODE_CHECK && only_filenode)
     {
         pg_log_error("option -f/--filenode can only be used with --check");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     /* Read the control file and check compatibility */
     ControlFile = get_controlfile(DataDir, &crc_ok);
     if (!crc_ok)
-    {
-        pg_log_error("pg_control CRC value is incorrect");
-        exit(1);
-    }
+        pg_fatal("pg_control CRC value is incorrect");

     if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
-    {
-        pg_log_error("cluster is not compatible with this version of pg_checksums");
-        exit(1);
-    }
+        pg_fatal("cluster is not compatible with this version of pg_checksums");

     if (ControlFile->blcksz != BLCKSZ)
     {
         pg_log_error("database cluster is not compatible");
-        fprintf(stderr, _("The database cluster was initialized with block size %u, but pg_checksums was compiled with
blocksize %u.\n"), 
-                ControlFile->blcksz, BLCKSZ);
+        pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled
withblock size %u.", 
+                            ControlFile->blcksz, BLCKSZ);
         exit(1);
     }

@@ -608,31 +581,19 @@ main(int argc, char *argv[])
      */
     if (ControlFile->state != DB_SHUTDOWNED &&
         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
-    {
-        pg_log_error("cluster must be shut down");
-        exit(1);
-    }
+        pg_fatal("cluster must be shut down");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_CHECK)
-    {
-        pg_log_error("data checksums are not enabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are not enabled in cluster");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_DISABLE)
-    {
-        pg_log_error("data checksums are already disabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are already disabled in cluster");

     if (ControlFile->data_checksum_version > 0 &&
         mode == PG_MODE_ENABLE)
-    {
-        pg_log_error("data checksums are already enabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are already enabled in cluster");

     /* Operate on all files if checking or enabling checksums */
     if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d94..c390ec51ce 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -134,7 +134,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -152,15 +153,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b9a25442f5..794e6e7ce9 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -340,9 +340,9 @@ flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,

             /* With partitions there can only be one parent */
             if (tblinfo[i].numParents != 1)
-                fatal("invalid number of parents %d for table \"%s\"",
-                      tblinfo[i].numParents,
-                      tblinfo[i].dobj.name);
+                pg_fatal("invalid number of parents %d for table \"%s\"",
+                         tblinfo[i].numParents,
+                         tblinfo[i].dobj.name);

             attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo));
             attachinfo->dobj.objType = DO_TABLE_ATTACH;
@@ -1001,13 +1001,10 @@ findParentsByOid(TableInfo *self,

                 parent = findTableByOid(inhinfo[i].inhparent);
                 if (parent == NULL)
-                {
-                    pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
-                                 inhinfo[i].inhparent,
-                                 self->dobj.name,
-                                 oid);
-                    exit_nicely(1);
-                }
+                    pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
+                             inhinfo[i].inhparent,
+                             self->dobj.name,
+                             oid);
                 self->parents[j++] = parent;
             }
         }
@@ -1043,10 +1040,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
             if (j > 0)
             {
                 if (argNum >= arraysize)
-                {
-                    pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
-                    exit_nicely(1);
-                }
+                    pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
                 temp[j] = '\0';
                 array[argNum++] = atooid(temp);
                 j = 0;
@@ -1058,10 +1052,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
         {
             if (!(isdigit((unsigned char) s) || s == '-') ||
                 j >= sizeof(temp) - 1)
-            {
-                pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
-                exit_nicely(1);
-            }
+                pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
             temp[j++] = s;
         }
     }
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 9077fdb74d..62f940ff7a 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -108,7 +108,7 @@ ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
         *alg = COMPR_ALG_NONE;
     else
     {
-        fatal("invalid compression code: %d", compression);
+        pg_fatal("invalid compression code: %d", compression);
         *alg = COMPR_ALG_NONE;    /* keep compiler quiet */
     }

@@ -131,7 +131,7 @@ AllocateCompressor(int compression, WriteFunc writeF)

 #ifndef HAVE_LIBZ
     if (alg == COMPR_ALG_LIBZ)
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif

     cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
@@ -167,7 +167,7 @@ ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
 #ifdef HAVE_LIBZ
         ReadDataFromArchiveZlib(AH, readF);
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif
     }
 }
@@ -185,7 +185,7 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
 #ifdef HAVE_LIBZ
             WriteDataToArchiveZlib(AH, cs, data, dLen);
 #else
-            fatal("not built with zlib support");
+            pg_fatal("not built with zlib support");
 #endif
             break;
         case COMPR_ALG_NONE:
@@ -233,8 +233,8 @@ InitCompressorZlib(CompressorState *cs, int level)
     cs->zlibOutSize = ZLIB_OUT_SIZE;

     if (deflateInit(zp, level) != Z_OK)
-        fatal("could not initialize compression library: %s",
-              zp->msg);
+        pg_fatal("could not initialize compression library: %s",
+                 zp->msg);

     /* Just be paranoid - maybe End is called after Start, with no Write */
     zp->next_out = (void *) cs->zlibOut;
@@ -253,7 +253,7 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
     DeflateCompressorZlib(AH, cs, true);

     if (deflateEnd(zp) != Z_OK)
-        fatal("could not close compression stream: %s", zp->msg);
+        pg_fatal("could not close compression stream: %s", zp->msg);

     free(cs->zlibOut);
     free(cs->zp);
@@ -270,7 +270,7 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
     {
         res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
         if (res == Z_STREAM_ERROR)
-            fatal("could not compress data: %s", zp->msg);
+            pg_fatal("could not compress data: %s", zp->msg);
         if ((flush && (zp->avail_out < cs->zlibOutSize))
             || (zp->avail_out == 0)
             || (zp->avail_in != 0)
@@ -330,8 +330,8 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
     out = pg_malloc(ZLIB_OUT_SIZE + 1);

     if (inflateInit(zp) != Z_OK)
-        fatal("could not initialize compression library: %s",
-              zp->msg);
+        pg_fatal("could not initialize compression library: %s",
+                 zp->msg);

     /* no minimal chunk size for zlib */
     while ((cnt = readF(AH, &buf, &buflen)))
@@ -346,7 +346,7 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)

             res = inflate(zp, 0);
             if (res != Z_OK && res != Z_STREAM_END)
-                fatal("could not uncompress data: %s", zp->msg);
+                pg_fatal("could not uncompress data: %s", zp->msg);

             out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
             ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
@@ -361,14 +361,14 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
         zp->avail_out = ZLIB_OUT_SIZE;
         res = inflate(zp, 0);
         if (res != Z_OK && res != Z_STREAM_END)
-            fatal("could not uncompress data: %s", zp->msg);
+            pg_fatal("could not uncompress data: %s", zp->msg);

         out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
         ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
     }

     if (inflateEnd(zp) != Z_OK)
-        fatal("could not close compression library: %s", zp->msg);
+        pg_fatal("could not close compression library: %s", zp->msg);

     free(buf);
     free(out);
@@ -501,7 +501,7 @@ cfopen_write(const char *path, const char *mode, int compression)
         fp = cfopen(fname, mode, compression);
         free_keep_errno(fname);
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
         fp = NULL;                /* keep compiler quiet */
 #endif
     }
@@ -544,7 +544,7 @@ cfopen(const char *path, const char *mode, int compression)
             fp = NULL;
         }
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif
     }
     else
@@ -581,8 +581,8 @@ cfread(void *ptr, int size, cfp *fp)
             int            errnum;
             const char *errmsg = gzerror(fp->compressedfp, &errnum);

-            fatal("could not read from input file: %s",
-                  errnum == Z_ERRNO ? strerror(errno) : errmsg);
+            pg_fatal("could not read from input file: %s",
+                     errnum == Z_ERRNO ? strerror(errno) : errmsg);
         }
     }
     else
@@ -618,9 +618,9 @@ cfgetc(cfp *fp)
         if (ret == EOF)
         {
             if (!gzeof(fp->compressedfp))
-                fatal("could not read from input file: %s", strerror(errno));
+                pg_fatal("could not read from input file: %s", strerror(errno));
             else
-                fatal("could not read from input file: end of file");
+                pg_fatal("could not read from input file: end of file");
         }
     }
     else
diff --git a/src/bin/pg_dump/nls.mk b/src/bin/pg_dump/nls.mk
index 6276fd443b..220d1ec75f 100644
--- a/src/bin/pg_dump/nls.mk
+++ b/src/bin/pg_dump/nls.mk
@@ -11,8 +11,7 @@ GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) \
                    ../../common/exec.c ../../common/fe_memutils.c \
                    ../../common/wait_error.c
 GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) \
-                   fatal simple_prompt \
+                   simple_prompt \
                    ExecuteSqlCommand:3 warn_or_exit_horribly:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    fatal:1:c-format \
     warn_or_exit_horribly:2:c-format
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index bc5251be82..c9f6b86bb0 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -250,10 +250,7 @@ init_parallel_dump_utils(void)
         /* Initialize socket access */
         err = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (err != 0)
-        {
-            pg_log_error("%s() failed: error code %d", "WSAStartup", err);
-            exit_nicely(1);
-        }
+            pg_fatal("%s() failed: error code %d", "WSAStartup", err);

         parallel_init_done = true;
     }
@@ -393,7 +390,7 @@ archive_close_connection(int code, void *arg)
  *
  * Note that we don't expect to come here during normal exit (the workers
  * should be long gone, and the ParallelState too).  We're only here in a
- * fatal() situation, so intervening to cancel active commands is
+ * pg_fatal() situation, so intervening to cancel active commands is
  * appropriate.
  */
 static void
@@ -961,7 +958,7 @@ ParallelBackupStart(ArchiveHandle *AH)

         /* Create communication pipes for this worker */
         if (pgpipe(pipeMW) < 0 || pgpipe(pipeWM) < 0)
-            fatal("could not create communication channels: %m");
+            pg_fatal("could not create communication channels: %m");

         /* leader's ends of the pipes */
         slot->pipeRead = pipeWM[PIPE_READ];
@@ -1018,7 +1015,7 @@ ParallelBackupStart(ArchiveHandle *AH)
         else if (pid < 0)
         {
             /* fork failed */
-            fatal("could not create worker process: %m");
+            pg_fatal("could not create worker process: %m");
         }

         /* In Leader after successful fork */
@@ -1148,8 +1145,8 @@ parseWorkerCommand(ArchiveHandle *AH, TocEntry **te, T_Action *act,
         Assert(*te != NULL);
     }
     else
-        fatal("unrecognized command received from leader: \"%s\"",
-              msg);
+        pg_fatal("unrecognized command received from leader: \"%s\"",
+                 msg);
 }

 /*
@@ -1191,8 +1188,8 @@ parseWorkerResponse(ArchiveHandle *AH, TocEntry *te,
         AH->public.n_errors += n_errors;
     }
     else
-        fatal("invalid message received from worker: \"%s\"",
-              msg);
+        pg_fatal("invalid message received from worker: \"%s\"",
+                 msg);

     return status;
 }
@@ -1323,10 +1320,10 @@ lockTableForWorker(ArchiveHandle *AH, TocEntry *te)
     res = PQexec(AH->connection, query->data);

     if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-        fatal("could not obtain lock on relation \"%s\"\n"
-              "This usually means that someone requested an ACCESS EXCLUSIVE lock "
-              "on the table after the pg_dump parent process had gotten the "
-              "initial ACCESS SHARE lock on the table.", qualId);
+        pg_fatal("could not obtain lock on relation \"%s\"\n"
+                 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
+                 "on the table after the pg_dump parent process had gotten the "
+                 "initial ACCESS SHARE lock on the table.", qualId);

     PQclear(res);
     destroyPQExpBuffer(query);
@@ -1412,7 +1409,7 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
     {
         /* If do_wait is true, we must have detected EOF on some socket */
         if (do_wait)
-            fatal("a worker process died unexpectedly");
+            pg_fatal("a worker process died unexpectedly");
         return false;
     }

@@ -1429,8 +1426,8 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
         pstate->te[worker] = NULL;
     }
     else
-        fatal("invalid message received from worker: \"%s\"",
-              msg);
+        pg_fatal("invalid message received from worker: \"%s\"",
+                 msg);

     /* Free the string returned from getMessageFromWorker */
     free(msg);
@@ -1534,7 +1531,7 @@ sendMessageToLeader(int pipefd[2], const char *str)
     int            len = strlen(str) + 1;

     if (pipewrite(pipefd[PIPE_WRITE], str, len) != len)
-        fatal("could not write to the communication channel: %m");
+        pg_fatal("could not write to the communication channel: %m");
 }

 /*
@@ -1611,7 +1608,7 @@ getMessageFromWorker(ParallelState *pstate, bool do_wait, int *worker)
     }

     if (i < 0)
-        fatal("%s() failed: %m", "select");
+        pg_fatal("%s() failed: %m", "select");

     for (i = 0; i < pstate->numWorkers; i++)
     {
@@ -1652,7 +1649,7 @@ sendMessageToWorker(ParallelState *pstate, int worker, const char *str)

     if (pipewrite(pstate->parallelSlot[worker].pipeWrite, str, len) != len)
     {
-        fatal("could not write to the communication channel: %m");
+        pg_fatal("could not write to the communication channel: %m");
     }
 }

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..24e42fa5d7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -276,7 +276,7 @@ CloseArchive(Archive *AHX)
         res = fclose(AH->OF);

     if (res != 0)
-        fatal("could not close output file: %m");
+        pg_fatal("could not close output file: %m");
 }

 /* Public */
@@ -330,8 +330,8 @@ ProcessArchiveRestoreOptions(Archive *AHX)
                     /* ok no matter which section we were in */
                     break;
                 default:
-                    fatal("unexpected section code %d",
-                          (int) te->section);
+                    pg_fatal("unexpected section code %d",
+                             (int) te->section);
                     break;
             }
         }
@@ -367,11 +367,11 @@ RestoreArchive(Archive *AHX)
     {
         /* We haven't got round to making this work for all archive formats */
         if (AH->ClonePtr == NULL || AH->ReopenPtr == NULL)
-            fatal("parallel restore is not supported with this archive file format");
+            pg_fatal("parallel restore is not supported with this archive file format");

         /* Doesn't work if the archive represents dependencies as OIDs */
         if (AH->version < K_VERS_1_8)
-            fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");
+            pg_fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");

         /*
          * It's also not gonna work if we can't reopen the input file, so
@@ -389,7 +389,7 @@ RestoreArchive(Archive *AHX)
         for (te = AH->toc->next; te != AH->toc; te = te->next)
         {
             if (te->hadDumper && (te->reqs & REQ_DATA) != 0)
-                fatal("cannot restore from compressed archive (compression not supported in this installation)");
+                pg_fatal("cannot restore from compressed archive (compression not supported in this installation)");
         }
     }
 #endif
@@ -408,7 +408,7 @@ RestoreArchive(Archive *AHX)
     {
         pg_log_info("connecting to database for restore");
         if (AH->version < K_VERS_1_3)
-            fatal("direct database connections are not supported in pre-1.3 archives");
+            pg_fatal("direct database connections are not supported in pre-1.3 archives");

         /*
          * We don't want to guess at whether the dump will successfully
@@ -1037,7 +1037,7 @@ WriteData(Archive *AHX, const void *data, size_t dLen)
     ArchiveHandle *AH = (ArchiveHandle *) AHX;

     if (!AH->currToc)
-        fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");
+        pg_fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");

     AH->WriteDataPtr(AH, data, dLen);
 }
@@ -1220,7 +1220,7 @@ StartBlob(Archive *AHX, Oid oid)
     ArchiveHandle *AH = (ArchiveHandle *) AHX;

     if (!AH->StartBlobPtr)
-        fatal("large-object output not supported in chosen format");
+        pg_fatal("large-object output not supported in chosen format");

     AH->StartBlobPtr(AH, AH->currToc, oid);

@@ -1311,13 +1311,13 @@ StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
         {
             loOid = lo_create(AH->connection, oid);
             if (loOid == 0 || loOid != oid)
-                fatal("could not create large object %u: %s",
-                      oid, PQerrorMessage(AH->connection));
+                pg_fatal("could not create large object %u: %s",
+                         oid, PQerrorMessage(AH->connection));
         }
         AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
         if (AH->loFd == -1)
-            fatal("could not open large object %u: %s",
-                  oid, PQerrorMessage(AH->connection));
+            pg_fatal("could not open large object %u: %s",
+                     oid, PQerrorMessage(AH->connection));
     }
     else
     {
@@ -1372,7 +1372,7 @@ SortTocFromFile(Archive *AHX)
     /* Setup the file */
     fh = fopen(ropt->tocFile, PG_BINARY_R);
     if (!fh)
-        fatal("could not open TOC file \"%s\": %m", ropt->tocFile);
+        pg_fatal("could not open TOC file \"%s\": %m", ropt->tocFile);

     initStringInfo(&linebuf);

@@ -1407,8 +1407,8 @@ SortTocFromFile(Archive *AHX)
         /* Find TOC entry */
         te = getTocEntryByDumpId(AH, id);
         if (!te)
-            fatal("could not find entry for ID %d",
-                  id);
+            pg_fatal("could not find entry for ID %d",
+                     id);

         /* Mark it wanted */
         ropt->idWanted[id - 1] = true;
@@ -1430,7 +1430,7 @@ SortTocFromFile(Archive *AHX)
     pg_free(linebuf.data);

     if (fclose(fh) != 0)
-        fatal("could not close TOC file: %m");
+        pg_fatal("could not close TOC file: %m");
 }

 /**********************
@@ -1544,9 +1544,9 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
     if (!AH->OF)
     {
         if (filename)
-            fatal("could not open output file \"%s\": %m", filename);
+            pg_fatal("could not open output file \"%s\": %m", filename);
         else
-            fatal("could not open output file: %m");
+            pg_fatal("could not open output file: %m");
     }
 }

@@ -1573,7 +1573,7 @@ RestoreOutput(ArchiveHandle *AH, OutputContext savedContext)
         res = fclose(AH->OF);

     if (res != 0)
-        fatal("could not close output file: %m");
+        pg_fatal("could not close output file: %m");

     AH->gzOut = savedContext.gzOut;
     AH->OF = savedContext.OF;
@@ -1736,34 +1736,34 @@ warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt,...)

         case STAGE_INITIALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while INITIALIZING:");
+                pg_log_info("while INITIALIZING:");
             break;

         case STAGE_PROCESSING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while PROCESSING TOC:");
+                pg_log_info("while PROCESSING TOC:");
             break;

         case STAGE_FINALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while FINALIZING:");
+                pg_log_info("while FINALIZING:");
             break;
     }
     if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
     {
-        pg_log_generic(PG_LOG_INFO, "from TOC entry %d; %u %u %s %s %s",
-                       AH->currentTE->dumpId,
-                       AH->currentTE->catalogId.tableoid,
-                       AH->currentTE->catalogId.oid,
-                       AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
-                       AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
-                       AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
+        pg_log_info("from TOC entry %d; %u %u %s %s %s",
+                    AH->currentTE->dumpId,
+                    AH->currentTE->catalogId.tableoid,
+                    AH->currentTE->catalogId.oid,
+                    AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
+                    AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
+                    AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
     }
     AH->lastErrorStage = AH->stage;
     AH->lastErrorTE = AH->currentTE;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, fmt, ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
     va_end(ap);

     if (AH->public.exit_on_error)
@@ -1827,7 +1827,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
     {
         /* this check is purely paranoia, maxDumpId should be correct */
         if (te->dumpId <= 0 || te->dumpId > maxDumpId)
-            fatal("bad dumpId");
+            pg_fatal("bad dumpId");

         /* tocsByDumpId indexes all TOCs by their dump ID */
         AH->tocsByDumpId[te->dumpId] = te;
@@ -1848,7 +1848,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
              * item's dump ID, so there should be a place for it in the array.
              */
             if (tableId <= 0 || tableId > maxDumpId)
-                fatal("bad table dumpId for TABLE DATA item");
+                pg_fatal("bad table dumpId for TABLE DATA item");

             AH->tableDataId[tableId] = te->dumpId;
         }
@@ -1940,7 +1940,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
             break;

         default:
-            fatal("unexpected data offset flag %d", offsetFlg);
+            pg_fatal("unexpected data offset flag %d", offsetFlg);
     }

     /*
@@ -1953,7 +1953,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
         else
         {
             if (AH->ReadBytePtr(AH) != 0)
-                fatal("file offset in dump file is too large");
+                pg_fatal("file offset in dump file is too large");
         }
     }

@@ -2091,8 +2091,8 @@ _discoverArchiveFormat(ArchiveHandle *AH)
             char        buf[MAXPGPATH];

             if (snprintf(buf, MAXPGPATH, "%s/toc.dat", AH->fSpec) >= MAXPGPATH)
-                fatal("directory name too long: \"%s\"",
-                      AH->fSpec);
+                pg_fatal("directory name too long: \"%s\"",
+                         AH->fSpec);
             if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
             {
                 AH->format = archDirectory;
@@ -2101,39 +2101,39 @@ _discoverArchiveFormat(ArchiveHandle *AH)

 #ifdef HAVE_LIBZ
             if (snprintf(buf, MAXPGPATH, "%s/toc.dat.gz", AH->fSpec) >= MAXPGPATH)
-                fatal("directory name too long: \"%s\"",
-                      AH->fSpec);
+                pg_fatal("directory name too long: \"%s\"",
+                         AH->fSpec);
             if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
             {
                 AH->format = archDirectory;
                 return AH->format;
             }
 #endif
-            fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
-                  AH->fSpec);
+            pg_fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
+                     AH->fSpec);
             fh = NULL;            /* keep compiler quiet */
         }
         else
         {
             fh = fopen(AH->fSpec, PG_BINARY_R);
             if (!fh)
-                fatal("could not open input file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
         }
     }
     else
     {
         fh = stdin;
         if (!fh)
-            fatal("could not open input file: %m");
+            pg_fatal("could not open input file: %m");
     }

     if ((cnt = fread(sig, 1, 5, fh)) != 5)
     {
         if (ferror(fh))
-            fatal("could not read input file: %m");
+            pg_fatal("could not read input file: %m");
         else
-            fatal("input file is too short (read %lu, expected 5)",
-                  (unsigned long) cnt);
+            pg_fatal("input file is too short (read %lu, expected 5)",
+                     (unsigned long) cnt);
     }

     /* Save it, just in case we need it later */
@@ -2164,19 +2164,19 @@ _discoverArchiveFormat(ArchiveHandle *AH)
              * looks like it's probably a text format dump. so suggest they
              * try psql
              */
-            fatal("input file appears to be a text format dump. Please use psql.");
+            pg_fatal("input file appears to be a text format dump. Please use psql.");
         }

         if (AH->lookaheadLen != 512)
         {
             if (feof(fh))
-                fatal("input file does not appear to be a valid archive (too short?)");
+                pg_fatal("input file does not appear to be a valid archive (too short?)");
             else
                 READ_ERROR_EXIT(fh);
         }

         if (!isValidTarHeader(AH->lookahead))
-            fatal("input file does not appear to be a valid archive");
+            pg_fatal("input file does not appear to be a valid archive");

         AH->format = archTar;
     }
@@ -2185,7 +2185,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
     if (wantClose)
     {
         if (fclose(fh) != 0)
-            fatal("could not close input file: %m");
+            pg_fatal("could not close input file: %m");
         /* Forget lookahead, since we'll re-read header after re-opening */
         AH->readHeader = 0;
         AH->lookaheadLen = 0;
@@ -2302,7 +2302,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
             break;

         default:
-            fatal("unrecognized file format \"%d\"", fmt);
+            pg_fatal("unrecognized file format \"%d\"", fmt);
     }

     return AH;
@@ -2388,8 +2388,8 @@ mark_dump_job_done(ArchiveHandle *AH,
                 te->dumpId, te->desc, te->tag);

     if (status != 0)
-        fatal("worker process failed: exit code %d",
-              status);
+        pg_fatal("worker process failed: exit code %d",
+                 status);
 }


@@ -2509,8 +2509,8 @@ ReadToc(ArchiveHandle *AH)

         /* Sanity check */
         if (te->dumpId <= 0)
-            fatal("entry ID %d out of range -- perhaps a corrupt TOC",
-                  te->dumpId);
+            pg_fatal("entry ID %d out of range -- perhaps a corrupt TOC",
+                     te->dumpId);

         te->hadDumper = ReadInt(AH);

@@ -2671,13 +2671,13 @@ processEncodingEntry(ArchiveHandle *AH, TocEntry *te)
         *ptr2 = '\0';
         encoding = pg_char_to_encoding(ptr1);
         if (encoding < 0)
-            fatal("unrecognized encoding \"%s\"",
-                  ptr1);
+            pg_fatal("unrecognized encoding \"%s\"",
+                     ptr1);
         AH->public.encoding = encoding;
     }
     else
-        fatal("invalid ENCODING item: %s",
-              te->defn);
+        pg_fatal("invalid ENCODING item: %s",
+                 te->defn);

     free(defn);
 }
@@ -2694,8 +2694,8 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
     else if (ptr1 && strncmp(ptr1, "'off'", 5) == 0)
         AH->public.std_strings = false;
     else
-        fatal("invalid STDSTRINGS item: %s",
-              te->defn);
+        pg_fatal("invalid STDSTRINGS item: %s",
+                 te->defn);
 }

 static void
@@ -2719,35 +2719,35 @@ StrictNamesCheck(RestoreOptions *ropt)
     {
         missing_name = simple_string_list_not_touched(&ropt->schemaNames);
         if (missing_name != NULL)
-            fatal("schema \"%s\" not found", missing_name);
+            pg_fatal("schema \"%s\" not found", missing_name);
     }

     if (ropt->tableNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->tableNames);
         if (missing_name != NULL)
-            fatal("table \"%s\" not found", missing_name);
+            pg_fatal("table \"%s\" not found", missing_name);
     }

     if (ropt->indexNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->indexNames);
         if (missing_name != NULL)
-            fatal("index \"%s\" not found", missing_name);
+            pg_fatal("index \"%s\" not found", missing_name);
     }

     if (ropt->functionNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->functionNames);
         if (missing_name != NULL)
-            fatal("function \"%s\" not found", missing_name);
+            pg_fatal("function \"%s\" not found", missing_name);
     }

     if (ropt->triggerNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->triggerNames);
         if (missing_name != NULL)
-            fatal("trigger \"%s\" not found", missing_name);
+            pg_fatal("trigger \"%s\" not found", missing_name);
     }
 }

@@ -3140,8 +3140,8 @@ _doSetSessionAuth(ArchiveHandle *AH, const char *user)

         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
             /* NOT warn_or_exit_horribly... use -O instead to skip this. */
-            fatal("could not set session user to \"%s\": %s",
-                  user, PQerrorMessage(AH->connection));
+            pg_fatal("could not set session user to \"%s\": %s",
+                     user, PQerrorMessage(AH->connection));

         PQclear(res);
     }
@@ -3751,7 +3751,7 @@ ReadHead(ArchiveHandle *AH)
         AH->ReadBufPtr(AH, tmpMag, 5);

         if (strncmp(tmpMag, "PGDMP", 5) != 0)
-            fatal("did not find magic string in file header");
+            pg_fatal("did not find magic string in file header");
     }

     vmaj = AH->ReadBytePtr(AH);
@@ -3765,13 +3765,13 @@ ReadHead(ArchiveHandle *AH)
     AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);

     if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
-        fatal("unsupported version (%d.%d) in file header",
-              vmaj, vmin);
+        pg_fatal("unsupported version (%d.%d) in file header",
+                 vmaj, vmin);

     AH->intSize = AH->ReadBytePtr(AH);
     if (AH->intSize > 32)
-        fatal("sanity check on integer size (%lu) failed",
-              (unsigned long) AH->intSize);
+        pg_fatal("sanity check on integer size (%lu) failed",
+                 (unsigned long) AH->intSize);

     if (AH->intSize > sizeof(int))
         pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
@@ -3784,8 +3784,8 @@ ReadHead(ArchiveHandle *AH)
     fmt = AH->ReadBytePtr(AH);

     if (AH->format != fmt)
-        fatal("expected format (%d) differs from format found in file (%d)",
-              AH->format, fmt);
+        pg_fatal("expected format (%d) differs from format found in file (%d)",
+                 AH->format, fmt);

     if (AH->version >= K_VERS_1_2)
     {
@@ -4455,8 +4455,8 @@ mark_restore_job_done(ArchiveHandle *AH,
     else if (status == WORKER_IGNORED_ERRORS)
         AH->public.n_errors++;
     else if (status != 0)
-        fatal("worker process failed: exit code %d",
-              status);
+        pg_fatal("worker process failed: exit code %d",
+                 status);

     reduce_dependencies(AH, te, ready_list);
 }
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 540d4f6a83..084cd87e8d 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -121,14 +121,14 @@ struct ParallelState;
 #define READ_ERROR_EXIT(fd) \
     do { \
         if (feof(fd)) \
-            fatal("could not read from input file: end of file"); \
+            pg_fatal("could not read from input file: end of file"); \
         else \
-            fatal("could not read from input file: %m"); \
+            pg_fatal("could not read from input file: %m"); \
     } while (0)

 #define WRITE_ERROR_EXIT \
     do { \
-        fatal("could not write to output file: %m"); \
+        pg_fatal("could not write to output file: %m"); \
     } while (0)

 typedef enum T_Action
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..c3b9c365d5 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -153,13 +153,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
         {
             AH->FH = fopen(AH->fSpec, PG_BINARY_W);
             if (!AH->FH)
-                fatal("could not open output file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open output file \"%s\": %m", AH->fSpec);
         }
         else
         {
             AH->FH = stdout;
             if (!AH->FH)
-                fatal("could not open output file: %m");
+                pg_fatal("could not open output file: %m");
         }

         ctx->hasSeek = checkSeek(AH->FH);
@@ -170,13 +170,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
         {
             AH->FH = fopen(AH->fSpec, PG_BINARY_R);
             if (!AH->FH)
-                fatal("could not open input file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
         }
         else
         {
             AH->FH = stdin;
             if (!AH->FH)
-                fatal("could not open input file: %m");
+                pg_fatal("could not open input file: %m");
         }

         ctx->hasSeek = checkSeek(AH->FH);
@@ -373,7 +373,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     lclContext *ctx = (lclContext *) AH->formatData;

     if (oid == 0)
-        fatal("invalid OID for large object");
+        pg_fatal("invalid OID for large object");

     WriteInt(AH, oid);

@@ -436,7 +436,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
         if (ctx->hasSeek)
         {
             if (fseeko(AH->FH, ctx->lastFilePos, SEEK_SET) != 0)
-                fatal("error during file seek: %m");
+                pg_fatal("error during file seek: %m");
         }

         for (;;)
@@ -492,8 +492,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
                     break;

                 default:        /* Always have a default */
-                    fatal("unrecognized data block type (%d) while searching archive",
-                          blkType);
+                    pg_fatal("unrecognized data block type (%d) while searching archive",
+                             blkType);
                     break;
             }
         }
@@ -502,7 +502,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     {
         /* We can just seek to the place we need to be. */
         if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
-            fatal("error during file seek: %m");
+            pg_fatal("error during file seek: %m");

         _readBlockHeader(AH, &blkType, &id);
     }
@@ -514,20 +514,20 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     if (blkType == EOF)
     {
         if (!ctx->hasSeek)
-            fatal("could not find block ID %d in archive -- "
-                  "possibly due to out-of-order restore request, "
-                  "which cannot be handled due to non-seekable input file",
-                  te->dumpId);
+            pg_fatal("could not find block ID %d in archive -- "
+                     "possibly due to out-of-order restore request, "
+                     "which cannot be handled due to non-seekable input file",
+                     te->dumpId);
         else
-            fatal("could not find block ID %d in archive -- "
-                  "possibly corrupt archive",
-                  te->dumpId);
+            pg_fatal("could not find block ID %d in archive -- "
+                     "possibly corrupt archive",
+                     te->dumpId);
     }

     /* Are we sane? */
     if (id != te->dumpId)
-        fatal("found unexpected block ID (%d) when reading data -- expected %d",
-              id, te->dumpId);
+        pg_fatal("found unexpected block ID (%d) when reading data -- expected %d",
+                 id, te->dumpId);

     switch (blkType)
     {
@@ -540,8 +540,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
             break;

         default:                /* Always have a default */
-            fatal("unrecognized data block type %d while restoring archive",
-                  blkType);
+            pg_fatal("unrecognized data block type %d while restoring archive",
+                     blkType);
             break;
     }

@@ -626,7 +626,7 @@ _skipData(ArchiveHandle *AH)
         if (ctx->hasSeek)
         {
             if (fseeko(AH->FH, blkLen, SEEK_CUR) != 0)
-                fatal("error during file seek: %m");
+                pg_fatal("error during file seek: %m");
         }
         else
         {
@@ -640,9 +640,9 @@ _skipData(ArchiveHandle *AH)
             if (fread(buf, 1, blkLen, AH->FH) != blkLen)
             {
                 if (feof(AH->FH))
-                    fatal("could not read from input file: end of file");
+                    pg_fatal("could not read from input file: end of file");
                 else
-                    fatal("could not read from input file: %m");
+                    pg_fatal("could not read from input file: %m");
             }
         }

@@ -743,7 +743,7 @@ _CloseArchive(ArchiveHandle *AH)
         /* Remember TOC's seek position for use below */
         tpos = ftello(AH->FH);
         if (tpos < 0 && ctx->hasSeek)
-            fatal("could not determine seek position in archive file: %m");
+            pg_fatal("could not determine seek position in archive file: %m");
         WriteToc(AH);
         WriteDataChunks(AH, NULL);

@@ -759,7 +759,7 @@ _CloseArchive(ArchiveHandle *AH)
     }

     if (fclose(AH->FH) != 0)
-        fatal("could not close archive file: %m");
+        pg_fatal("could not close archive file: %m");

     /* Sync the output file if one is defined */
     if (AH->dosync && AH->mode == archModeWrite && AH->fSpec)
@@ -782,32 +782,32 @@ _ReopenArchive(ArchiveHandle *AH)
     pgoff_t        tpos;

     if (AH->mode == archModeWrite)
-        fatal("can only reopen input archives");
+        pg_fatal("can only reopen input archives");

     /*
      * These two cases are user-facing errors since they represent unsupported
      * (but not invalid) use-cases.  Word the error messages appropriately.
      */
     if (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)
-        fatal("parallel restore from standard input is not supported");
+        pg_fatal("parallel restore from standard input is not supported");
     if (!ctx->hasSeek)
-        fatal("parallel restore from non-seekable file is not supported");
+        pg_fatal("parallel restore from non-seekable file is not supported");

     tpos = ftello(AH->FH);
     if (tpos < 0)
-        fatal("could not determine seek position in archive file: %m");
+        pg_fatal("could not determine seek position in archive file: %m");

 #ifndef WIN32
     if (fclose(AH->FH) != 0)
-        fatal("could not close archive file: %m");
+        pg_fatal("could not close archive file: %m");
 #endif

     AH->FH = fopen(AH->fSpec, PG_BINARY_R);
     if (!AH->FH)
-        fatal("could not open input file \"%s\": %m", AH->fSpec);
+        pg_fatal("could not open input file \"%s\": %m", AH->fSpec);

     if (fseeko(AH->FH, tpos, SEEK_SET) != 0)
-        fatal("could not set seek position in archive file: %m");
+        pg_fatal("could not set seek position in archive file: %m");
 }

 /*
@@ -862,7 +862,7 @@ _PrepParallelRestore(ArchiveHandle *AH)
         pgoff_t        endpos;

         if (fseeko(AH->FH, 0, SEEK_END) != 0)
-            fatal("error during file seek: %m");
+            pg_fatal("error during file seek: %m");
         endpos = ftello(AH->FH);
         if (endpos > prev_tctx->dataPos)
             prev_te->dataLength = endpos - prev_tctx->dataPos;
@@ -886,7 +886,7 @@ _Clone(ArchiveHandle *AH)

     /* sanity check, shouldn't happen */
     if (ctx->cs != NULL)
-        fatal("compressor active");
+        pg_fatal("compressor active");

     /*
      * We intentionally do not clone TOC-entry-local state: it's useful to
@@ -940,7 +940,7 @@ _getFilePos(ArchiveHandle *AH, lclContext *ctx)
     {
         /* Not expected if we found we can seek. */
         if (ctx->hasSeek)
-            fatal("could not determine seek position in archive file: %m");
+            pg_fatal("could not determine seek position in archive file: %m");
     }
     return pos;
 }
@@ -956,7 +956,7 @@ _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
     int            byt;

     /*
-     * Note: if we are at EOF with a pre-1.3 input file, we'll fatal() inside
+     * Note: if we are at EOF with a pre-1.3 input file, we'll pg_fatal() inside
      * ReadInt rather than returning EOF.  It doesn't seem worth jumping
      * through hoops to deal with that case better, because no such files are
      * likely to exist in the wild: only some 7.1 development versions of
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 3184eda3e7..89cdbf80e0 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -39,7 +39,7 @@ _check_database_version(ArchiveHandle *AH)
     remoteversion_str = PQparameterStatus(AH->connection, "server_version");
     remoteversion = PQserverVersion(AH->connection);
     if (remoteversion == 0 || !remoteversion_str)
-        fatal("could not get server_version from libpq");
+        pg_fatal("could not get server_version from libpq");

     AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
     AH->public.remoteVersion = remoteversion;
@@ -50,9 +50,10 @@ _check_database_version(ArchiveHandle *AH)
         && (remoteversion < AH->public.minRemoteVersion ||
             remoteversion > AH->public.maxRemoteVersion))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
-        fatal("aborting because of server version mismatch");
+        pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
+        exit(1);
     }

     /*
@@ -116,7 +117,7 @@ ConnectDatabase(Archive *AHX,
     bool        new_pass;

     if (AH->connection)
-        fatal("already connected to a database");
+        pg_fatal("already connected to a database");

     /* Never prompt for a password during a reconnection */
     prompt_password = isReconnect ? TRI_NO : cparams->promptPassword;
@@ -166,7 +167,7 @@ ConnectDatabase(Archive *AHX,
         AH->connection = PQconnectdbParams(keywords, values, true);

         if (!AH->connection)
-            fatal("could not connect to database");
+            pg_fatal("could not connect to database");

         if (PQstatus(AH->connection) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(AH->connection) &&
@@ -183,11 +184,11 @@ ConnectDatabase(Archive *AHX,
     if (PQstatus(AH->connection) == CONNECTION_BAD)
     {
         if (isReconnect)
-            fatal("reconnection failed: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("reconnection failed: %s",
+                     PQerrorMessage(AH->connection));
         else
-            fatal("%s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("%s",
+                     PQerrorMessage(AH->connection));
     }

     /* Start strict; later phases may override this. */
@@ -235,7 +236,7 @@ DisconnectDatabase(Archive *AHX)
         /*
          * If we have an active query, send a cancel before closing, ignoring
          * any errors.  This is of no use for a normal exit, but might be
-         * helpful during fatal().
+         * helpful during pg_fatal().
          */
         if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
             (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
@@ -261,16 +262,17 @@ GetConnection(Archive *AHX)
 static void
 notice_processor(void *arg, const char *message)
 {
-    pg_log_generic(PG_LOG_INFO, "%s", message);
+    pg_log_info("%s", message);
 }

-/* Like fatal(), but with a complaint about a particular query. */
+/* Like pg_fatal(), but with a complaint about a particular query. */
 static void
 die_on_query_failure(ArchiveHandle *AH, const char *query)
 {
     pg_log_error("query failed: %s",
                  PQerrorMessage(AH->connection));
-    fatal("query was: %s", query);
+    pg_log_error_detail("Query was: %s", query);
+    exit(1);
 }

 void
@@ -311,10 +313,10 @@ ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
     /* Expecting a single result only */
     ntups = PQntuples(res);
     if (ntups != 1)
-        fatal(ngettext("query returned %d row instead of one: %s",
-                       "query returned %d rows instead of one: %s",
-                       ntups),
-              ntups, query);
+        pg_fatal(ngettext("query returned %d row instead of one: %s",
+                          "query returned %d rows instead of one: %s",
+                          ntups),
+                 ntups, query);

     return res;
 }
@@ -456,8 +458,8 @@ ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
          */
         if (AH->pgCopyIn &&
             PQputCopyData(AH->connection, buf, bufLen) <= 0)
-            fatal("error returned by PQputCopyData: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("error returned by PQputCopyData: %s",
+                     PQerrorMessage(AH->connection));
     }
     else if (AH->outputKind == OUTPUT_OTHERDATA)
     {
@@ -505,8 +507,8 @@ EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
         PGresult   *res;

         if (PQputCopyEnd(AH->connection, NULL) <= 0)
-            fatal("error returned by PQputCopyEnd: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("error returned by PQputCopyEnd: %s",
+                     PQerrorMessage(AH->connection));

         /* Check command status and return to normal libpq state */
         res = PQgetResult(AH->connection);
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 7f4e340dea..3f46f7988a 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -153,7 +153,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
      */

     if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
-        fatal("no output directory specified");
+        pg_fatal("no output directory specified");

     ctx->directory = AH->fSpec;

@@ -182,18 +182,18 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
                 }

                 if (errno)
-                    fatal("could not read directory \"%s\": %m",
-                          ctx->directory);
+                    pg_fatal("could not read directory \"%s\": %m",
+                             ctx->directory);

                 if (closedir(dir))
-                    fatal("could not close directory \"%s\": %m",
-                          ctx->directory);
+                    pg_fatal("could not close directory \"%s\": %m",
+                             ctx->directory);
             }
         }

         if (!is_empty && mkdir(ctx->directory, 0700) < 0)
-            fatal("could not create directory \"%s\": %m",
-                  ctx->directory);
+            pg_fatal("could not create directory \"%s\": %m",
+                     ctx->directory);
     }
     else
     {                            /* Read Mode */
@@ -204,7 +204,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)

         tocFH = cfopen_read(fname, PG_BINARY_R);
         if (tocFH == NULL)
-            fatal("could not open input file \"%s\": %m", fname);
+            pg_fatal("could not open input file \"%s\": %m", fname);

         ctx->dataFH = tocFH;

@@ -219,7 +219,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)

         /* Nothing else in the file, so close it again... */
         if (cfclose(tocFH) != 0)
-            fatal("could not close TOC file: %m");
+            pg_fatal("could not close TOC file: %m");
         ctx->dataFH = NULL;
     }
 }
@@ -329,7 +329,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)

     ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
     if (ctx->dataFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -352,8 +352,8 @@ _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }
 }

@@ -370,7 +370,7 @@ _EndData(ArchiveHandle *AH, TocEntry *te)

     /* Close the file */
     if (cfclose(ctx->dataFH) != 0)
-        fatal("could not close data file: %m");
+        pg_fatal("could not close data file: %m");

     ctx->dataFH = NULL;
 }
@@ -392,7 +392,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
     cfp = cfopen_read(filename, PG_BINARY_R);

     if (!cfp)
-        fatal("could not open input file \"%s\": %m", filename);
+        pg_fatal("could not open input file \"%s\": %m", filename);

     buf = pg_malloc(ZLIB_OUT_SIZE);
     buflen = ZLIB_OUT_SIZE;
@@ -404,7 +404,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)

     free(buf);
     if (cfclose(cfp) != 0)
-        fatal("could not close data file \"%s\": %m", filename);
+        pg_fatal("could not close data file \"%s\": %m", filename);
 }

 /*
@@ -444,8 +444,8 @@ _LoadBlobs(ArchiveHandle *AH)
     ctx->blobsTocFH = cfopen_read(tocfname, PG_BINARY_R);

     if (ctx->blobsTocFH == NULL)
-        fatal("could not open large object TOC file \"%s\" for input: %m",
-              tocfname);
+        pg_fatal("could not open large object TOC file \"%s\" for input: %m",
+                 tocfname);

     /* Read the blobs TOC file line-by-line, and process each blob */
     while ((cfgets(ctx->blobsTocFH, line, MAXPGPATH)) != NULL)
@@ -455,8 +455,8 @@ _LoadBlobs(ArchiveHandle *AH)

         /* Can't overflow because line and blobfname are the same length */
         if (sscanf(line, "%u %" CppAsString2(MAXPGPATH) "s\n", &oid, blobfname) != 2)
-            fatal("invalid line in large object TOC file \"%s\": \"%s\"",
-                  tocfname, line);
+            pg_fatal("invalid line in large object TOC file \"%s\": \"%s\"",
+                     tocfname, line);

         StartRestoreBlob(AH, oid, AH->public.ropt->dropSchema);
         snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, blobfname);
@@ -464,12 +464,12 @@ _LoadBlobs(ArchiveHandle *AH)
         EndRestoreBlob(AH, oid);
     }
     if (!cfeof(ctx->blobsTocFH))
-        fatal("error reading large object TOC file \"%s\"",
-              tocfname);
+        pg_fatal("error reading large object TOC file \"%s\"",
+                 tocfname);

     if (cfclose(ctx->blobsTocFH) != 0)
-        fatal("could not close large object TOC file \"%s\": %m",
-              tocfname);
+        pg_fatal("could not close large object TOC file \"%s\": %m",
+                 tocfname);

     ctx->blobsTocFH = NULL;

@@ -494,8 +494,8 @@ _WriteByte(ArchiveHandle *AH, const int i)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }

     return 1;
@@ -530,8 +530,8 @@ _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }
 }

@@ -550,7 +550,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
      * exit on short reads.
      */
     if (cfread(buf, len, ctx->dataFH) != len)
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");
 }

 /*
@@ -583,7 +583,7 @@ _CloseArchive(ArchiveHandle *AH)
         /* The TOC is always created uncompressed */
         tocFH = cfopen_write(fname, PG_BINARY_W, 0);
         if (tocFH == NULL)
-            fatal("could not open output file \"%s\": %m", fname);
+            pg_fatal("could not open output file \"%s\": %m", fname);
         ctx->dataFH = tocFH;

         /*
@@ -596,7 +596,7 @@ _CloseArchive(ArchiveHandle *AH)
         AH->format = archDirectory;
         WriteToc(AH);
         if (cfclose(tocFH) != 0)
-            fatal("could not close TOC file: %m");
+            pg_fatal("could not close TOC file: %m");
         WriteDataChunks(AH, ctx->pstate);

         ParallelBackupEnd(AH, ctx->pstate);
@@ -646,7 +646,7 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
     /* The blob TOC file is never compressed */
     ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
     if (ctx->blobsTocFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -665,7 +665,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);

     if (ctx->dataFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -682,13 +682,13 @@ _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)

     /* Close the BLOB data file itself */
     if (cfclose(ctx->dataFH) != 0)
-        fatal("could not close blob data file: %m");
+        pg_fatal("could not close blob data file: %m");
     ctx->dataFH = NULL;

     /* register the blob in blobs.toc */
     len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
     if (cfwrite(buf, len, ctx->blobsTocFH) != len)
-        fatal("could not write to blobs TOC file");
+        pg_fatal("could not write to blobs TOC file");
 }

 /*
@@ -702,7 +702,7 @@ _EndBlobs(ArchiveHandle *AH, TocEntry *te)
     lclContext *ctx = (lclContext *) AH->formatData;

     if (cfclose(ctx->blobsTocFH) != 0)
-        fatal("could not close blobs TOC file: %m");
+        pg_fatal("could not close blobs TOC file: %m");
     ctx->blobsTocFH = NULL;
 }

@@ -721,7 +721,7 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
     dname = ctx->directory;

     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
-        fatal("file name too long: \"%s\"", dname);
+        pg_fatal("file name too long: \"%s\"", dname);

     strcpy(buf, dname);
     strcat(buf, "/");
diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c
index 0458979f3c..541306d991 100644
--- a/src/bin/pg_dump/pg_backup_null.c
+++ b/src/bin/pg_dump/pg_backup_null.c
@@ -71,7 +71,7 @@ InitArchiveFmt_Null(ArchiveHandle *AH)
      * Now prevent reading...
      */
     if (AH->mode == archModeRead)
-        fatal("this format cannot be read");
+        pg_fatal("this format cannot be read");
 }

 /*
@@ -144,7 +144,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     bool        old_blob_style = (AH->version < K_VERS_1_12);

     if (oid == 0)
-        fatal("invalid OID for large object");
+        pg_fatal("invalid OID for large object");

     /* With an old archive we must do drop and create logic here */
     if (old_blob_style && AH->public.ropt->dropSchema)
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 5c351acda0..dd83222f16 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -174,14 +174,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
         {
             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file \"%s\" for output: %m",
-                      AH->fSpec);
+                pg_fatal("could not open TOC file \"%s\" for output: %m",
+                         AH->fSpec);
         }
         else
         {
             ctx->tarFH = stdout;
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file for output: %m");
+                pg_fatal("could not open TOC file for output: %m");
         }

         ctx->tarFHpos = 0;
@@ -200,7 +200,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
          * positioning.
          */
         if (AH->compression != 0)
-            fatal("compression is not supported by tar archive format");
+            pg_fatal("compression is not supported by tar archive format");
     }
     else
     {                            /* Read Mode */
@@ -208,14 +208,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
         {
             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file \"%s\" for input: %m",
-                      AH->fSpec);
+                pg_fatal("could not open TOC file \"%s\" for input: %m",
+                         AH->fSpec);
         }
         else
         {
             ctx->tarFH = stdin;
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file for input: %m");
+                pg_fatal("could not open TOC file for input: %m");
         }

         /*
@@ -335,7 +335,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
                  * Couldn't find the requested file. Future: do SEEK(0) and
                  * retry.
                  */
-                fatal("could not find file \"%s\" in archive", filename);
+                pg_fatal("could not find file \"%s\" in archive", filename);
             }
             else
             {
@@ -349,7 +349,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
         if (AH->compression == 0)
             tm->nFH = ctx->tarFH;
         else
-            fatal("compression is not supported by tar archive format");
+            pg_fatal("compression is not supported by tar archive format");
         /* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
 #else
         tm->nFH = ctx->tarFH;
@@ -401,7 +401,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 #endif

         if (tm->tmpFH == NULL)
-            fatal("could not generate temporary file name: %m");
+            pg_fatal("could not generate temporary file name: %m");

         umask(old_umask);

@@ -412,7 +412,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
             sprintf(fmode, "wb%d", AH->compression);
             tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
             if (tm->zFH == NULL)
-                fatal("could not open temporary file");
+                pg_fatal("could not open temporary file");
         }
         else
             tm->nFH = tm->tmpFH;
@@ -441,7 +441,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
     {
         errno = 0;                /* in case gzclose() doesn't set it */
         if (GZCLOSE(th->zFH) != 0)
-            fatal("could not close tar member: %m");
+            pg_fatal("could not close tar member: %m");
     }

     if (th->mode == 'w')
@@ -551,11 +551,11 @@ _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
                     int            errnum;
                     const char *errmsg = gzerror(th->zFH, &errnum);

-                    fatal("could not read from input file: %s",
-                          errnum == Z_ERRNO ? strerror(errno) : errmsg);
+                    pg_fatal("could not read from input file: %s",
+                             errnum == Z_ERRNO ? strerror(errno) : errmsg);
 #else
-                    fatal("could not read from input file: %s",
-                          strerror(errno));
+                    pg_fatal("could not read from input file: %s",
+                             strerror(errno));
 #endif
                 }
             }
@@ -685,8 +685,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
             pos1 = (int) strlen(te->copyStmt) - 13;
             if (pos1 < 6 || strncmp(te->copyStmt, "COPY ", 5) != 0 ||
                 strcmp(te->copyStmt + pos1, " FROM stdin;\n") != 0)
-                fatal("unexpected COPY statement syntax: \"%s\"",
-                      te->copyStmt);
+                pg_fatal("unexpected COPY statement syntax: \"%s\"",
+                         te->copyStmt);

             /* Emit all but the FROM part ... */
             ahwrite(te->copyStmt, 1, pos1, AH);
@@ -787,7 +787,7 @@ _ReadByte(ArchiveHandle *AH)
     res = tarRead(&c, 1, ctx->FH);
     if (res != 1)
         /* We already would have exited for errors on reads, must be EOF */
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");
     ctx->filePos += 1;
     return c;
 }
@@ -810,7 +810,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)

     if (tarRead(buf, len, ctx->FH) != len)
         /* We already would have exited for errors on reads, must be EOF */
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");

     ctx->filePos += len;
 }
@@ -952,7 +952,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     char       *sfx;

     if (oid == 0)
-        fatal("invalid OID for large object (%u)", oid);
+        pg_fatal("invalid OID for large object (%u)", oid);

     if (AH->compression != 0)
         sfx = ".gz";
@@ -1080,12 +1080,12 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
      * Find file len & go back to start.
      */
     if (fseeko(tmp, 0, SEEK_END) != 0)
-        fatal("error during file seek: %m");
+        pg_fatal("error during file seek: %m");
     th->fileLen = ftello(tmp);
     if (th->fileLen < 0)
-        fatal("could not determine seek position in archive file: %m");
+        pg_fatal("could not determine seek position in archive file: %m");
     if (fseeko(tmp, 0, SEEK_SET) != 0)
-        fatal("error during file seek: %m");
+        pg_fatal("error during file seek: %m");

     _tarWriteHeader(th);

@@ -1099,7 +1099,7 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
         READ_ERROR_EXIT(tmp);

     if (fclose(tmp) != 0)        /* This *should* delete it... */
-        fatal("could not close temporary file: %m");
+        pg_fatal("could not close temporary file: %m");

     if (len != th->fileLen)
     {
@@ -1108,8 +1108,8 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)

         snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) len);
         snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) th->fileLen);
-        fatal("actual file length (%s) does not match expected (%s)",
-              buf1, buf2);
+        pg_fatal("actual file length (%s) does not match expected (%s)",
+                 buf1, buf2);
     }

     pad = tarPaddingBytesRequired(len);
@@ -1165,7 +1165,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
     if (!_tarGetHeader(AH, th))
     {
         if (filename)
-            fatal("could not find header for file \"%s\" in tar archive", filename);
+            pg_fatal("could not find header for file \"%s\" in tar archive", filename);
         else
         {
             /*
@@ -1183,9 +1183,9 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)

         id = atoi(th->targetFile);
         if ((TocIDRequired(AH, id) & REQ_DATA) != 0)
-            fatal("restoring data out of order is not supported in this archive format: "
-                  "\"%s\" is required, but comes before \"%s\" in the archive file.",
-                  th->targetFile, filename);
+            pg_fatal("restoring data out of order is not supported in this archive format: "
+                     "\"%s\" is required, but comes before \"%s\" in the archive file.",
+                     th->targetFile, filename);

         /* Header doesn't match, so read to next header */
         len = th->fileLen;
@@ -1196,7 +1196,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
             _tarReadRaw(AH, &header[0], TAR_BLOCK_SIZE, NULL, ctx->tarFH);

         if (!_tarGetHeader(AH, th))
-            fatal("could not find header for file \"%s\" in tar archive", filename);
+            pg_fatal("could not find header for file \"%s\" in tar archive", filename);
     }

     ctx->tarNextMember = ctx->tarFHpos + th->fileLen
@@ -1230,10 +1230,10 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
             return 0;

         if (len != TAR_BLOCK_SIZE)
-            fatal(ngettext("incomplete tar header found (%lu byte)",
-                           "incomplete tar header found (%lu bytes)",
-                           len),
-                  (unsigned long) len);
+            pg_fatal(ngettext("incomplete tar header found (%lu byte)",
+                              "incomplete tar header found (%lu bytes)",
+                              len),
+                     (unsigned long) len);

         /* Calc checksum */
         chk = tarChecksum(h);
@@ -1281,8 +1281,8 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)

         snprintf(posbuf, sizeof(posbuf), UINT64_FORMAT,
                  (uint64) ftello(ctx->tarFH));
-        fatal("corrupt tar header found in %s (expected %d, computed %d) file position %s",
-              tag, sum, chk, posbuf);
+        pg_fatal("corrupt tar header found in %s (expected %d, computed %d) file position %s",
+                 tag, sum, chk, posbuf);
     }

     th->targetFile = pg_strdup(tag);
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 57140a5504..e40890cb26 100644
--- a/src/bin/pg_dump/pg_backup_utils.c
+++ b/src/bin/pg_dump/pg_backup_utils.c
@@ -52,8 +52,7 @@ set_dump_section(const char *arg, int *dumpSections)
     else
     {
         pg_log_error("unrecognized section name: \"%s\"", arg);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }
 }
@@ -64,10 +63,7 @@ void
 on_exit_nicely(on_exit_nicely_callback function, void *arg)
 {
     if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
-    {
-        pg_log_fatal("out of on_exit_nicely slots");
-        exit_nicely(1);
-    }
+        pg_fatal("out of on_exit_nicely slots");
     on_exit_nicely_list[on_exit_nicely_index].function = function;
     on_exit_nicely_list[on_exit_nicely_index].arg = arg;
     on_exit_nicely_index++;
diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index 6ebc3afee4..5b1c51554d 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -31,6 +31,12 @@ extern void set_dump_section(const char *arg, int *dumpSections);
 extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);
 extern void exit_nicely(int code) pg_attribute_noreturn();

-#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)
+/* In pg_dump, we modify pg_fatal to call exit_nicely instead of exit */
+#undef pg_fatal
+#define pg_fatal(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit_nicely(1); \
+    } while(0)

 #endif                            /* PG_BACKUP_UTILS_H */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e69dcf8a48..0e83def6b4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -620,7 +620,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -637,8 +638,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -655,32 +655,26 @@ main(int argc, char **argv)
         dopt.sequence_data = 1;

     if (dopt.dataOnly && dopt.schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
-        fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
+        pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");

     if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
-        fatal("option --include-foreign-data is not supported with parallel backup");
+        pg_fatal("option --include-foreign-data is not supported with parallel backup");

     if (dopt.dataOnly && dopt.outputClean)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -c/--clean and -a/--data-only cannot be used together");

     if (dopt.if_exists && !dopt.outputClean)
-        fatal("option --if-exists requires option -c/--clean");
+        pg_fatal("option --if-exists requires option -c/--clean");

     /*
      * --inserts are already implied above if --column-inserts or
      * --rows-per-insert were specified.
      */
     if (dopt.do_nothing && dopt.dump_inserts == 0)
-        fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
+        pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");

     /* Identify archive format to emit */
     archiveFormat = parseArchiveFormat(format, &archiveMode);
@@ -715,7 +709,7 @@ main(int argc, char **argv)

     /* Parallel backup only in the directory archive format so far */
     if (archiveFormat != archDirectory && numWorkers > 1)
-        fatal("parallel backup only supported by the directory format");
+        pg_fatal("parallel backup only supported by the directory format");

     /* Open the output file */
     fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
@@ -770,7 +764,7 @@ main(int argc, char **argv)
                                     &schema_include_oids,
                                     strict_names);
         if (schema_include_oids.head == NULL)
-            fatal("no matching schemas were found");
+            pg_fatal("no matching schemas were found");
     }
     expand_schema_name_patterns(fout, &schema_exclude_patterns,
                                 &schema_exclude_oids,
@@ -784,7 +778,7 @@ main(int argc, char **argv)
                                    &table_include_oids,
                                    strict_names);
         if (table_include_oids.head == NULL)
-            fatal("no matching tables were found");
+            pg_fatal("no matching tables were found");
     }
     expand_table_name_patterns(fout, &table_exclude_patterns,
                                &table_exclude_oids,
@@ -806,7 +800,7 @@ main(int argc, char **argv)
                                        &extension_include_oids,
                                        strict_names);
         if (extension_include_oids.head == NULL)
-            fatal("no matching extensions were found");
+            pg_fatal("no matching extensions were found");
     }

     /*
@@ -1087,8 +1081,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
     if (dumpencoding)
     {
         if (PQsetClientEncoding(conn, dumpencoding) < 0)
-            fatal("invalid client encoding \"%s\" specified",
-                  dumpencoding);
+            pg_fatal("invalid client encoding \"%s\" specified",
+                     dumpencoding);
     }

     /*
@@ -1225,7 +1219,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
     else if (AH->numWorkers > 1)
     {
         if (AH->isStandby && AH->remoteVersion < 100000)
-            fatal("parallel dumps from standby servers are not supported by this server version");
+            pg_fatal("parallel dumps from standby servers are not supported by this server version");
         AH->sync_snapshot_id = get_synchronized_snapshot(AH);
     }
 }
@@ -1290,7 +1284,7 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
     else if (pg_strcasecmp(format, "tar") == 0)
         archiveFormat = archTar;
     else
-        fatal("invalid output format \"%s\" specified", format);
+        pg_fatal("invalid output format \"%s\" specified", format);
     return archiveFormat;
 }

@@ -1328,7 +1322,7 @@ expand_schema_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching schemas were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -1375,7 +1369,7 @@ expand_extension_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching extensions were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -1422,7 +1416,7 @@ expand_foreign_server_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (PQntuples(res) == 0)
-            fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
             simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
@@ -1485,7 +1479,7 @@ expand_table_name_patterns(Archive *fout,
         PQclear(ExecuteSqlQueryForSingleRow(fout,
                                             ALWAYS_SECURE_SEARCH_PATH_SQL));
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching tables were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -2033,8 +2027,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     {
         /* copy data transfer failed */
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("Command was: %s", q->data);
         exit_nicely(1);
     }

@@ -2043,8 +2037,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("Command was: %s", q->data);
         exit_nicely(1);
     }
     PQclear(res);
@@ -2124,8 +2118,8 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
         /* cross-check field count, allowing for dummy NULL if any */
         if (nfields != PQnfields(res) &&
             !(nfields == 0 && PQnfields(res) == 1))
-            fatal("wrong number of fields retrieved from table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("wrong number of fields retrieved from table \"%s\"",
+                     tbinfo->dobj.name);

         /*
          * First time through, we build as much of the INSERT statement as
@@ -3231,7 +3225,7 @@ dumpSearchPath(Archive *AH)
                                       "SELECT pg_catalog.current_schemas(false)");

     if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
-        fatal("could not parse result of current_schemas()");
+        pg_fatal("could not parse result of current_schemas()");

     /*
      * We use set_config(), not a simple "SET search_path" command, because
@@ -3457,8 +3451,8 @@ dumpBlobs(Archive *fout, const void *arg)
             /* Open the BLOB */
             loFd = lo_open(conn, blobOid, INV_READ);
             if (loFd == -1)
-                fatal("could not open large object %u: %s",
-                      blobOid, PQerrorMessage(conn));
+                pg_fatal("could not open large object %u: %s",
+                         blobOid, PQerrorMessage(conn));

             StartBlob(fout, blobOid);

@@ -3467,8 +3461,8 @@ dumpBlobs(Archive *fout, const void *arg)
             {
                 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
                 if (cnt < 0)
-                    fatal("error reading large object %u: %s",
-                          blobOid, PQerrorMessage(conn));
+                    pg_fatal("error reading large object %u: %s",
+                             blobOid, PQerrorMessage(conn));

                 WriteData(fout, buf, cnt);
             } while (cnt > 0);
@@ -3714,11 +3708,8 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
     else if (polinfo->polcmd == 'd')
         cmd = " FOR DELETE";
     else
-    {
-        pg_log_error("unexpected policy command type: %c",
-                     polinfo->polcmd);
-        exit_nicely(1);
-    }
+        pg_fatal("unexpected policy command type: %c",
+                 polinfo->polcmd);

     query = createPQExpBuffer();
     delqry = createPQExpBuffer();
@@ -4437,7 +4428,7 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)

     /* Build list of quoted publications and append them to query. */
     if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
-        fatal("could not parse %s array", "subpublications");
+        pg_fatal("could not parse %s array", "subpublications");

     publications = createPQExpBuffer();
     for (i = 0; i < npubnames; i++)
@@ -4816,8 +4807,8 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
         extobj = NULL;
     }
     if (extobj == NULL)
-        fatal("could not find parent extension for %s %s",
-              objtype, objname);
+        pg_fatal("could not find parent extension for %s %s",
+                 objtype, objname);

     appendPQExpBufferStr(upgrade_buffer,
                          "\n-- For binary upgrade, handle extension membership the hard way\n");
@@ -4961,7 +4952,7 @@ findNamespace(Oid nsoid)

     nsinfo = findNamespaceByOid(nsoid);
     if (nsinfo == NULL)
-        fatal("schema with OID %u does not exist", nsoid);
+        pg_fatal("schema with OID %u does not exist", nsoid);
     return nsinfo;
 }

@@ -6415,8 +6406,8 @@ getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)

         owning_tab = findTableByOid(seqinfo->owning_tab);
         if (owning_tab == NULL)
-            fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-                  seqinfo->owning_tab, seqinfo->dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+                     seqinfo->owning_tab, seqinfo->dobj.catId.oid);

         /*
          * Only dump identity sequences if we're going to dump the table that
@@ -6719,12 +6710,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", indrelid);
+            pg_fatal("unrecognized table OID %u", indrelid);
         /* cross-check that we only got requested tables */
         if (!tbinfo->hasindex ||
             !tbinfo->interesting)
-            fatal("unexpected index data for table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("unexpected index data for table \"%s\"",
+                     tbinfo->dobj.name);

         /* Save data for this table */
         tbinfo->indexes = indxinfo + j;
@@ -6986,7 +6977,7 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
                     break;
             }
             if (curtblindx >= numTables)
-                fatal("unrecognized table OID %u", conrelid);
+                pg_fatal("unrecognized table OID %u", conrelid);
         }

         constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
@@ -7218,8 +7209,8 @@ getRules(Archive *fout, int *numRules)
         ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
         ruleinfo[i].ruletable = findTableByOid(ruletableoid);
         if (ruleinfo[i].ruletable == NULL)
-            fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
-                  ruletableoid, ruleinfo[i].dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
+                     ruletableoid, ruleinfo[i].dobj.catId.oid);
         ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
         ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
         ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
@@ -7457,7 +7448,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", tgrelid);
+            pg_fatal("unrecognized table OID %u", tgrelid);

         /* Save data for this table */
         tbinfo->triggers = tginfo + j;
@@ -7509,10 +7500,10 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
                     if (OidIsValid(tginfo[j].tgconstrrelid))
                     {
                         if (PQgetisnull(res, j, i_tgconstrrelname))
-                            fatal("query produced null referenced table name for foreign key trigger \"%s\" on table
\"%s\"(OID of table: %u)", 
-                                  tginfo[j].dobj.name,
-                                  tbinfo->dobj.name,
-                                  tginfo[j].tgconstrrelid);
+                            pg_fatal("query produced null referenced table name for foreign key trigger \"%s\" on
table\"%s\" (OID of table: %u)", 
+                                     tginfo[j].dobj.name,
+                                     tbinfo->dobj.name,
+                                     tginfo[j].tgconstrrelid);
                         tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
                     }
                     else
@@ -8124,12 +8115,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", attrelid);
+            pg_fatal("unrecognized table OID %u", attrelid);
         /* cross-check that we only got requested tables */
         if (tbinfo->relkind == RELKIND_SEQUENCE ||
             !tbinfo->interesting)
-            fatal("unexpected column data for table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("unexpected column data for table \"%s\"",
+                     tbinfo->dobj.name);

         /* Save data for this table */
         tbinfo->numatts = numatts;
@@ -8158,8 +8149,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
         for (int j = 0; j < numatts; j++, r++)
         {
             if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
-                fatal("invalid column numbering in table \"%s\"",
-                      tbinfo->dobj.name);
+                pg_fatal("invalid column numbering in table \"%s\"",
+                         tbinfo->dobj.name);
             tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
             tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
             tbinfo->atttypmod[j] = atoi(PQgetvalue(res, r, i_atttypmod));
@@ -8245,12 +8236,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                         break;
                 }
                 if (curtblindx >= numTables)
-                    fatal("unrecognized table OID %u", adrelid);
+                    pg_fatal("unrecognized table OID %u", adrelid);
             }

             if (adnum <= 0 || adnum > tbinfo->numatts)
-                fatal("invalid adnum value %d for table \"%s\"",
-                      adnum, tbinfo->dobj.name);
+                pg_fatal("invalid adnum value %d for table \"%s\"",
+                         adnum, tbinfo->dobj.name);

             /*
              * dropped columns shouldn't have defaults, but just in case,
@@ -8399,7 +8390,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                     break;
             }
             if (curtblindx >= numTables)
-                fatal("unrecognized table OID %u", conrelid);
+                pg_fatal("unrecognized table OID %u", conrelid);

             if (numcons != tbinfo->ncheck)
             {
@@ -8407,7 +8398,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                                       "expected %d check constraints on table \"%s\" but found %d",
                                       tbinfo->ncheck),
                              tbinfo->ncheck, tbinfo->dobj.name, numcons);
-                pg_log_error("(The system catalogs might be corrupted.)");
+                pg_log_error_hint("The system catalogs might be corrupted.");
                 exit_nicely(1);
             }

@@ -9097,7 +9088,7 @@ getRoleName(const char *roleoid_str)
         }
     }

-    fatal("role with OID %u does not exist", roleoid);
+    pg_fatal("role with OID %u does not exist", roleoid);
     return NULL;                /* keep compiler quiet */
 }

@@ -11565,7 +11556,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
     if (*proconfig)
     {
         if (!parsePGArray(proconfig, &configitems, &nconfigitems))
-            fatal("could not parse %s array", "proconfig");
+            pg_fatal("could not parse %s array", "proconfig");
     }
     else
     {
@@ -11634,8 +11625,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
         else if (provolatile[0] == PROVOLATILE_STABLE)
             appendPQExpBufferStr(q, " STABLE");
         else if (provolatile[0] != PROVOLATILE_VOLATILE)
-            fatal("unrecognized provolatile value for function \"%s\"",
-                  finfo->dobj.name);
+            pg_fatal("unrecognized provolatile value for function \"%s\"",
+                     finfo->dobj.name);
     }

     if (proisstrict[0] == 't')
@@ -11684,8 +11675,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
         else if (proparallel[0] == PROPARALLEL_RESTRICTED)
             appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
         else if (proparallel[0] != PROPARALLEL_UNSAFE)
-            fatal("unrecognized proparallel value for function \"%s\"",
-                  finfo->dobj.name);
+            pg_fatal("unrecognized proparallel value for function \"%s\"",
+                     finfo->dobj.name);
     }

     for (i = 0; i < nconfigitems; i++)
@@ -11815,8 +11806,8 @@ dumpCast(Archive *fout, const CastInfo *cast)
     {
         funcInfo = findFuncByOid(cast->castfunc);
         if (funcInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  cast->castfunc);
+            pg_fatal("could not find function definition for function with OID %u",
+                     cast->castfunc);
     }

     defqry = createPQExpBuffer();
@@ -11921,15 +11912,15 @@ dumpTransform(Archive *fout, const TransformInfo *transform)
     {
         fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
         if (fromsqlFuncInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  transform->trffromsql);
+            pg_fatal("could not find function definition for function with OID %u",
+                     transform->trffromsql);
     }
     if (OidIsValid(transform->trftosql))
     {
         tosqlFuncInfo = findFuncByOid(transform->trftosql);
         if (tosqlFuncInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  transform->trftosql);
+            pg_fatal("could not find function definition for function with OID %u",
+                     transform->trftosql);
     }

     defqry = createPQExpBuffer();
@@ -12987,8 +12978,8 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
         /* to allow dumping pg_catalog; not accepted on input */
         appendPQExpBufferStr(q, "default");
     else
-        fatal("unrecognized collation provider: %s",
-              collprovider);
+        pg_fatal("unrecognized collation provider: %s",
+                 collprovider);

     if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
         appendPQExpBufferStr(q, ", deterministic = false");
@@ -13394,8 +13385,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
                     appendPQExpBufferStr(details, ",\n    FINALFUNC_MODIFY = READ_WRITE");
                     break;
                 default:
-                    fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
-                          agginfo->aggfn.dobj.name);
+                    pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
+                             agginfo->aggfn.dobj.name);
                     break;
             }
         }
@@ -13450,8 +13441,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
                     appendPQExpBufferStr(details, ",\n    MFINALFUNC_MODIFY = READ_WRITE");
                     break;
                 default:
-                    fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
-                          agginfo->aggfn.dobj.name);
+                    pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
+                             agginfo->aggfn.dobj.name);
                     break;
             }
         }
@@ -13475,8 +13466,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
         else if (proparallel[0] == PROPARALLEL_RESTRICTED)
             appendPQExpBufferStr(details, ",\n    PARALLEL = restricted");
         else if (proparallel[0] != PROPARALLEL_UNSAFE)
-            fatal("unrecognized proparallel value for function \"%s\"",
-                  agginfo->aggfn.dobj.name);
+            pg_fatal("unrecognized proparallel value for function \"%s\"",
+                     agginfo->aggfn.dobj.name);
     }

     appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
@@ -14168,8 +14159,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
             break;
         default:
             /* shouldn't get here */
-            fatal("unrecognized object type in default privileges: %d",
-                  (int) daclinfo->defaclobjtype);
+            pg_fatal("unrecognized object type in default privileges: %d",
+                     (int) daclinfo->defaclobjtype);
             type = "";            /* keep compiler quiet */
     }

@@ -14184,8 +14175,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
                                  daclinfo->defaclrole,
                                  fout->remoteVersion,
                                  q))
-        fatal("could not parse default ACL list (%s)",
-              daclinfo->dacl.acl);
+        pg_fatal("could not parse default ACL list (%s)",
+                 daclinfo->dacl.acl);

     if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
         ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
@@ -14266,8 +14257,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
         if (!buildACLCommands(name, subname, nspname, type,
                               initprivs, acldefault, owner,
                               "", fout->remoteVersion, sql))
-            fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
-                  initprivs, acldefault, name, type);
+            pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
+                     initprivs, acldefault, name, type);
         appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
     }

@@ -14291,8 +14282,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
     if (!buildACLCommands(name, subname, nspname, type,
                           acls, baseacls, owner,
                           "", fout->remoteVersion, sql))
-        fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
-              acls, baseacls, name, type);
+        pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
+                 acls, baseacls, name, type);

     if (sql->len > 0)
     {
@@ -14829,18 +14820,18 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
     if (PQntuples(res) != 1)
     {
         if (PQntuples(res) < 1)
-            fatal("query to obtain definition of view \"%s\" returned no data",
-                  tbinfo->dobj.name);
+            pg_fatal("query to obtain definition of view \"%s\" returned no data",
+                     tbinfo->dobj.name);
         else
-            fatal("query to obtain definition of view \"%s\" returned more than one definition",
-                  tbinfo->dobj.name);
+            pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
+                     tbinfo->dobj.name);
     }

     len = PQgetlength(res, 0, 0);

     if (len == 0)
-        fatal("definition of view \"%s\" appears to be empty (length zero)",
-              tbinfo->dobj.name);
+        pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
+                 tbinfo->dobj.name);

     /* Strip off the trailing semicolon so that other things may follow. */
     Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
@@ -15852,8 +15843,8 @@ getAttrName(int attrnum, const TableInfo *tblInfo)
         case TableOidAttributeNumber:
             return "tableoid";
     }
-    fatal("invalid column number %d for table \"%s\"",
-          attrnum, tblInfo->dobj.name);
+    pg_fatal("invalid column number %d for table \"%s\"",
+             attrnum, tblInfo->dobj.name);
     return NULL;                /* keep compiler quiet */
 }

@@ -15930,11 +15921,11 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
             int            j;

             if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
-                fatal("could not parse index statistic columns");
+                pg_fatal("could not parse index statistic columns");
             if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
-                fatal("could not parse index statistic values");
+                pg_fatal("could not parse index statistic values");
             if (nstatcols != nstatvals)
-                fatal("mismatched number of columns and values for index statistics");
+                pg_fatal("mismatched number of columns and values for index statistics");

             for (j = 0; j < nstatcols; j++)
             {
@@ -16152,8 +16143,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
         indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);

         if (indxinfo == NULL)
-            fatal("missing index for constraint \"%s\"",
-                  coninfo->dobj.name);
+            pg_fatal("missing index for constraint \"%s\"",
+                     coninfo->dobj.name);

         if (dopt->binary_upgrade)
             binary_upgrade_set_pg_class_oids(fout, q,
@@ -16380,8 +16371,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
     }
     else
     {
-        fatal("unrecognized constraint type: %c",
-              coninfo->contype);
+        pg_fatal("unrecognized constraint type: %c",
+                 coninfo->contype);
     }

     /* Dump Constraint Comments --- only works for table constraints */
@@ -16480,13 +16471,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-                              PQntuples(res)),
-                     tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }
+        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+                          PQntuples(res)),
+                 tbinfo->dobj.name, PQntuples(res));

     seqtype = PQgetvalue(res, 0, 0);
     startv = PQgetvalue(res, 0, 1);
@@ -16515,7 +16503,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     }
     else
     {
-        fatal("unrecognized sequence type: %s", seqtype);
+        pg_fatal("unrecognized sequence type: %s", seqtype);
         default_minv = default_maxv = 0;    /* keep compiler quiet */
     }

@@ -16638,8 +16626,8 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
         TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);

         if (owning_tab == NULL)
-            fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-                  tbinfo->owning_tab, tbinfo->dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+                     tbinfo->owning_tab, tbinfo->dobj.catId.oid);

         if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
         {
@@ -16702,13 +16690,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-                              PQntuples(res)),
-                     tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }
+        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+                          PQntuples(res)),
+                 tbinfo->dobj.name, PQntuples(res));

     last = PQgetvalue(res, 0, 0);
     called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
@@ -16797,10 +16782,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
         else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
             appendPQExpBufferStr(query, "INSTEAD OF");
         else
-        {
-            pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
-            exit_nicely(1);
-        }
+            pg_fatal("unexpected tgtype value: %d", tginfo->tgtype);

         findx = 0;
         if (TRIGGER_FOR_INSERT(tginfo->tgtype))
@@ -16872,11 +16854,10 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
             if (p + tlen >= tgargs + lentgargs)
             {
                 /* hm, not found before end of bytea value... */
-                pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
-                             tginfo->tgargs,
-                             tginfo->dobj.name,
-                             tbinfo->dobj.name);
-                exit_nicely(1);
+                pg_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
+                         tginfo->tgargs,
+                         tginfo->dobj.name,
+                         tbinfo->dobj.name);
             }

             if (findx > 0)
@@ -17142,11 +17123,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

         if (PQntuples(res) != 1)
-        {
-            pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
-                         rinfo->dobj.name, tbinfo->dobj.name);
-            exit_nicely(1);
-        }
+            pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+                     rinfo->dobj.name, tbinfo->dobj.name);

         printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

@@ -17384,11 +17362,11 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
             int            j;

             if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
-                fatal("could not parse %s array", "extconfig");
+                pg_fatal("could not parse %s array", "extconfig");
             if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
-                fatal("could not parse %s array", "extcondition");
+                pg_fatal("could not parse %s array", "extcondition");
             if (nconfigitems != nconditionitems)
-                fatal("mismatched number of configurations and conditions for extension");
+                pg_fatal("mismatched number of configurations and conditions for extension");

             for (j = 0; j < nconfigitems; j++)
             {
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index d979f93b3d..7656b26e8f 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -419,13 +419,13 @@ TopoSort(DumpableObject **objs,
         obj = objs[i];
         j = obj->dumpId;
         if (j <= 0 || j > maxDumpId)
-            fatal("invalid dumpId %d", j);
+            pg_fatal("invalid dumpId %d", j);
         idMap[j] = i;
         for (j = 0; j < obj->nDeps; j++)
         {
             k = obj->dependencies[j];
             if (k <= 0 || k > maxDumpId)
-                fatal("invalid dependency %d", k);
+                pg_fatal("invalid dependency %d", k);
             beforeConstraints[k]++;
         }
     }
@@ -658,7 +658,7 @@ findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)

     /* We'd better have fixed at least one loop */
     if (!fixedloop)
-        fatal("could not identify dependency loop");
+        pg_fatal("could not identify dependency loop");

     free(workspace);
     free(searchFailed);
@@ -1233,9 +1233,9 @@ repairDependencyLoop(DumpableObject **loop,
                                 "there are circular foreign-key constraints among these tables:",
                                 nLoop));
         for (i = 0; i < nLoop; i++)
-            pg_log_generic(PG_LOG_INFO, "  %s", loop[i]->name);
-        pg_log_generic(PG_LOG_INFO, "You might not be able to restore the dump without using --disable-triggers or
temporarilydropping the constraints."); 
-        pg_log_generic(PG_LOG_INFO, "Consider using a full dump instead of a --data-only dump to avoid this
problem.");
+            pg_log_info("  %s", loop[i]->name);
+        pg_log_info("You might not be able to restore the dump without using --disable-triggers or temporarily
droppingthe constraints."); 
+        pg_log_info("Consider using a full dump instead of a --data-only dump to avoid this problem.");
         if (nLoop > 1)
             removeObjectDependency(loop[0], loop[1]->dumpId);
         else                    /* must be a self-dependency */
@@ -1253,7 +1253,7 @@ repairDependencyLoop(DumpableObject **loop,
         char        buf[1024];

         describeDumpableObject(loop[i], buf, sizeof(buf));
-        pg_log_generic(PG_LOG_INFO, "  %s", buf);
+        pg_log_info("  %s", buf);
     }

     if (nLoop > 1)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..b5beb96b43 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -200,16 +200,15 @@ main(int argc, char *argv[])
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
-                         "pg_dump", progname, full_path);
+            pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                     "same directory as \"%s\".\n"
+                     "Check your installation.",
+                     "pg_dump", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
-                         "pg_dump", full_path, progname);
-        exit_nicely(1);
+            pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                     "but was not the same version as %s.\n"
+                     "Check your installation.",
+                     "pg_dump", full_path, progname);
     }

     pgdumpopts = createPQExpBuffer();
@@ -339,7 +338,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -349,8 +349,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -358,8 +357,7 @@ main(int argc, char *argv[])
         (globals_only || roles_only || tablespaces_only))
     {
         pg_log_error("option --exclude-database cannot be used together with -g/--globals-only, -r/--roles-only, or
-t/--tablespaces-only");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -367,30 +365,24 @@ main(int argc, char *argv[])
     if (globals_only && roles_only)
     {
         pg_log_error("options -g/--globals-only and -r/--roles-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (globals_only && tablespaces_only)
     {
         pg_log_error("options -g/--globals-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (if_exists && !output_clean)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_fatal("option --if-exists requires option -c/--clean");

     if (roles_only && tablespaces_only)
     {
         pg_log_error("options -r/--roles-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -451,10 +443,7 @@ main(int argc, char *argv[])
                                prompt_password, false);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", pgdb);
-            exit_nicely(1);
-        }
+            pg_fatal("could not connect to database \"%s\"", pgdb);
     }
     else
     {
@@ -468,8 +457,7 @@ main(int argc, char *argv[])
         {
             pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n"
                          "Please specify an alternative database.");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
     }
@@ -487,11 +475,8 @@ main(int argc, char *argv[])
     {
         OPF = fopen(filename, PG_BINARY_W);
         if (!OPF)
-        {
-            pg_log_error("could not open output file \"%s\": %m",
-                         filename);
-            exit_nicely(1);
-        }
+            pg_fatal("could not open output file \"%s\": %m",
+                     filename);
     }
     else
         OPF = stdout;
@@ -502,11 +487,8 @@ main(int argc, char *argv[])
     if (dumpencoding)
     {
         if (PQsetClientEncoding(conn, dumpencoding) < 0)
-        {
-            pg_log_error("invalid client encoding \"%s\" specified",
-                         dumpencoding);
-            exit_nicely(1);
-        }
+            pg_fatal("invalid client encoding \"%s\" specified",
+                     dumpencoding);
     }

     /*
@@ -1321,20 +1303,14 @@ dumpDatabases(PGconn *conn)

         ret = runPgDump(dbname, create_opts);
         if (ret != 0)
-        {
-            pg_log_error("pg_dump failed on database \"%s\", exiting", dbname);
-            exit_nicely(1);
-        }
+            pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);

         if (filename)
         {
             OPF = fopen(filename, PG_BINARY_A);
             if (!OPF)
-            {
-                pg_log_error("could not re-open the output file \"%s\": %m",
-                             filename);
-                exit_nicely(1);
-            }
+                pg_fatal("could not re-open the output file \"%s\": %m",
+                         filename);
         }

     }
@@ -1470,10 +1446,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         {
             conn_opts = PQconninfoParse(connection_string, &err_msg);
             if (conn_opts == NULL)
-            {
-                pg_log_error("%s", err_msg);
-                exit_nicely(1);
-            }
+                pg_fatal("%s", err_msg);

             for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
             {
@@ -1540,10 +1513,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", dbname);
-            exit_nicely(1);
-        }
+            pg_fatal("could not connect to database \"%s\"", dbname);

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -1560,10 +1530,7 @@ connectDatabase(const char *dbname, const char *connection_string,
     if (PQstatus(conn) == CONNECTION_BAD)
     {
         if (fail_on_error)
-        {
-            pg_log_error("%s", PQerrorMessage(conn));
-            exit_nicely(1);
-        }
+            pg_fatal("%s", PQerrorMessage(conn));
         else
         {
             PQfinish(conn);
@@ -1589,17 +1556,11 @@ connectDatabase(const char *dbname, const char *connection_string,
     /* Check version */
     remoteversion_str = PQparameterStatus(conn, "server_version");
     if (!remoteversion_str)
-    {
-        pg_log_error("could not get server version");
-        exit_nicely(1);
-    }
+        pg_fatal("could not get server version");
     server_version = PQserverVersion(conn);
     if (server_version == 0)
-    {
-        pg_log_error("could not parse server version \"%s\"",
-                     remoteversion_str);
-        exit_nicely(1);
-    }
+        pg_fatal("could not parse server version \"%s\"",
+                 remoteversion_str);

     my_version = PG_VERSION_NUM;

@@ -1611,9 +1572,9 @@ connectDatabase(const char *dbname, const char *connection_string,
         && (server_version < 90200 ||
             (server_version / 100) > (my_version / 100)))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
         pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
         exit_nicely(1);
     }

@@ -1675,7 +1636,7 @@ executeQuery(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
@@ -1698,7 +1659,7 @@ executeCommand(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 55bf1b6975..049a100634 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -287,7 +287,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -303,17 +304,13 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     /* Complain if neither -f nor -d was specified (except if dumping TOC) */
     if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
-    {
-        pg_log_error("one of -d/--dbname and -f/--file must be specified");
-        exit_nicely(1);
-    }
+        pg_fatal("one of -d/--dbname and -f/--file must be specified");

     /* Should get at most one of -d and -f, else user is confused */
     if (opts->cparams.dbname)
@@ -321,41 +318,28 @@ main(int argc, char **argv)
         if (opts->filename)
         {
             pg_log_error("options -d/--dbname and -f/--file cannot be used together");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
         opts->useDB = 1;
     }

     if (opts->dataOnly && opts->schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (opts->dataOnly && opts->dropSchema)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -c/--clean and -a/--data-only cannot be used together");

     /*
      * -C is not compatible with -1, because we can't create a database inside
      * a transaction block.
      */
     if (opts->createDB && opts->single_txn)
-    {
-        pg_log_error("options -C/--create and -1/--single-transaction cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -C/--create and -1/--single-transaction cannot be used together");

     /* Can't do single-txn mode with multiple connections */
     if (opts->single_txn && numWorkers > 1)
-    {
-        pg_log_error("cannot specify both --single-transaction and multiple jobs");
-        exit_nicely(1);
-    }
+        pg_fatal("cannot specify both --single-transaction and multiple jobs");

     opts->disable_triggers = disable_triggers;
     opts->enable_row_security = enable_row_security;
@@ -369,10 +353,7 @@ main(int argc, char **argv)
     opts->no_subscriptions = no_subscriptions;

     if (if_exists && !opts->dropSchema)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_fatal("option --if-exists requires option -c/--clean");
     opts->if_exists = if_exists;
     opts->strict_names = strict_names;

@@ -396,9 +377,8 @@ main(int argc, char **argv)
                 break;

             default:
-                pg_log_error("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
-                             opts->formatName);
-                exit_nicely(1);
+                pg_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
+                         opts->formatName);
         }
     }

diff --git a/src/bin/pg_dump/t/003_pg_dump_with_server.pl b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
index 528db179cb..c284866326 100644
--- a/src/bin/pg_dump/t/003_pg_dump_with_server.pl
+++ b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
@@ -30,7 +30,7 @@ my ($cmd, $stdout, $stderr, $result);

 command_fails_like(
     [ "pg_dump", '-p', $port, '--include-foreign-data=s0', 'postgres' ],
-    qr/foreign-data wrapper \"dummy\" has no handler\r?\npg_dump: error: query was:.*t0/,
+    qr/foreign-data wrapper \"dummy\" has no handler\r?\ndetail: Query was: .*t0/,
     "correctly fails to dump a foreign table from a dummy FDW");

 command_ok(
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 1eb4509fca..d4772a2965 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -161,14 +161,11 @@ main(int argc, char *argv[])
                     /*------
                       translator: the second %s is a command line argument (-e, etc) */
                     pg_log_error("invalid argument for option %s", "-e");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_xid_epoch == -1)
-                {
-                    pg_log_error("transaction ID epoch (-e) must not be -1");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID epoch (-e) must not be -1");
                 break;

             case 'u':
@@ -177,14 +174,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-u");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_oldest_xid))
-                {
-                    pg_log_error("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
                 break;

             case 'x':
@@ -193,14 +187,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-x");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_xid))
-                {
-                    pg_log_error("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
                 break;

             case 'c':
@@ -209,30 +200,24 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 if (set_oldest_commit_ts_xid < 2 &&
                     set_oldest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");

                 if (set_newest_commit_ts_xid < 2 &&
                     set_newest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");
                 break;

             case 'o':
@@ -241,14 +226,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-o");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_oid == 0)
-                {
-                    pg_log_error("OID (-o) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("OID (-o) must not be 0");
                 break;

             case 'm':
@@ -257,7 +239,7 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -265,24 +247,18 @@ main(int argc, char *argv[])
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxid == 0)
-                {
-                    pg_log_error("multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("multitransaction ID (-m) must not be 0");

                 /*
                  * XXX It'd be nice to have more sanity checks here, e.g. so
                  * that oldest is not wrapped around w.r.t. nextMulti.
                  */
                 if (set_oldestmxid == 0)
-                {
-                    pg_log_error("oldest multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("oldest multitransaction ID (-m) must not be 0");
                 break;

             case 'O':
@@ -291,21 +267,18 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-O");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxoff == -1)
-                {
-                    pg_log_error("multitransaction offset (-O) must not be -1");
-                    exit(1);
-                }
+                    pg_fatal("multitransaction offset (-O) must not be -1");
                 break;

             case 'l':
                 if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
                 {
                     pg_log_error("invalid argument for option %s", "-l");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -320,19 +293,14 @@ main(int argc, char *argv[])
                 errno = 0;
                 set_wal_segsize = strtol(optarg, &endptr, 10) * 1024 * 1024;
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("argument of --wal-segsize must be a number");
-                    exit(1);
-                }
+                    pg_fatal("argument of --wal-segsize must be a number");
                 if (!IsValidWalSegSize(set_wal_segsize))
-                {
-                    pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-                    exit(1);
-                }
+                    pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -345,15 +313,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -367,8 +334,8 @@ main(int argc, char *argv[])
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        pg_log_info("You must run %s as the PostgreSQL superuser.",
-                    progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+                          progname);
         exit(1);
     }
 #endif
@@ -377,20 +344,14 @@ main(int argc, char *argv[])

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(DataDir))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
-                     DataDir);
-        exit(1);
-    }
+        pg_fatal("could not read permissions of directory \"%s\": %m",
+                 DataDir);

     umask(pg_mode_mask);

     if (chdir(DataDir) < 0)
-    {
-        pg_log_error("could not change directory to \"%s\": %m",
-                     DataDir);
-        exit(1);
-    }
+        pg_fatal("could not change directory to \"%s\": %m",
+                 DataDir);

     /* Check that data directory matches our server version */
     CheckDataVersion();
@@ -402,16 +363,13 @@ main(int argc, char *argv[])
     if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
     {
         if (errno != ENOENT)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
-                         "postmaster.pid");
-            exit(1);
-        }
+            pg_fatal("could not open file \"%s\" for reading: %m",
+                     "postmaster.pid");
     }
     else
     {
         pg_log_error("lock file \"%s\" exists", "postmaster.pid");
-        pg_log_info("Is a server running?  If not, delete the lock file and try again.");
+        pg_log_error_hint("Is a server running?  If not, delete the lock file and try again.");
         exit(1);
     }

@@ -557,20 +515,16 @@ CheckDataVersion(void)
     char        rawline[64];

     if ((ver_fd = fopen(ver_file, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m",
-                     ver_file);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m",
+                 ver_file);

     /* version number has to be the first line read */
     if (!fgets(rawline, sizeof(rawline), ver_fd))
     {
         if (!ferror(ver_fd))
-            pg_log_error("unexpected empty file \"%s\"", ver_file);
+            pg_fatal("unexpected empty file \"%s\"", ver_file);
         else
-            pg_log_error("could not read file \"%s\": %m", ver_file);
-        exit(1);
+            pg_fatal("could not read file \"%s\": %m", ver_file);
     }

     /* strip trailing newline and carriage return */
@@ -579,8 +533,8 @@ CheckDataVersion(void)
     if (strcmp(rawline, PG_MAJORVERSION) != 0)
     {
         pg_log_error("data directory is of wrong version");
-        pg_log_info("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
-                    ver_file, rawline, PG_MAJORVERSION);
+        pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version
\"%s\".",
+                            ver_file, rawline, PG_MAJORVERSION);
         exit(1);
     }

@@ -612,10 +566,10 @@ read_controlfile(void)
         pg_log_error("could not open file \"%s\" for reading: %m",
                      XLOG_CONTROL_FILE);
         if (errno == ENOENT)
-            pg_log_info("If you are sure the data directory path is correct, execute\n"
-                        "  touch %s\n"
-                        "and try again.",
-                        XLOG_CONTROL_FILE);
+            pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
+                              "  touch %s\n"
+                              "and try again.",
+                              XLOG_CONTROL_FILE);
         exit(1);
     }

@@ -624,10 +578,7 @@ read_controlfile(void)

     len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
     if (len < 0)
-    {
-        pg_log_error("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
-        exit(1);
-    }
+        pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
     close(fd);

     if (len >= sizeof(ControlFileData) &&
@@ -968,10 +919,7 @@ FindEndOfXLOG(void)
      */
     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1003,16 +951,10 @@ FindEndOfXLOG(void)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", XLOGDIR);

     /*
      * Finally, convert to new xlog seg size, and advance by one to ensure we
@@ -1036,10 +978,7 @@ KillExistingXLOG(void)

     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1048,24 +987,15 @@ KillExistingXLOG(void)
         {
             snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
 }


@@ -1083,10 +1013,7 @@ KillExistingArchiveStatus(void)

     xldir = opendir(ARCHSTATDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1098,24 +1025,15 @@ KillExistingArchiveStatus(void)
         {
             snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
 }


@@ -1179,10 +1097,7 @@ WriteEmptyXLOG(void)
     fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
               pg_file_create_mode);
     if (fd < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", path);

     errno = 0;
     if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
@@ -1190,8 +1105,7 @@ WriteEmptyXLOG(void)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
+        pg_fatal("could not write file \"%s\": %m", path);
     }

     /* Fill the rest of the file with zeroes */
@@ -1203,16 +1117,12 @@ WriteEmptyXLOG(void)
         {
             if (errno == 0)
                 errno = ENOSPC;
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
+            pg_fatal("could not write file \"%s\": %m", path);
         }
     }

     if (fsync(fd) != 0)
-    {
-        pg_log_error("fsync error: %m");
-        exit(1);
-    }
+        pg_fatal("fsync error: %m");

     close(fd);
 }
diff --git a/src/bin/pg_rewind/nls.mk b/src/bin/pg_rewind/nls.mk
index a561f965df..a50f9139df 100644
--- a/src/bin/pg_rewind/nls.mk
+++ b/src/bin/pg_rewind/nls.mk
@@ -2,7 +2,6 @@
 CATALOG_NAME     = pg_rewind
 AVAIL_LANGUAGES  = cs de es fr it ja ko pl pt_BR ru sv tr uk zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) datapagemap.c file_ops.c filemap.c libpq_source.c local_source.c
parsexlog.cpg_rewind.c timeline.c xlogreader.c ../../common/fe_memutils.c ../../common/restricted_token.c
../../fe_utils/archive.c../../fe_utils/recovery_gen.c 
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) pg_fatal report_invalid_record:2
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) report_invalid_record:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    pg_fatal:1:c-format \
     report_invalid_record:2:c-format
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index efb82a4034..ab62fe2e83 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -160,10 +160,6 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
-
             case 'c':
                 restore_wal = true;
                 break;
@@ -204,34 +200,39 @@ main(int argc, char **argv)
             case 4:
                 no_ensure_shutdown = true;
                 break;
+
+            default:
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+                exit(1);
         }
     }

     if (datadir_source == NULL && connstr_source == NULL)
     {
         pg_log_error("no source specified (--source-pgdata or --source-server)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_source != NULL && connstr_source != NULL)
     {
         pg_log_error("only one of --source-pgdata or --source-server can be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_target == NULL)
     {
         pg_log_error("no target data directory specified (--target-pgdata)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (writerecoveryconf && connstr_source == NULL)
     {
         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -239,7 +240,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -253,8 +254,8 @@ main(int argc, char **argv)
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
-                progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+                          progname);
         exit(1);
     }
 #endif
@@ -263,11 +264,8 @@ main(int argc, char **argv)

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(datadir_target))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
-                     datadir_target);
-        exit(1);
-    }
+        pg_fatal("could not read permissions of directory \"%s\": %m",
+                 datadir_target);

     umask(pg_mode_mask);

@@ -1035,16 +1033,15 @@ getRestoreCommand(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (rc == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
-                         "postgres", progname, full_path);
+            pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                     "same directory as \"%s\".\n"
+                     "Check your installation.",
+                     "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
-                         "postgres", full_path, progname);
-        exit(1);
+            pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                     "but was not the same version as %s.\n"
+                     "Check your installation.",
+                     "postgres", full_path, progname);
     }

     /*
@@ -1125,7 +1122,8 @@ ensureCleanShutdown(const char *argv0)
     if (system(cmd) != 0)
     {
         pg_log_error("postgres single-user mode in target cluster failed");
-        pg_fatal("Command was: %s", cmd);
+        pg_log_error_detail("Command was: %s", cmd);
+        exit(1);
     }
 }

diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 388870ce95..393182fe2a 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -33,9 +33,6 @@ extern int    targetNentries;
 extern uint64 fetch_size;
 extern uint64 fetch_done;

-/* logging support */
-#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
-
 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
                            int tliIndex, XLogRecPtr endpoint,
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c
index df8f82a50c..983388c92b 100644
--- a/src/bin/pg_rewind/timeline.c
+++ b/src/bin/pg_rewind/timeline.c
@@ -73,19 +73,19 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
         {
             /* expect a numeric timeline ID as first field of line */
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a numeric timeline ID.");
+            pg_log_error_detail("Expected a numeric timeline ID.");
             exit(1);
         }
         if (nfields != 3)
         {
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a write-ahead log switchpoint location.");
+            pg_log_error_detail("Expected a write-ahead log switchpoint location.");
             exit(1);
         }
         if (entries && tli <= lasttli)
         {
             pg_log_error("invalid data in history file: %s", fline);
-            pg_log_error("Timeline IDs must be in increasing sequence.");
+            pg_log_error_detail("Timeline IDs must be in increasing sequence.");
             exit(1);
         }

@@ -106,7 +106,7 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
     if (entries && targetTLI <= lasttli)
     {
         pg_log_error("invalid data in history file");
-        pg_log_error("Timeline IDs must be less than child timeline's ID.");
+        pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
         exit(1);
     }

diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index ddabf64c58..f7bc199a30 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -47,10 +47,7 @@ do { \
     alarm_triggered = false; \
     if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
         INVALID_HANDLE_VALUE) \
-    { \
-        pg_log_error("could not create thread for alarm"); \
-        exit(1); \
-    } \
+        pg_fatal("could not create thread for alarm"); \
     gettimeofday(&start_t, NULL); \
 } while (0)
 #endif
@@ -95,7 +92,7 @@ static int    pg_fsync_writethrough(int fd);
 #endif
 static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops);

-#define die(msg) do { pg_log_error("%s: %m", _(msg)); exit(1); } while(0)
+#define die(msg) pg_fatal("%s: %m", _(msg))


 int
@@ -186,24 +183,20 @@ handle_args(int argc, char *argv[])
                     errno != 0 || optval != (unsigned int) optval)
                 {
                     pg_log_error("invalid argument for option %s", "--secs-per-test");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 secs_per_test = (unsigned int) optval;
                 if (secs_per_test == 0)
-                {
-                    pg_log_error("%s must be in range %u..%u",
-                                 "--secs-per-test", 1, UINT_MAX);
-                    exit(1);
-                }
+                    pg_fatal("%s must be in range %u..%u",
+                             "--secs-per-test", 1, UINT_MAX);
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -211,8 +204,7 @@ handle_args(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ca86c11292..4ea9217884 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -12,6 +12,9 @@

 #include "libpq-fe.h"

+/* For now, pg_upgrade does not use common/logging.c; use our own pg_fatal */
+#undef pg_fatal
+
 /* Use port in the private/dynamic port number range */
 #define DEF_PGUPORT            50432

diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05cb520c11..299aba7c2c 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -252,8 +252,8 @@ main(int argc, char **argv)
                 canonicalize_path(wal_directory);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -261,9 +261,8 @@ main(int argc, char **argv)
     /* Get backup directory name */
     if (optind >= argc)
     {
-        pg_log_fatal("no backup directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error("no backup directory specified");
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     context.backup_directory = pstrdup(argv[optind++]);
@@ -272,10 +271,9 @@ main(int argc, char **argv)
     /* Complain if any arguments remain */
     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -295,16 +293,15 @@ main(int argc, char **argv)
             if (find_my_exec(argv[0], full_path) < 0)
                 strlcpy(full_path, progname, sizeof(full_path));
             if (ret == -1)
-                pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
-                             "same directory as \"%s\".\n"
-                             "Check your installation.",
-                             "pg_waldump", "pg_verifybackup", full_path);
+                pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                         "same directory as \"%s\".\n"
+                         "Check your installation.",
+                         "pg_waldump", "pg_verifybackup", full_path);
             else
-                pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
-                             "but was not the same version as %s.\n"
-                             "Check your installation.",
-                             "pg_waldump", full_path, "pg_verifybackup");
-            exit(1);
+                pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                         "but was not the same version as %s.\n"
+                         "Check your installation.",
+                         "pg_waldump", full_path, "pg_verifybackup");
         }
     }

@@ -449,7 +446,7 @@ report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
@@ -840,7 +837,7 @@ report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     context->saw_any_error = true;
@@ -857,7 +854,7 @@ report_fatal_error(const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 118beb53d7..7890645161 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -192,7 +192,7 @@ sub test_fatal_error

     my ($test_name, $manifest_contents) = @_;

-    test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+    test_bad_manifest($test_name, qr/error: $test_name/, $manifest_contents);
     return;
 }

diff --git a/src/bin/pg_waldump/nls.mk b/src/bin/pg_waldump/nls.mk
index a3d5e88e4f..159638fc00 100644
--- a/src/bin/pg_waldump/nls.mk
+++ b/src/bin/pg_waldump/nls.mk
@@ -2,5 +2,5 @@
 CATALOG_NAME     = pg_waldump
 AVAIL_LANGUAGES  = cs de el es fr ja ko ru sv tr uk vi zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) pg_waldump.c
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) fatal_error
-GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) fatal_error:1:c-format
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS)
+GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a6251e1a96..0076cbe1d1 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -75,7 +75,6 @@ typedef struct XLogDumpStats
     Stats        record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
 } XLogDumpStats;

-#define fatal_error(...) do { pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)

 /*
  * When sigint is called, just tell the system to exit at the next possible
@@ -161,7 +160,7 @@ open_file_in_directory(const char *directory, const char *fname)
     fd = open(fpath, O_RDONLY | PG_BINARY, 0);

     if (fd < 0 && errno != ENOENT)
-        fatal_error("could not open file \"%s\": %m", fname);
+        pg_fatal("could not open file \"%s\": %m", fname);
     return fd;
 }

@@ -217,19 +216,19 @@ search_directory(const char *directory, const char *fname)
             WalSegSz = longhdr->xlp_seg_size;

             if (!IsValidWalSegSize(WalSegSz))
-                fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d byte", 
-                                     "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d bytes", 
-                                     WalSegSz),
-                            fname, WalSegSz);
+                pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d byte", 
+                                  "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d bytes", 
+                                  WalSegSz),
+                         fname, WalSegSz);
         }
         else
         {
             if (errno != 0)
-                fatal_error("could not read file \"%s\": %m",
-                            fname);
+                pg_fatal("could not read file \"%s\": %m",
+                         fname);
             else
-                fatal_error("could not read file \"%s\": read %d of %d",
-                            fname, r, XLOG_BLCKSZ);
+                pg_fatal("could not read file \"%s\": read %d of %d",
+                         fname, r, XLOG_BLCKSZ);
         }
         close(fd);
         return true;
@@ -290,9 +289,9 @@ identify_target_directory(char *directory, char *fname)

     /* could not locate WAL file */
     if (fname)
-        fatal_error("could not locate WAL file \"%s\"", fname);
+        pg_fatal("could not locate WAL file \"%s\"", fname);
     else
-        fatal_error("could not find any WAL file");
+        pg_fatal("could not find any WAL file");

     return NULL;                /* not reached */
 }
@@ -333,7 +332,7 @@ WALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo,
         break;
     }

-    fatal_error("could not find file \"%s\": %m", fname);
+    pg_fatal("could not find file \"%s\": %m", fname);
 }

 /*
@@ -382,13 +381,13 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
         if (errinfo.wre_errno != 0)
         {
             errno = errinfo.wre_errno;
-            fatal_error("could not read from file %s, offset %d: %m",
-                        fname, errinfo.wre_off);
+            pg_fatal("could not read from file %s, offset %d: %m",
+                     fname, errinfo.wre_off);
         }
         else
-            fatal_error("could not read from file %s, offset %d: read %d of %d",
-                        fname, errinfo.wre_off, errinfo.wre_read,
-                        errinfo.wre_req);
+            pg_fatal("could not read from file %s, offset %d: read %d of %d",
+                     fname, errinfo.wre_off, errinfo.wre_read,
+                     errinfo.wre_req);
     }

     return count;
@@ -1010,13 +1009,13 @@ main(int argc, char **argv)
             waldir = directory;

             if (!verify_directory(waldir))
-                fatal_error("could not open directory \"%s\": %m", waldir);
+                pg_fatal("could not open directory \"%s\": %m", waldir);
         }

         waldir = identify_target_directory(waldir, fname);
         fd = open_file_in_directory(waldir, fname);
         if (fd < 0)
-            fatal_error("could not open file \"%s\"", fname);
+            pg_fatal("could not open file \"%s\"", fname);
         close(fd);

         /* parse position from file */
@@ -1046,15 +1045,15 @@ main(int argc, char **argv)

             fd = open_file_in_directory(waldir, fname);
             if (fd < 0)
-                fatal_error("could not open file \"%s\"", fname);
+                pg_fatal("could not open file \"%s\"", fname);
             close(fd);

             /* parse position from file */
             XLogFromFileName(fname, &private.timeline, &endsegno, WalSegSz);

             if (endsegno < segno)
-                fatal_error("ENDSEG %s is before STARTSEG %s",
-                            argv[optind + 1], argv[optind]);
+                pg_fatal("ENDSEG %s is before STARTSEG %s",
+                         argv[optind + 1], argv[optind]);

             if (XLogRecPtrIsInvalid(private.endptr))
                 XLogSegNoOffsetToRecPtr(endsegno + 1, 0, WalSegSz,
@@ -1094,14 +1093,14 @@ main(int argc, char **argv)
                                       .segment_close = WALDumpCloseSegment),
                            &private);
     if (!xlogreader_state)
-        fatal_error("out of memory while allocating a WAL reading processor");
+        pg_fatal("out of memory while allocating a WAL reading processor");

     /* first find a valid recptr to start from */
     first_record = XLogFindNextRecord(xlogreader_state, private.startptr);

     if (first_record == InvalidXLogRecPtr)
-        fatal_error("could not find a valid record after %X/%X",
-                    LSN_FORMAT_ARGS(private.startptr));
+        pg_fatal("could not find a valid record after %X/%X",
+                 LSN_FORMAT_ARGS(private.startptr));

     /*
      * Display a message that we're skipping data if `from` wasn't a pointer
@@ -1176,15 +1175,15 @@ main(int argc, char **argv)
         exit(0);

     if (errormsg)
-        fatal_error("error in WAL record at %X/%X: %s",
-                    LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
-                    errormsg);
+        pg_fatal("error in WAL record at %X/%X: %s",
+                 LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
+                 errormsg);

     XLogReaderFree(xlogreader_state);

     return EXIT_SUCCESS;

 bad_argument:
-    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     return EXIT_FAILURE;
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f166a77e3a..1779e3911c 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1301,8 +1301,8 @@ executeStatement(PGconn *con, const char *sql)
     res = PQexec(con, sql);
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
-        pg_log_fatal("query failed: %s", PQerrorMessage(con));
-        pg_log_info("query was: %s", sql);
+        pg_log_error("query failed: %s", PQerrorMessage(con));
+        pg_log_error_detail("Query was: %s", sql);
         exit(1);
     }
     PQclear(res);
@@ -1318,7 +1318,7 @@ tryExecuteStatement(PGconn *con, const char *sql)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("%s", PQerrorMessage(con));
-        pg_log_info("(ignoring this error and continuing anyway)");
+        pg_log_error_detail("(ignoring this error and continuing anyway)");
     }
     PQclear(res);
 }
@@ -2650,8 +2650,7 @@ evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)

         default:
             /* internal error which should never occur */
-            pg_log_fatal("unexpected enode type in evaluation: %d", expr->etype);
-            exit(1);
+            pg_fatal("unexpected enode type in evaluation: %d", expr->etype);
     }
 }

@@ -4192,10 +4191,7 @@ initGenerateDataClientSide(PGconn *con)
     res = PQexec(con, copy_statement);

     if (PQresultStatus(res) != PGRES_COPY_IN)
-    {
-        pg_log_fatal("unexpected copy in result: %s", PQerrorMessage(con));
-        exit(1);
-    }
+        pg_fatal("unexpected copy in result: %s", PQerrorMessage(con));
     PQclear(res);

     start = pg_time_now();
@@ -4209,10 +4205,7 @@ initGenerateDataClientSide(PGconn *con)
                           INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",
                           j, k / naccounts + 1, 0);
         if (PQputline(con, sql.data))
-        {
-            pg_log_fatal("PQputline failed");
-            exit(1);
-        }
+            pg_fatal("PQputline failed");

         if (CancelRequested)
             break;
@@ -4254,15 +4247,9 @@ initGenerateDataClientSide(PGconn *con)
         fputc('\n', stderr);    /* Need to move to next line */

     if (PQputline(con, "\\.\n"))
-    {
-        pg_log_fatal("very last PQputline failed");
-        exit(1);
-    }
+        pg_fatal("very last PQputline failed");
     if (PQendcopy(con))
-    {
-        pg_log_fatal("PQendcopy failed");
-        exit(1);
-    }
+        pg_fatal("PQendcopy failed");

     termPQExpBuffer(&sql);

@@ -4402,17 +4389,14 @@ static void
 checkInitSteps(const char *initialize_steps)
 {
     if (initialize_steps[0] == '\0')
-    {
-        pg_log_fatal("no initialization steps specified");
-        exit(1);
-    }
+        pg_fatal("no initialization steps specified");

     for (const char *step = initialize_steps; *step != '\0'; step++)
     {
         if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
         {
-            pg_log_fatal("unrecognized initialization step \"%c\"", *step);
-            pg_log_info("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
+            pg_log_error("unrecognized initialization step \"%c\"", *step);
+            pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
             exit(1);
         }
     }
@@ -4433,10 +4417,7 @@ runInitSteps(const char *initialize_steps)
     initPQExpBuffer(&stats);

     if ((con = doConnect()) == NULL)
-    {
-        pg_log_fatal("could not create connection for initialization");
-        exit(1);
-    }
+        pg_fatal("could not create connection for initialization");

     setup_cancel_handler(NULL);
     SetCancelConn(con);
@@ -4479,7 +4460,7 @@ runInitSteps(const char *initialize_steps)
             case ' ':
                 break;            /* ignore */
             default:
-                pg_log_fatal("unrecognized initialization step \"%c\"", *step);
+                pg_log_error("unrecognized initialization step \"%c\"", *step);
                 PQfinish(con);
                 exit(1);
         }
@@ -4523,21 +4504,18 @@ GetTableInfo(PGconn *con, bool scale_given)
     {
         char       *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);

-        pg_log_fatal("could not count number of branches: %s", PQerrorMessage(con));
+        pg_log_error("could not count number of branches: %s", PQerrorMessage(con));

         if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
-            pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\"",
-                        PQdb(con));
+            pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
+                              PQdb(con));

         exit(1);
     }
     scale = atoi(PQgetvalue(res, 0, 0));
     if (scale < 0)
-    {
-        pg_log_fatal("invalid count(*) from pgbench_branches: \"%s\"",
-                     PQgetvalue(res, 0, 0));
-        exit(1);
-    }
+        pg_fatal("invalid count(*) from pgbench_branches: \"%s\"",
+                 PQgetvalue(res, 0, 0));
     PQclear(res);

     /* warn if we override user-given -s switch */
@@ -4584,8 +4562,8 @@ GetTableInfo(PGconn *con, bool scale_given)
          * This case is unlikely as pgbench already found "pgbench_branches"
          * above to compute the scale.
          */
-        pg_log_fatal("no pgbench_accounts table found in search_path");
-        pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
+        pg_log_error("no pgbench_accounts table found in search_path");
+        pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
         exit(1);
     }
     else                        /* PQntupes(res) == 1 */
@@ -4607,8 +4585,7 @@ GetTableInfo(PGconn *con, bool scale_given)
             else
             {
                 /* possibly a newer version with new partition method */
-                pg_log_fatal("unexpected partition method: \"%s\"", ps);
-                exit(1);
+                pg_fatal("unexpected partition method: \"%s\"", ps);
             }
         }

@@ -4700,7 +4677,7 @@ syntax_error(const char *source, int lineno,
     if (command != NULL)
         appendPQExpBuffer(&buf, " in command \"%s\"", command);

-    pg_log_fatal("%s", buf.data);
+    pg_log_error("%s", buf.data);

     termPQExpBuffer(&buf);

@@ -5048,9 +5025,8 @@ process_backslash_command(PsqlScanState sstate, const char *source)
 static void
 ConditionError(const char *desc, int cmdn, const char *msg)
 {
-    pg_log_fatal("condition error in script \"%s\" command %d: %s",
-                 desc, cmdn, msg);
-    exit(1);
+    pg_fatal("condition error in script \"%s\" command %d: %s",
+             desc, cmdn, msg);
 }

 /*
@@ -5286,18 +5262,12 @@ process_file(const char *filename, int weight)
     if (strcmp(filename, "-") == 0)
         fd = stdin;
     else if ((fd = fopen(filename, "r")) == NULL)
-    {
-        pg_log_fatal("could not open file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", filename);

     buf = read_file_contents(fd);

     if (ferror(fd))
-    {
-        pg_log_fatal("could not read file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not read file \"%s\": %m", filename);

     if (fd != stdin)
         fclose(fd);
@@ -5350,9 +5320,9 @@ findBuiltin(const char *name)

     /* error cases */
     if (found == 0)
-        pg_log_fatal("no builtin script found for name \"%s\"", name);
+        pg_log_error("no builtin script found for name \"%s\"", name);
     else                        /* found > 1 */
-        pg_log_fatal("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
+        pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);

     listAvailableScripts();
     exit(1);
@@ -5384,16 +5354,10 @@ parseScriptWeight(const char *option, char **script)
         errno = 0;
         wtmp = strtol(sep + 1, &badp, 10);
         if (errno != 0 || badp == sep + 1 || *badp != '\0')
-        {
-            pg_log_fatal("invalid weight specification: %s", sep);
-            exit(1);
-        }
+            pg_fatal("invalid weight specification: %s", sep);
         if (wtmp > INT_MAX || wtmp < 0)
-        {
-            pg_log_fatal("weight specification out of range (0 .. %d): %lld",
-                         INT_MAX, (long long) wtmp);
-            exit(1);
-        }
+            pg_fatal("weight specification out of range (0 .. %d): %lld",
+                     INT_MAX, (long long) wtmp);
         weight = wtmp;
     }
     else
@@ -5410,16 +5374,10 @@ static void
 addScript(ParsedScript script)
 {
     if (script.commands == NULL || script.commands[0] == NULL)
-    {
-        pg_log_fatal("empty command list for script \"%s\"", script.desc);
-        exit(1);
-    }
+        pg_fatal("empty command list for script \"%s\"", script.desc);

     if (num_scripts >= MAX_SCRIPTS)
-    {
-        pg_log_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
-        exit(1);
-    }
+        pg_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);

     CheckConditional(script);

@@ -5735,7 +5693,7 @@ set_random_seed(const char *seed)
         if (sscanf(seed, "%lu%c", &ulseed, &garbage) != 1)
         {
             pg_log_error("unrecognized random seed option \"%s\"", seed);
-            pg_log_info("Expecting an unsigned integer, \"time\" or \"rand\"");
+            pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
             return false;
         }
         iseed = (uint64) ulseed;
@@ -5866,10 +5824,7 @@ main(int argc, char **argv)

     /* set random seed early, because it may be used while parsing scripts. */
     if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
-    {
-        pg_log_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
-        exit(1);
-    }
+        pg_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");

     while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) !=
-1)
     {
@@ -5916,15 +5871,12 @@ main(int argc, char **argv)
 #else                            /* but BSD doesn't ... */
                 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
 #endif                            /* RLIMIT_NOFILE */
-                {
-                    pg_log_fatal("getrlimit failed: %m");
-                    exit(1);
-                }
+                    pg_fatal("getrlimit failed: %m");
                 if (rlim.rlim_cur < nclients + 3)
                 {
-                    pg_log_fatal("need at least %d open files, but system limit is %ld",
+                    pg_log_error("need at least %d open files, but system limit is %ld",
                                  nclients + 3, (long) rlim.rlim_cur);
-                    pg_log_info("Reduce number of clients, or use limit/ulimit to increase the system limit.");
+                    pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
                     exit(1);
                 }
 #endif                            /* HAVE_GETRLIMIT */
@@ -5938,10 +5890,7 @@ main(int argc, char **argv)
                 }
 #ifndef ENABLE_THREAD_SAFETY
                 if (nthreads != 1)
-                {
-                    pg_log_fatal("threads are not supported on this platform; use -j1");
-                    exit(1);
-                }
+                    pg_fatal("threads are not supported on this platform; use -j1");
 #endif                            /* !ENABLE_THREAD_SAFETY */
                 break;
             case 'C':
@@ -6014,10 +5963,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
-                    {
-                        pg_log_fatal("invalid variable definition: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid variable definition: \"%s\"", optarg);

                     *p++ = '\0';
                     if (!putVariable(&state[0], "option", optarg, p))
@@ -6036,10 +5982,7 @@ main(int argc, char **argv)
                     if (strcmp(optarg, QUERYMODE[querymode]) == 0)
                         break;
                 if (querymode >= NUM_QUERYMODE)
-                {
-                    pg_log_fatal("invalid query mode (-M): \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid query mode (-M): \"%s\"", optarg);
                 break;
             case 'P':
                 benchmarking_option_set = true;
@@ -6055,10 +5998,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if (throttle_value <= 0.0)
-                    {
-                        pg_log_fatal("invalid rate limit: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid rate limit: \"%s\"", optarg);
                     /* Invert rate limit into per-transaction delay in usec */
                     throttle_delay = 1000000.0 / throttle_value;
                 }
@@ -6068,10 +6008,7 @@ main(int argc, char **argv)
                     double        limit_ms = atof(optarg);

                     if (limit_ms <= 0.0)
-                    {
-                        pg_log_fatal("invalid latency limit: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid latency limit: \"%s\"", optarg);
                     benchmarking_option_set = true;
                     latency_limit = (int64) (limit_ms * 1000);
                 }
@@ -6092,10 +6029,7 @@ main(int argc, char **argv)
                 benchmarking_option_set = true;
                 sample_rate = atof(optarg);
                 if (sample_rate <= 0.0 || sample_rate > 1.0)
-                {
-                    pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid sampling rate: \"%s\"", optarg);
                 break;
             case 5:                /* aggregate-interval */
                 benchmarking_option_set = true;
@@ -6118,10 +6052,7 @@ main(int argc, char **argv)
             case 9:                /* random-seed */
                 benchmarking_option_set = true;
                 if (!set_random_seed(optarg))
-                {
-                    pg_log_fatal("error while setting random seed from --random-seed option");
-                    exit(1);
-                }
+                    pg_fatal("error while setting random seed from --random-seed option");
                 break;
             case 10:            /* list */
                 {
@@ -6144,16 +6075,13 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "hash") == 0)
                     partition_method = PART_HASH;
                 else
-                {
-                    pg_log_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
+                             optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -6179,10 +6107,7 @@ main(int argc, char **argv)
     }

     if (total_weight == 0 && !is_init_mode)
-    {
-        pg_log_fatal("total script weight must not be zero");
-        exit(1);
-    }
+        pg_fatal("total script weight must not be zero");

     /* show per script stats if several scripts are used */
     if (num_scripts > 1)
@@ -6217,25 +6142,19 @@ main(int argc, char **argv)

     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (is_init_mode)
     {
         if (benchmarking_option_set)
-        {
-            pg_log_fatal("some of the specified options cannot be used in initialization (-i) mode");
-            exit(1);
-        }
+            pg_fatal("some of the specified options cannot be used in initialization (-i) mode");

         if (partitions == 0 && partition_method != PART_NONE)
-        {
-            pg_log_fatal("--partition-method requires greater than zero --partitions");
-            exit(1);
-        }
+            pg_fatal("--partition-method requires greater than zero --partitions");

         /* set default method */
         if (partitions > 0 && partition_method == PART_NONE)
@@ -6271,17 +6190,11 @@ main(int argc, char **argv)
     else
     {
         if (initialization_option_set)
-        {
-            pg_log_fatal("some of the specified options cannot be used in benchmarking mode");
-            exit(1);
-        }
+            pg_fatal("some of the specified options cannot be used in benchmarking mode");
     }

     if (nxacts > 0 && duration > 0)
-    {
-        pg_log_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
-        exit(1);
-    }
+        pg_fatal("specify either a number of transactions (-t) or a duration (-T), not both");

     /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
     if (nxacts <= 0 && duration <= 0)
@@ -6289,47 +6202,26 @@ main(int argc, char **argv)

     /* --sampling-rate may be used only with -l */
     if (sample_rate > 0.0 && !use_log)
-    {
-        pg_log_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
-        exit(1);
-    }
+        pg_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");

     /* --sampling-rate may not be used with --aggregate-interval */
     if (sample_rate > 0.0 && agg_interval > 0)
-    {
-        pg_log_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same
time");
-        exit(1);
-    }
+        pg_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same
time");

     if (agg_interval > 0 && !use_log)
-    {
-        pg_log_fatal("log aggregation is allowed only when actually logging transactions");
-        exit(1);
-    }
+        pg_fatal("log aggregation is allowed only when actually logging transactions");

     if (!use_log && logfile_prefix)
-    {
-        pg_log_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
-        exit(1);
-    }
+        pg_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");

     if (duration > 0 && agg_interval > duration)
-    {
-        pg_log_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)",
agg_interval,duration); 
-        exit(1);
-    }
+        pg_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval,
duration);

     if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
-    {
-        pg_log_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
-        exit(1);
-    }
+        pg_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);

     if (progress_timestamp && progress == 0)
-    {
-        pg_log_fatal("--progress-timestamp is allowed only under --progress");
-        exit(1);
-    }
+        pg_fatal("--progress-timestamp is allowed only under --progress");

     /*
      * save main process id in the global variable because process id will be
@@ -6378,10 +6270,7 @@ main(int argc, char **argv)
     /* opening connection... */
     con = doConnect();
     if (con == NULL)
-    {
-        pg_log_fatal("could not create connection for setup");
-        exit(1);
-    }
+        pg_fatal("could not create connection for setup");

     /* report pgbench and server versions */
     printVersion(con);
@@ -6487,10 +6376,7 @@ main(int argc, char **argv)

     errno = THREAD_BARRIER_INIT(&barrier, nthreads);
     if (errno != 0)
-    {
-        pg_log_fatal("could not initialize barrier: %m");
-        exit(1);
-    }
+        pg_fatal("could not initialize barrier: %m");

 #ifdef ENABLE_THREAD_SAFETY
     /* start all threads but thread 0 which is executed directly later */
@@ -6502,10 +6388,7 @@ main(int argc, char **argv)
         errno = THREAD_CREATE(&thread->thread, threadRun, thread);

         if (errno != 0)
-        {
-            pg_log_fatal("could not create thread: %m");
-            exit(1);
-        }
+            pg_fatal("could not create thread: %m");
     }
 #else
     Assert(nthreads == 1);
@@ -6569,7 +6452,7 @@ main(int argc, char **argv)
     THREAD_BARRIER_DESTROY(&barrier);

     if (exit_code != 0)
-        pg_log_fatal("Run was aborted; the above results are incomplete.");
+        pg_log_error("Run was aborted; the above results are incomplete.");

     return exit_code;
 }
@@ -6603,10 +6486,7 @@ threadRun(void *arg)
         thread->logfile = fopen(logpath, "w");

         if (thread->logfile == NULL)
-        {
-            pg_log_fatal("could not open logfile \"%s\": %m", logpath);
-            exit(1);
-        }
+            pg_fatal("could not open logfile \"%s\": %m", logpath);
     }

     /* explicitly initialize the state machines */
@@ -6631,9 +6511,8 @@ threadRun(void *arg)
             if ((state[i].con = doConnect()) == NULL)
             {
                 /* coldly abort on initial connection failure */
-                pg_log_fatal("could not create connection for client %d",
-                             state[i].id);
-                exit(1);
+                pg_fatal("could not create connection for client %d",
+                         state[i].id);
             }
         }
     }
@@ -6901,10 +6780,7 @@ setalarm(int seconds)
         !CreateTimerQueueTimer(&timer, queue,
                                win32_timer_callback, NULL, seconds * 1000, 0,
                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-    {
-        pg_log_fatal("failed to set timer");
-        exit(1);
-    }
+        pg_fatal("failed to set timer");
 }

 #endif                            /* WIN32 */
@@ -7048,8 +6924,7 @@ add_socket_to_set(socket_set *sa, int fd, int idx)
          * Doing a hard exit here is a bit grotty, but it doesn't seem worth
          * complicating the API to make it less grotty.
          */
-        pg_log_fatal("too many client connections for select()");
-        exit(1);
+        pg_fatal("too many client connections for select()");
     }
     FD_SET(fd, &sa->fds);
     if (fd > sa->maxfd)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 292cff5df9..1380a95b3e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -232,7 +232,7 @@ HandleSlashCmds(PsqlScanState scan_state,
     {
         pg_log_error("invalid command \\%s", cmd);
         if (pset.cur_cmd_interactive)
-            pg_log_info("Try \\? for help.");
+            pg_log_error_hint("Try \\? for help.");
         status = PSQL_CMD_ERROR;
     }

diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index d65b9a124f..f9c0a2a4d2 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -302,7 +302,7 @@ CheckConnection(void)
     {
         if (!pset.cur_cmd_interactive)
         {
-            pg_log_fatal("connection to server was lost");
+            pg_log_error("connection to server was lost");
             exit(EXIT_BADCONN);
         }

diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..e24979cba4 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -58,10 +58,7 @@ usage(unsigned short int pager)
     {
         user = get_user_name(&errstr);
         if (!user)
-        {
-            pg_log_fatal("%s", errstr);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("%s", errstr);
     }

     /*
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index e5c976fc4f..b0c4177a20 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -77,10 +77,7 @@ MainLoop(FILE *source)
     if (PQExpBufferBroken(query_buf) ||
         PQExpBufferBroken(previous_buf) ||
         PQExpBufferBroken(history_buf))
-    {
-        pg_log_error("out of memory");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("out of memory");

     /* main loop to get queries and execute them */
     while (successResult == EXIT_SUCCESS)
@@ -398,10 +395,7 @@ MainLoop(FILE *source)
             prompt_status = prompt_tmp;

             if (PQExpBufferBroken(query_buf))
-            {
-                pg_log_error("out of memory");
-                exit(EXIT_FAILURE);
-            }
+                pg_fatal("out of memory");

             /*
              * Increase statement line number counter for each linebreak added
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index be9dec749d..127b578b34 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -216,10 +216,7 @@ main(int argc, char *argv[])

     /* Bail out if -1 was specified but will be ignored. */
     if (options.single_txn && options.actions.head == NULL)
-    {
-        pg_log_fatal("-1 can only be used in non-interactive mode");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("-1 can only be used in non-interactive mode");

     if (!pset.popt.topt.fieldSep.separator &&
         !pset.popt.topt.fieldSep.separator_zero)
@@ -342,11 +339,8 @@ main(int argc, char *argv[])
     {
         pset.logfile = fopen(options.logfilename, "a");
         if (!pset.logfile)
-        {
-            pg_log_fatal("could not open log file \"%s\": %m",
-                         options.logfilename);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not open log file \"%s\": %m",
+                     options.logfilename);
     }

     if (!options.no_psqlrc)
@@ -607,10 +601,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                     }

                     if (!result)
-                    {
-                        pg_log_fatal("could not set printing parameter \"%s\"", value);
-                        exit(EXIT_FAILURE);
-                    }
+                        pg_fatal("could not set printing parameter \"%s\"", value);

                     free(value);
                     break;
@@ -716,10 +707,10 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                 break;
             default:
         unknown_option:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        pset.progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.",
+                                  pset.progname);
                 exit(EXIT_FAILURE);
-                break;
         }
     }

@@ -781,10 +772,7 @@ process_psqlrc(char *argv0)
     char       *envrc = getenv("PSQLRC");

     if (find_my_exec(argv0, my_exec_path) < 0)
-    {
-        pg_log_fatal("could not find own program executable");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not find own program executable");

     get_etc_path(my_exec_path, etc_path);

diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 4c97bd41d7..df1766679b 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -109,7 +109,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -128,7 +129,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -144,16 +145,10 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot cluster all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot cluster all databases and a specific one at the same time");

         if (tables.head != NULL)
-        {
-            pg_log_error("cannot cluster specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot cluster specific table(s) in all databases");

         cparams.dbname = maintenance_db;

diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index b0c6805bc9..89a1a49bd5 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -120,7 +120,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -139,22 +140,16 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 2]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

     if (locale)
     {
         if (lc_ctype)
-        {
-            pg_log_error("only one of --locale and --lc-ctype can be specified");
-            exit(1);
-        }
+            pg_fatal("only one of --locale and --lc-ctype can be specified");
         if (lc_collate)
-        {
-            pg_log_error("only one of --locale and --lc-collate can be specified");
-            exit(1);
-        }
+            pg_fatal("only one of --locale and --lc-collate can be specified");
         lc_ctype = locale;
         lc_collate = locale;
     }
@@ -162,10 +157,7 @@ main(int argc, char *argv[])
     if (encoding)
     {
         if (pg_char_to_encoding(encoding) < 0)
-        {
-            pg_log_error("\"%s\" is not a valid encoding name", encoding);
-            exit(1);
-        }
+            pg_fatal("\"%s\" is not a valid encoding name", encoding);
     }

     if (dbname == NULL)
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index d6ce04a809..bfba0d09d1 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -166,7 +166,8 @@ main(int argc, char *argv[])
                 interactive = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -181,7 +182,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -274,11 +275,8 @@ main(int argc, char *argv[])
                                                    newuser,
                                                    NULL);
         if (!encrypted_password)
-        {
-            pg_log_error("password encryption failed: %s",
-                         PQerrorMessage(conn));
-            exit(1);
-        }
+            pg_fatal("password encryption failed: %s",
+                     PQerrorMessage(conn));
         appendStringLiteralConn(&sql, encrypted_password, conn);
         PQfreemem(encrypted_password);
     }
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index 7e321dd11b..afc00dac78 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -100,7 +100,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -109,7 +110,7 @@ main(int argc, char *argv[])
     {
         case 0:
             pg_log_error("missing required argument database name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         case 1:
             dbname = argv[optind];
@@ -117,7 +118,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index dfe4a5088c..82c1f35ab2 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -91,7 +91,8 @@ main(int argc, char *argv[])
                 /* this covers the long options */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -106,7 +107,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -119,7 +120,7 @@ main(int argc, char *argv[])
         else
         {
             pg_log_error("missing required argument role name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c
index a7653b3eaf..1aa834742d 100644
--- a/src/bin/scripts/pg_isready.c
+++ b/src/bin/scripts/pg_isready.c
@@ -93,7 +93,8 @@ main(int argc, char **argv)
                 pguser = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);

                 /*
                  * We need to make sure we don't return 1 here because someone
@@ -107,7 +108,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);

         /*
          * We need to make sure we don't return 1 here because someone
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index c292d43203..f3b03ec325 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -170,7 +170,8 @@ main(int argc, char *argv[])
                 tablespace = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -189,7 +190,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -205,30 +206,15 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot reindex all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex all databases and a specific one at the same time");
         if (syscatalog)
-        {
-            pg_log_error("cannot reindex all databases and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex all databases and system catalogs at the same time");
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific schema(s) in all databases");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific table(s) in all databases");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific index(es) in all databases");

         cparams.dbname = maintenance_db;

@@ -238,26 +224,14 @@ main(int argc, char *argv[])
     else if (syscatalog)
     {
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific schema(s) and system catalogs at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific table(s) and system catalogs at the same time");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific index(es) and system catalogs at the same time");

         if (concurrentCons > 1)
-        {
-            pg_log_error("cannot use multiple jobs to reindex system catalogs");
-            exit(1);
-        }
+            pg_fatal("cannot use multiple jobs to reindex system catalogs");

         if (dbname == NULL)
         {
@@ -283,10 +257,7 @@ main(int argc, char *argv[])
          * depending on the same relation.
          */
         if (concurrentCons > 1 && indexes.head != NULL)
-        {
-            pg_log_error("cannot use multiple jobs to reindex indexes");
-            exit(1);
-        }
+            pg_fatal("cannot use multiple jobs to reindex indexes");

         if (dbname == NULL)
         {
@@ -349,17 +320,15 @@ reindex_one_database(ConnParams *cparams, ReindexType type,
     if (concurrently && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "concurrently", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "concurrently", "12");
     }

     if (tablespace && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "tablespace", "14");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "tablespace", "14");
     }

     if (!parallel)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 4f6917fd39..92f1ffe147 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -237,7 +237,8 @@ main(int argc, char *argv[])
                 vacopts.process_toast = false;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -256,54 +257,33 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (vacopts.analyze_only)
     {
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "full");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "full");
         if (vacopts.freeze)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "freeze");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "freeze");
         if (vacopts.disable_page_skipping)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "disable-page-skipping");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "disable-page-skipping");
         if (vacopts.no_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-index-cleanup");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-index-cleanup");
         if (vacopts.force_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "force-index-cleanup");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "force-index-cleanup");
         if (!vacopts.do_truncate)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-truncate");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-truncate");
         if (!vacopts.process_toast)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-process-toast");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-process-toast");
         /* allow 'and_analyze' with 'analyze_only' */
     }

@@ -311,26 +291,17 @@ main(int argc, char *argv[])
     if (vacopts.parallel_workers >= 0)
     {
         if (vacopts.analyze_only)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "parallel");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "parallel");
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing full vacuum",
-                         "parallel");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing full vacuum",
+                     "parallel");
     }

     /* Prohibit --no-index-cleanup and --force-index-cleanup together */
     if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
-    {
-        pg_log_error("cannot use the \"%s\" option with the \"%s\" option",
-                     "no-index-cleanup", "force-index-cleanup");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
+                 "no-index-cleanup", "force-index-cleanup");

     /* fill cparams except for dbname, which is set below */
     cparams.pghost = host;
@@ -348,15 +319,9 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot vacuum all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot vacuum all databases and a specific one at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot vacuum specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot vacuum specific table(s) in all databases");

         cparams.dbname = maintenance_db;

@@ -457,71 +422,56 @@ vacuum_one_database(ConnParams *cparams,
     if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "disable-page-skipping", "9.6");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "disable-page-skipping", "9.6");
     }

     if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-index-cleanup", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-index-cleanup", "12");
     }

     if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "force-index-cleanup", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "force-index-cleanup", "12");
     }

     if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-truncate", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-truncate", "12");
     }

     if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-process-toast", "14");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-process-toast", "14");
     }

     if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "skip-locked", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "skip-locked", "12");
     }

     if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--min-xid-age", "9.6");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--min-xid-age", "9.6");

     if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--min-mxid-age", "9.6");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--min-mxid-age", "9.6");

     if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--parallel", "13");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--parallel", "13");

     if (!quiet)
     {
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 348f046a44..4c0da6e124 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -70,11 +70,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                         ControlFilePath)));
 #else
     if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
-    {
-        pg_log_fatal("could not open file \"%s\" for reading: %m",
-                     ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m",
+                 ControlFilePath);
 #endif

     r = read(fd, ControlFile, sizeof(ControlFileData));
@@ -86,10 +83,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                     (errcode_for_file_access(),
                      errmsg("could not read file \"%s\": %m", ControlFilePath)));
 #else
-        {
-            pg_log_fatal("could not read file \"%s\": %m", ControlFilePath);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not read file \"%s\": %m", ControlFilePath);
 #endif
         else
 #ifndef FRONTEND
@@ -98,11 +92,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                      errmsg("could not read file \"%s\": read %d of %zu",
                             ControlFilePath, r, sizeof(ControlFileData))));
 #else
-        {
-            pg_log_fatal("could not read file \"%s\": read %d of %zu",
-                         ControlFilePath, r, sizeof(ControlFileData));
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not read file \"%s\": read %d of %zu",
+                     ControlFilePath, r, sizeof(ControlFileData));
 #endif
     }

@@ -114,10 +105,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                         ControlFilePath)));
 #else
     if (close(fd) != 0)
-    {
-        pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif

     /* Check the CRC. */
@@ -203,10 +191,7 @@ update_controlfile(const char *DataDir,
 #else
     if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
                    pg_file_create_mode)) == -1)
-    {
-        pg_log_fatal("could not open file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not open file \"%s\": %m", ControlFilePath);
 #endif

     errno = 0;
@@ -225,8 +210,7 @@ update_controlfile(const char *DataDir,
                  errmsg("could not write file \"%s\": %m",
                         ControlFilePath)));
 #else
-        pg_log_fatal("could not write file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
+        pg_fatal("could not write file \"%s\": %m", ControlFilePath);
 #endif
     }
 #ifndef FRONTEND
@@ -245,10 +229,7 @@ update_controlfile(const char *DataDir,
         pgstat_report_wait_end();
 #else
         if (fsync(fd) != 0)
-        {
-            pg_log_fatal("could not fsync file \"%s\": %m", ControlFilePath);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
 #endif
     }

@@ -260,8 +241,7 @@ update_controlfile(const char *DataDir,
                  errmsg("could not close file \"%s\": %m",
                         ControlFilePath)));
 #else
-        pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
+        pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif
     }
 }
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7138068633..19d308ad1f 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -300,7 +300,7 @@ fsync_fname(const char *fname, bool isdir)
      */
     if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL)))
     {
-        pg_log_fatal("could not fsync file \"%s\": %m", fname);
+        pg_log_error("could not fsync file \"%s\": %m", fname);
         (void) close(fd);
         exit(EXIT_FAILURE);
     }
@@ -370,7 +370,7 @@ durable_rename(const char *oldfile, const char *newfile)
     {
         if (fsync(fd) != 0)
         {
-            pg_log_fatal("could not fsync file \"%s\": %m", newfile);
+            pg_log_error("could not fsync file \"%s\": %m", newfile);
             close(fd);
             exit(EXIT_FAILURE);
         }
@@ -448,7 +448,7 @@ get_dirent_type(const char *path,
         {
             result = PGFILETYPE_ERROR;
 #ifdef FRONTEND
-            pg_log_generic(elevel, "could not stat file \"%s\": %m", path);
+            pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
 #else
             ereport(elevel,
                     (errcode_for_file_access(),
diff --git a/src/common/logging.c b/src/common/logging.c
index 9a076bb812..18d6669f27 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -151,6 +151,9 @@ pg_logging_init(const char *argv0)
     }
 }

+/*
+ * Change the logging flags.
+ */
 void
 pg_logging_config(int new_flags)
 {
@@ -194,17 +197,19 @@ pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)
 }

 void
-pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
+pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+               const char *pg_restrict fmt,...)
 {
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(level, fmt, ap);
+    pg_log_generic_v(level, part, fmt, ap);
     va_end(ap);
 }

 void
-pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
+pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                 const char *pg_restrict fmt, va_list ap)
 {
     int            save_errno = errno;
     const char *filename = NULL;
@@ -232,7 +237,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     fmt = _(fmt);

-    if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+    if (part == PG_LOG_PRIMARY &&
+        (!(log_flags & PG_LOG_FLAG_TERSE) || filename))
     {
         if (sgr_locus)
             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
@@ -251,30 +257,34 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     if (!(log_flags & PG_LOG_FLAG_TERSE))
     {
-        switch (level)
+        switch (part)
         {
-            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);
+            case PG_LOG_PRIMARY:
+                switch (level)
+                {
+                    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;
+                }
                 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);
+            case PG_LOG_DETAIL:
+                fprintf(stderr, _("detail: "));
                 break;
-            default:
+            case PG_LOG_HINT:
+                fprintf(stderr, _("hint: "));
                 break;
         }
     }
diff --git a/src/common/restricted_token.c b/src/common/restricted_token.c
index 48b1ce0585..82b74b565e 100644
--- a/src/common/restricted_token.c
+++ b/src/common/restricted_token.c
@@ -190,10 +190,7 @@ get_restricted_token(void)
             WaitForSingleObject(pi.hProcess, INFINITE);

             if (!GetExitCodeProcess(pi.hProcess, &x))
-            {
-                pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError());
-                exit(1);
-            }
+                pg_fatal("could not get exit code from subprocess: error code %lu", GetLastError());
             exit(x);
         }
         pg_free(cmdline);
diff --git a/src/fe_utils/archive.c b/src/fe_utils/archive.c
index 361c1c25ea..53d42c2be4 100644
--- a/src/fe_utils/archive.c
+++ b/src/fe_utils/archive.c
@@ -49,10 +49,7 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
     xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
                                          xlogfname, NULL);
     if (xlogRestoreCmd == NULL)
-    {
-        pg_log_fatal("cannot use restore_command with %%r placeholder");
-        exit(1);
-    }
+        pg_fatal("cannot use restore_command with %%r placeholder");

     /*
      * Execute restore_command, which should copy the missing file from
@@ -70,22 +67,16 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         if (stat(xlogpath, &stat_buf) == 0)
         {
             if (expectedSize > 0 && stat_buf.st_size != expectedSize)
-            {
-                pg_log_fatal("unexpected file size for \"%s\": %lld instead of %lld",
-                             xlogfname, (long long int) stat_buf.st_size,
-                             (long long int) expectedSize);
-                exit(1);
-            }
+                pg_fatal("unexpected file size for \"%s\": %lld instead of %lld",
+                         xlogfname, (long long int) stat_buf.st_size,
+                         (long long int) expectedSize);
             else
             {
                 int            xlogfd = open(xlogpath, O_RDONLY | PG_BINARY, 0);

                 if (xlogfd < 0)
-                {
-                    pg_log_fatal("could not open file \"%s\" restored from archive: %m",
-                                 xlogpath);
-                    exit(1);
-                }
+                    pg_fatal("could not open file \"%s\" restored from archive: %m",
+                             xlogpath);
                 else
                     return xlogfd;
             }
@@ -93,11 +84,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         else
         {
             if (errno != ENOENT)
-            {
-                pg_log_fatal("could not stat file \"%s\": %m",
-                             xlogpath);
-                exit(1);
-            }
+                pg_fatal("could not stat file \"%s\": %m",
+                         xlogpath);
         }
     }

@@ -108,11 +96,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
      * fatal too.
      */
     if (wait_result_is_any_signal(rc, true))
-    {
-        pg_log_fatal("restore_command failed: %s",
-                     wait_result_to_str(rc));
-        exit(1);
-    }
+        pg_fatal("restore_command failed: %s",
+                 wait_result_to_str(rc));

     /*
      * The file is not available, so just let the caller decide what to do
diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
index a30c66f13a..f2e583f9fa 100644
--- a/src/fe_utils/connect_utils.c
+++ b/src/fe_utils/connect_utils.c
@@ -88,11 +88,8 @@ connectDatabase(const ConnParams *cparams, const char *progname,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s: out of memory",
-                         cparams->dbname);
-            exit(1);
-        }
+            pg_fatal("could not connect to database %s: out of memory",
+                     cparams->dbname);

         /*
          * No luck?  Trying asking (again) for a password.
@@ -117,8 +114,7 @@ connectDatabase(const ConnParams *cparams, const char *progname,
             PQfinish(conn);
             return NULL;
         }
-        pg_log_error("%s", PQerrorMessage(conn));
-        exit(1);
+        pg_fatal("%s", PQerrorMessage(conn));
     }

     /* Start strict; callers may override this. */
diff --git a/src/fe_utils/parallel_slot.c b/src/fe_utils/parallel_slot.c
index 5896a8a6ca..684327885d 100644
--- a/src/fe_utils/parallel_slot.c
+++ b/src/fe_utils/parallel_slot.c
@@ -298,10 +298,7 @@ connect_slot(ParallelSlotArray *sa, int slotno, const char *dbname)
     sa->cparams->override_dbname = old_override;

     if (PQsocket(slot->connection) >= FD_SETSIZE)
-    {
-        pg_log_fatal("too many jobs for this platform");
-        exit(1);
-    }
+        pg_fatal("too many jobs for this platform");

     /* Setup the connection using the supplied command, if any. */
     if (sa->initcmd)
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c
index 0b31b33f17..2fc6e2405b 100644
--- a/src/fe_utils/query_utils.c
+++ b/src/fe_utils/query_utils.c
@@ -31,7 +31,7 @@ executeQuery(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
@@ -56,7 +56,7 @@ executeCommand(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
index 9407e76bba..c9a423038a 100644
--- a/src/fe_utils/recovery_gen.c
+++ b/src/fe_utils/recovery_gen.c
@@ -31,10 +31,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     contents = createPQExpBuffer();
     if (!contents)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     /*
      * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
@@ -45,10 +42,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     connOptions = PQconninfo(pgconn);
     if (connOptions == NULL)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     initPQExpBuffer(&conninfo_buf);
     for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
@@ -73,10 +67,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
         appendConnStrVal(&conninfo_buf, opt->val);
     }
     if (PQExpBufferDataBroken(conninfo_buf))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     /*
      * Escape the connection string, so that it can be put in the config file.
@@ -96,10 +87,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
     }

     if (PQExpBufferBroken(contents))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     PQconninfoFree(connOptions);

@@ -130,16 +118,10 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)

     cf = fopen(filename, use_recovery_conf ? "w" : "a");
     if (cf == NULL)
-    {
-        pg_log_error("could not open file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", filename);

     if (fwrite(contents->data, contents->len, 1, cf) != 1)
-    {
-        pg_log_error("could not write to file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not write to file \"%s\": %m", filename);

     fclose(cf);

@@ -148,10 +130,7 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
         snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
         cf = fopen(filename, "w");
         if (cf == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", filename);
-            exit(1);
-        }
+            pg_fatal("could not create file \"%s\": %m", filename);

         fclose(cf);
     }
@@ -167,9 +146,6 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");
     return result;
 }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index 43cc79afa8..e213bb70d0 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
 enum pg_log_level
 {
     /*
-     * Not initialized yet
+     * Not initialized yet (not to be used as an actual message log level).
      */
     PG_LOG_NOTSET = 0,

@@ -43,20 +43,42 @@ enum pg_log_level
     PG_LOG_ERROR,

     /*
-     * Severe errors that cause program termination.  (One-shot programs may
-     * chose to label even fatal errors as merely "errors".  The distinction
-     * is up to the program.)
-     */
-    PG_LOG_FATAL,
-
-    /*
-     * Turn all logging off.
+     * Turn all logging off (not to be used as an actual message log level).
      */
     PG_LOG_OFF,
 };

+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
 extern enum pg_log_level __pg_log_level;

+/*
+ * A log message can have several parts.  The primary message is required,
+ * others are optional.  When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+    /*
+     * The primary message.  Try to keep it to one line; follow the backend's
+     * style guideline for primary messages.
+     */
+    PG_LOG_PRIMARY,
+
+    /*
+     * Additional detail.  Follow the backend's style guideline for detail
+     * messages.
+     */
+    PG_LOG_DETAIL,
+
+    /*
+     * Hint (not guaranteed correct) about how to fix the problem.  Follow the
+     * backend's style guideline for hint messages.
+     */
+    PG_LOG_HINT,
+};
+
 /*
  * Kind of a hack to be able to produce the psql output exactly as required by
  * the regression tests.
@@ -70,27 +92,84 @@ void        pg_logging_increase_verbosity(void);
 void        pg_logging_set_pre_callback(void (*cb) (void));
 void        pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));

-void        pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void        pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2,
0);
+void        pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+                           const char *pg_restrict fmt,...)
+            pg_attribute_printf(3, 4);
+void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                             const char *pg_restrict fmt, va_list ap)
+            pg_attribute_printf(3, 0);
+
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)

-#define pg_log_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
     } while(0)

-#define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
-        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_detail(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_hint(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+    } while(0)
+
+/*
+ * A common shortcut: pg_log_error() and immediately exit(1).
+ */
+#define pg_fatal(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit(1); \
     } while(0)

 #endif                            /* COMMON_LOGGING_H */
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 8192927010..4a3d0ec2c5 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -293,8 +293,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
 #define SIMPLEHASH_H

 #ifdef FRONTEND
-#define sh_error(...) \
-    do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#define sh_error(...) pg_fatal(__VA_ARGS__)
 #define sh_log(...) pg_log_info(__VA_ARGS__)
 #else
 #define sh_error(...) elog(ERROR, __VA_ARGS__)
diff --git a/src/nls-global.mk b/src/nls-global.mk
index 53129f0a04..c1f7982300 100644
--- a/src/nls-global.mk
+++ b/src/nls-global.mk
@@ -72,10 +72,16 @@ BACKEND_COMMON_GETTEXT_FLAGS = \
 FRONTEND_COMMON_GETTEXT_FILES = $(top_srcdir)/src/common/logging.c

 FRONTEND_COMMON_GETTEXT_TRIGGERS = \
-    pg_log_fatal pg_log_error pg_log_warning pg_log_info pg_log_generic:2 pg_log_generic_v:2
+    pg_log_error pg_log_error_detail pg_log_error_hint \
+    pg_log_warning pg_log_warning_detail pg_log_warning_hint \
+    pg_log_info pg_log_info_detail pg_log_info_hint \
+    pg_fatal pg_log_generic:3 pg_log_generic_v:3

 FRONTEND_COMMON_GETTEXT_FLAGS = \
-    pg_log_fatal:1:c-format pg_log_error:1:c-format pg_log_warning:1:c-format pg_log_info:1:c-format
pg_log_generic:2:c-formatpg_log_generic_v:2:c-format 
+    pg_log_error:1:c-format pg_log_error_detail:1:c-format pg_log_error_hint:1:c-format \
+    pg_log_warning:1:c-format pg_log_warning_detail:1:c-format pg_log_warning_hint:1:c-format \
+    pg_log_info:1:c-format pg_log_info_detail:1:c-format pg_log_info_hint:1:c-format \
+    pg_fatal:1:c-format pg_log_generic:3:c-format pg_log_generic_v:3:c-format


 all-po: $(MO_FILES)

Re: Frontend error logging style

От
Michael Paquier
Дата:
On Fri, Feb 25, 2022 at 12:15:25PM -0500, Tom Lane wrote:
> I feel that the reasonable alternatives are either to drop the FATAL
> log level, or try to make it actually mean something by consistently
> using it for errors that are indeed fatal.  I had a go at doing the
> latter, but eventually concluded that that way madness lies.  It's
> not too hard to use "pg_log_fatal" when there's an exit(1) right
> after it, but there are quite a lot of cases where a subroutine
> reports an error and returns a failure code to its caller, whereupon
> the caller exits.  Either the subroutine has to make an unwarranted
> assumption about what its callers will do, or we need to make an API
> change to allow the subroutine itself to exit(), or we are going to
> present a user experience that is inconsistently different depending
> on internal implementation details.

Nice code cut.

> I conclude that we ought to drop the separate FATAL level and just
> use ERROR instead.

FWIW, I have found the uses of pg_log_error() and pg_log_fatal()
rather confusing in some of the src/bin/ tools, so I am in favor of
removing completely one or the other, and getting rid of FATAL is the
best choice.

> The attached revision does that, standardizes on pg_fatal() as the
> abbreviation for pg_log_error() + exit(1), and invents detail/hint
> features as per previous discussion.

+#define pg_log_warning_hint(...) do { \
+       if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+           pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__);
\
    } while(0)

Hm.  I am not sure to like much this abstraction by having so many
macros that understand the log level through their name.  Wouldn't it
be cleaner to have only one pg_log_hint() and one pg_log_detail() with
the log level passed as argument of the macro?
--
Michael

Вложения

Re: Frontend error logging style

От
Tom Lane
Дата:
Michael Paquier <michael@paquier.xyz> writes:
> Hm.  I am not sure to like much this abstraction by having so many
> macros that understand the log level through their name.  Wouldn't it
> be cleaner to have only one pg_log_hint() and one pg_log_detail() with
> the log level passed as argument of the macro?

Mmm ... that doesn't sound better to me.  I think it wouldn't be
obvious that pg_log_warning and pg_log_hint are fundamentally
different sorts of things: in the first, "warning" refers to
an error severity level, while in the second, "hint" refers to
a message component.  I'm not wedded to the way I did it in this
patch, but I think we ought to maintain a notational distinction
between those two sorts of concepts.

            regards, tom lane



Re: Frontend error logging style

От
Andres Freund
Дата:
Hi,

On 2022-02-25 12:15:25 -0500, Tom Lane wrote:
> The attached revision does that, standardizes on pg_fatal() as the
> abbreviation for pg_log_error() + exit(1), and invents detail/hint
> features as per previous discussion.

This unfortunately has had some bitrot: http://cfbot.cputube.org/patch_37_3574.log

Marked as waiting-on-author.

Greetings,

Andres



Re: Frontend error logging style

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2022-02-25 12:15:25 -0500, Tom Lane wrote:
>> The attached revision does that, standardizes on pg_fatal() as the
>> abbreviation for pg_log_error() + exit(1), and invents detail/hint
>> features as per previous discussion.

> This unfortunately has had some bitrot: http://cfbot.cputube.org/patch_37_3574.log

Yeah, I know.  That patch touches enough places that it's sure to
break every few days during a commitfest.  I'm not real excited about
maintaining it reactively.  The flip side of the coin is that pushing
it will doubtless break a lot of other uncommitted patches.  So I'm
not sure how to proceed.

Maybe the plan could be to push it at the end of the commitfest?  But
what I'd really like at the moment is buy-in or rejection of the whole
concept, so I know whether it's worth spending more time on.

            regards, tom lane



Re: Frontend error logging style

От
Andres Freund
Дата:
Hi,

On 2022-03-21 22:00:37 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > This unfortunately has had some bitrot: http://cfbot.cputube.org/patch_37_3574.log
> 
> Yeah, I know.  That patch touches enough places that it's sure to
> break every few days during a commitfest.  I'm not real excited about
> maintaining it reactively.

Makes sense. I'd leave it on waiting-on-author then, so that reviewers looking
through the CF don't need to look at it?


> Maybe the plan could be to push it at the end of the commitfest?

Would make sense to me.

Arguably parts of it could be committed sooner than that, e.g. exit(-1). But
that'd not make it meaningfully easier to maintain, so ...


> But what I'd really like at the moment is buy-in or rejection of the whole
> concept, so I know whether it's worth spending more time on.

I'm +1. I've not carefully looked through every single changed callsite, but
IMO it looks like a clear improvement.

Greetings,

Andres Freund



Re: Frontend error logging style

От
Tom Lane
Дата:
Here's a rebase up to today's HEAD.  I've fixed the merge problems,
but there may be some stray new error calls that could be converted
to use pg_fatal() and haven't been.  I don't want to do a full
fresh scan of the code until we're about ready to commit this.

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 65cce49993..a62a5eedb1 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -182,16 +182,17 @@ get_opts(int argc, char **argv, struct options *my_opts)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (optind < argc)
     {
-        fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
-                progname, argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error("too many command-line arguments (first is \"%s\")",
+                     argv[optind]);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
 }
@@ -328,11 +329,8 @@ sql_conn(struct options *my_opts)
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s",
-                         my_opts->dbname);
-            exit(1);
-        }
+            pg_fatal("could not connect to database %s",
+                     my_opts->dbname);

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -359,7 +357,7 @@ sql_conn(struct options *my_opts)
                      PQerrorMessage(conn));
         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }
     PQclear(res);

@@ -390,11 +388,11 @@ sql_exec(PGconn *conn, const char *todo, bool quiet)
     if (!res || PQresultStatus(res) > 2)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", todo);
+        pg_log_error_detail("Query was: %s", todo);

         PQclear(res);
         PQfinish(conn);
-        exit(-1);
+        exit(1);
     }

     /* get the number of fields */
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index d15edca454..b7c8f2c805 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -492,19 +492,13 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
             case 'h':
                 param.pg_host = pg_strdup(optarg);
                 break;
             case 'l':
                 param.transaction_limit = strtol(optarg, NULL, 10);
                 if (param.transaction_limit < 0)
-                {
-                    pg_log_error("transaction limit must not be negative (0 disables)");
-                    exit(1);
-                }
+                    pg_fatal("transaction limit must not be negative (0 disables)");
                 break;
             case 'n':
                 param.dry_run = 1;
@@ -513,10 +507,7 @@ main(int argc, char **argv)
             case 'p':
                 port = strtol(optarg, NULL, 10);
                 if ((port < 1) || (port > 65535))
-                {
-                    pg_log_error("invalid port number: %s", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid port number: %s", optarg);
                 param.pg_port = pg_strdup(optarg);
                 break;
             case 'U':
@@ -532,7 +523,8 @@ main(int argc, char **argv)
                 param.pg_prompt = TRI_YES;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -541,7 +533,7 @@ main(int argc, char **argv)
     if (optind >= argc)
     {
         pg_log_error("missing required argument: database name");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 5e36943ef3..55257e35cb 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -331,10 +331,7 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");
     return result;
 }

@@ -464,10 +461,7 @@ readfile(const char *path)
     int            n;

     if ((infile = fopen(path, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m", path);

     initStringInfo(&line);

@@ -508,24 +502,15 @@ writefile(char *path, char **lines)
     char      **line;

     if ((out_file = fopen(path, "w")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     for (line = lines; *line != NULL; line++)
     {
         if (fputs(*line, out_file) < 0)
-        {
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
-        }
+            pg_fatal("could not write file \"%s\": %m", path);
         free(*line);
     }
     if (fclose(out_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
 }

 /*
@@ -611,9 +596,7 @@ get_id(void)
     if (geteuid() == 0)            /* 0 is root's uid */
     {
         pg_log_error("cannot be run as root");
-        fprintf(stderr,
-                _("Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n"
-                  "own the server process.\n"));
+        pg_log_error_hint("Please log in (using, e.g., \"su\") as the (unprivileged) user that will own the server
process.");
         exit(1);
     }
 #endif
@@ -645,9 +628,8 @@ get_encoding_id(const char *encoding_name)
         if ((enc = pg_valid_server_encoding(encoding_name)) >= 0)
             return enc;
     }
-    pg_log_error("\"%s\" is not a valid server encoding name",
-                 encoding_name ? encoding_name : "(null)");
-    exit(1);
+    pg_fatal("\"%s\" is not a valid server encoding name",
+             encoding_name ? encoding_name : "(null)");
 }

 /*
@@ -791,25 +773,19 @@ check_input(char *path)
         if (errno == ENOENT)
         {
             pg_log_error("file \"%s\" does not exist", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         else
         {
             pg_log_error("could not access file \"%s\": %m", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe invocation option -L."); 
         }
         exit(1);
     }
     if (!S_ISREG(statbuf.st_mode))
     {
         pg_log_error("file \"%s\" is not a regular file", path);
-        fprintf(stderr,
-                _("This might mean you have a corrupted installation or identified\n"
-                  "the wrong directory with the invocation option -L.\n"));
+        pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with
theinvocation option -L."); 
         exit(1);
     }
 }
@@ -830,16 +806,10 @@ write_version_file(const char *extrapath)
         path = psprintf("%s/%s/PG_VERSION", pg_data, extrapath);

     if ((version_file = fopen(path, PG_BINARY_W)) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     if (fprintf(version_file, "%s\n", PG_MAJORVERSION) < 0 ||
         fclose(version_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -856,15 +826,9 @@ set_null_conf(void)
     path = psprintf("%s/postgresql.conf", pg_data);
     conf_file = fopen(path, PG_BINARY_W);
     if (conf_file == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for writing: %m", path);
     if (fclose(conf_file))
-    {
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -1218,10 +1182,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     /*
      * create the automatic configuration file to store the configuration
@@ -1237,10 +1198,7 @@ setup_config(void)

     writefile(path, autoconflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1323,10 +1281,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1338,10 +1293,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1375,9 +1327,8 @@ bootstrap_template1(void)
     {
         pg_log_error("input file \"%s\" does not belong to PostgreSQL %s",
                      bki_file, PG_VERSION);
-        fprintf(stderr,
-                _("Check your installation or specify the correct path "
-                  "using the option -L.\n"));
+        pg_log_error_hint("Check your installation or specify the correct path "
+                          "using the option -L.");
         exit(1);
     }

@@ -1503,21 +1454,17 @@ get_su_pwd(void)
         FILE       *pwf = fopen(pwfilename, "r");

         if (!pwf)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
-                         pwfilename);
-            exit(1);
-        }
+            pg_fatal("could not open file \"%s\" for reading: %m",
+                     pwfilename);
         pwd1 = pg_get_line(pwf, NULL);
         if (!pwd1)
         {
             if (ferror(pwf))
-                pg_log_error("could not read password from file \"%s\": %m",
-                             pwfilename);
+                pg_fatal("could not read password from file \"%s\": %m",
+                         pwfilename);
             else
-                pg_log_error("password file \"%s\" is empty",
-                             pwfilename);
-            exit(1);
+                pg_fatal("password file \"%s\" is empty",
+                         pwfilename);
         }
         fclose(pwf);

@@ -2057,10 +2004,7 @@ check_locale_name(int category, const char *locale, char **canonname)

     save = setlocale(category, NULL);
     if (!save)
-    {
-        pg_log_error("setlocale() failed");
-        exit(1);
-    }
+        pg_fatal("setlocale() failed");

     /* save may be pointing at a modifiable scratch variable, so copy it. */
     save = pg_strdup(save);
@@ -2078,17 +2022,14 @@ check_locale_name(int category, const char *locale, char **canonname)

     /* restore old value. */
     if (!setlocale(category, save))
-    {
-        pg_log_error("failed to restore old locale \"%s\"", save);
-        exit(1);
-    }
+        pg_fatal("failed to restore old locale \"%s\"", save);
     free(save);

     /* complain if locale wasn't valid */
     if (res == NULL)
     {
         if (*locale)
-            pg_log_error("invalid locale name \"%s\"", locale);
+            pg_fatal("invalid locale name \"%s\"", locale);
         else
         {
             /*
@@ -2099,9 +2040,8 @@ check_locale_name(int category, const char *locale, char **canonname)
              * setlocale's behavior is implementation-specific, it's hard to
              * be sure what it didn't like.  Print a safe generic message.
              */
-            pg_log_error("invalid locale settings; check LANG and LC_* environment variables");
+            pg_fatal("invalid locale settings; check LANG and LC_* environment variables");
         }
-        exit(1);
     }
 }

@@ -2127,15 +2067,14 @@ check_locale_encoding(const char *locale, int user_enc)
           user_enc == PG_SQL_ASCII))
     {
         pg_log_error("encoding mismatch");
-        fprintf(stderr,
-                _("The encoding you selected (%s) and the encoding that the\n"
-                  "selected locale uses (%s) do not match.  This would lead to\n"
-                  "misbehavior in various character string processing functions.\n"
-                  "Rerun %s and either do not specify an encoding explicitly,\n"
-                  "or choose a matching combination.\n"),
-                pg_encoding_to_char(user_enc),
-                pg_encoding_to_char(locale_enc),
-                progname);
+        pg_log_error_detail("The encoding you selected (%s) and the encoding that the "
+                            "selected locale uses (%s) do not match. This would lead to "
+                            "misbehavior in various character string processing functions.",
+                            pg_encoding_to_char(user_enc),
+                            pg_encoding_to_char(locale_enc));
+        pg_log_error_hint("Rerun %s and either do not specify an encoding explicitly, "
+                          "or choose a matching combination.",
+                          progname);
         return false;
     }
     return true;
@@ -2195,10 +2134,7 @@ setlocales(void)
     if (locale_provider == COLLPROVIDER_ICU)
     {
         if (!icu_locale)
-        {
-            pg_log_error("ICU locale must be specified");
-            exit(1);
-        }
+            pg_fatal("ICU locale must be specified");

         /*
          * In supported builds, the ICU locale ID will be checked by the
@@ -2206,7 +2142,8 @@ setlocales(void)
          */
 #ifndef USE_ICU
         pg_log_error("ICU is not supported in this build");
-        fprintf(stderr, _("You need to rebuild PostgreSQL using %s.\n"), "--with-icu");
+        pg_log_error_hint("You need to rebuild PostgreSQL using %s.",
+                          "--with-icu");
         exit(1);
 #endif
     }
@@ -2288,9 +2225,8 @@ check_authmethod_valid(const char *authmethod, const char *const *valid_methods,
                 return;
     }

-    pg_log_error("invalid authentication method \"%s\" for \"%s\" connections",
-                 authmethod, conntype);
-    exit(1);
+    pg_fatal("invalid authentication method \"%s\" for \"%s\" connections",
+             authmethod, conntype);
 }

 static void
@@ -2303,10 +2239,7 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
          strcmp(authmethodhost, "password") == 0 ||
          strcmp(authmethodhost, "scram-sha-256") == 0) &&
         !(pwprompt || pwfilename))
-    {
-        pg_log_error("must specify a password for the superuser to enable password authentication");
-        exit(1);
-    }
+        pg_fatal("must specify a password for the superuser to enable password authentication");
 }


@@ -2326,10 +2259,9 @@ setup_pgdata(void)
         else
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr,
-                    _("You must identify the directory where the data for this database system\n"
-                      "will reside.  Do this with either the invocation option -D or the\n"
-                      "environment variable PGDATA.\n"));
+            pg_log_error_hint("You must identify the directory where the data for this database system "
+                              "will reside.  Do this with either the invocation option -D or the "
+                              "environment variable PGDATA.");
             exit(1);
         }
     }
@@ -2344,10 +2276,7 @@ setup_pgdata(void)
      * have embedded spaces.
      */
     if (setenv("PGDATA", pg_data, 1) != 0)
-    {
-        pg_log_error("could not set environment");
-        exit(1);
-    }
+        pg_fatal("could not set environment");
 }


@@ -2365,15 +2294,13 @@ setup_bin_paths(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" was found by \"%s\" "
+                         "but was not the same version as %s",
                          "postgres", full_path, progname);
+        pg_log_error_hint("Check your installation.");
         exit(1);
     }

@@ -2388,10 +2315,7 @@ setup_bin_paths(const char *argv0)
         get_share_path(backend_exec, share_path);
     }
     else if (!is_absolute_path(share_path))
-    {
-        pg_log_error("input file location must be an absolute path");
-        exit(1);
-    }
+        pg_fatal("input file location must be an absolute path");

     canonicalize_path(share_path);
 }
@@ -2442,9 +2366,8 @@ setup_locale_encoding(void)
             /* Couldn't recognize the locale's codeset */
             pg_log_error("could not find suitable encoding for locale \"%s\"",
                          lc_ctype);
-            fprintf(stderr, _("Rerun %s with the -E option.\n"), progname);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Rerun %s with the -E option.", progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         else if (!pg_valid_server_encoding_id(ctype_enc))
@@ -2463,10 +2386,10 @@ setup_locale_encoding(void)
 #else
             pg_log_error("locale \"%s\" requires unsupported encoding \"%s\"",
                          lc_ctype, pg_encoding_to_char(ctype_enc));
-            fprintf(stderr,
-                    _("Encoding \"%s\" is not allowed as a server-side encoding.\n"
-                      "Rerun %s with a different locale selection.\n"),
-                    pg_encoding_to_char(ctype_enc), progname);
+            pg_log_error_detail("Encoding \"%s\" is not allowed as a server-side encoding.",
+                                pg_encoding_to_char(ctype_enc));
+            pg_log_error_hint("Rerun %s with a different locale selection.",
+                              progname);
             exit(1);
 #endif
         }
@@ -2609,10 +2532,7 @@ create_data_directory(void)
             fflush(stdout);

             if (pg_mkdir_p(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not create directory \"%s\": %m", pg_data);
-                exit(1);
-            }
+                pg_fatal("could not create directory \"%s\": %m", pg_data);
             else
                 check_ok();

@@ -2626,11 +2546,8 @@ create_data_directory(void)
             fflush(stdout);

             if (chmod(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not change permissions of directory \"%s\": %m",
-                             pg_data);
-                exit(1);
-            }
+                pg_fatal("could not change permissions of directory \"%s\": %m",
+                         pg_data);
             else
                 check_ok();

@@ -2645,17 +2562,15 @@ create_data_directory(void)
             if (ret != 4)
                 warn_on_mount_point(ret);
             else
-                fprintf(stderr,
-                        _("If you want to create a new database system, either remove or empty\n"
-                          "the directory \"%s\" or run %s\n"
-                          "with an argument other than \"%s\".\n"),
-                        pg_data, progname, pg_data);
+                pg_log_error_hint("If you want to create a new database system, either remove or empty "
+                                  "the directory \"%s\" or run %s "
+                                  "with an argument other than \"%s\".",
+                                  pg_data, progname, pg_data);
             exit(1);            /* no further message needed */

         default:
             /* Trouble accessing directory */
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
+            pg_fatal("could not access directory \"%s\": %m", pg_data);
     }
 }

@@ -2676,10 +2591,7 @@ create_xlog_or_symlink(void)
         /* clean up xlog directory name, check it's absolute */
         canonicalize_path(xlog_dir);
         if (!is_absolute_path(xlog_dir))
-        {
-            pg_log_error("WAL directory location must be an absolute path");
-            exit(1);
-        }
+            pg_fatal("WAL directory location must be an absolute path");

         /* check if the specified xlog directory exists/is empty */
         switch ((ret = pg_check_dir(xlog_dir)))
@@ -2691,11 +2603,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (pg_mkdir_p(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not create directory \"%s\": %m",
-                                 xlog_dir);
-                    exit(1);
-                }
+                    pg_fatal("could not create directory \"%s\": %m",
+                             xlog_dir);
                 else
                     check_ok();

@@ -2709,11 +2618,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (chmod(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not change permissions of directory \"%s\": %m",
-                                 xlog_dir);
-                    exit(1);
-                }
+                    pg_fatal("could not change permissions of directory \"%s\": %m",
+                             xlog_dir);
                 else
                     check_ok();

@@ -2728,39 +2634,29 @@ create_xlog_or_symlink(void)
                 if (ret != 4)
                     warn_on_mount_point(ret);
                 else
-                    fprintf(stderr,
-                            _("If you want to store the WAL there, either remove or empty the directory\n"
-                              "\"%s\".\n"),
-                            xlog_dir);
+                    pg_log_error_hint("If you want to store the WAL there, either remove or empty the directory
\"%s\".",
+                                      xlog_dir);
                 exit(1);

             default:
                 /* Trouble accessing directory */
-                pg_log_error("could not access directory \"%s\": %m", xlog_dir);
-                exit(1);
+                pg_fatal("could not access directory \"%s\": %m", xlog_dir);
         }

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, subdirloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m",
-                         subdirloc);
-            exit(1);
-        }
+            pg_fatal("could not create symbolic link \"%s\": %m",
+                     subdirloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_fatal("symlinks are not supported on this platform");
 #endif
     }
     else
     {
         /* Without -X option, just make the subdirectory normally */
         if (mkdir(subdirloc, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m",
-                         subdirloc);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m",
+                     subdirloc);
     }

     free(subdirloc);
@@ -2771,15 +2667,12 @@ void
 warn_on_mount_point(int error)
 {
     if (error == 2)
-        fprintf(stderr,
-                _("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.");
     else if (error == 3)
-        fprintf(stderr,
-                _("It contains a lost+found directory, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a lost+found directory, perhaps due to it being a mount point.");

-    fprintf(stderr,
-            _("Using a mount point directly as the data directory is not recommended.\n"
-              "Create a subdirectory under the mount point.\n"));
+    pg_log_error_hint("Using a mount point directly as the data directory is not recommended.\n"
+                      "Create a subdirectory under the mount point.");
 }


@@ -2818,10 +2711,7 @@ initialize_data_directory(void)
          * pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
          */
         if (mkdir(path, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m", path);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m", path);

         free(path);
     }
@@ -3089,18 +2979,14 @@ main(int argc, char *argv[])
                 else if (strcmp(optarg, "libc") == 0)
                     locale_provider = COLLPROVIDER_LIBC;
                 else
-                {
-                    pg_log_error("unrecognized locale provider: %s", optarg);
-                    exit(1);
-                }
+                    pg_fatal("unrecognized locale provider: %s", optarg);
                 break;
             case 16:
                 icu_locale = pg_strdup(optarg);
                 break;
             default:
                 /* getopt_long already emitted a complaint */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -3120,17 +3006,13 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (icu_locale && locale_provider != COLLPROVIDER_ICU)
-    {
-        pg_log_error("%s cannot be specified unless locale provider \"%s\" is chosen",
-                     "--icu-locale", "icu");
-        exit(1);
-    }
+        pg_fatal("%s cannot be specified unless locale provider \"%s\" is chosen",
+                 "--icu-locale", "icu");

     atexit(cleanup_directories_atexit);

@@ -3141,10 +3023,7 @@ main(int argc, char *argv[])

         /* must check that directory is readable */
         if (pg_check_dir(pg_data) <= 0)
-        {
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
-        }
+            pg_fatal("could not access directory \"%s\": %m", pg_data);

         fputs(_("syncing data to disk ... "), stdout);
         fflush(stdout);
@@ -3154,10 +3033,7 @@ main(int argc, char *argv[])
     }

     if (pwprompt && pwfilename)
-    {
-        pg_log_error("password prompt and password file cannot be specified together");
-        exit(1);
-    }
+        pg_fatal("password prompt and password file cannot be specified together");

     check_authmethod_unspecified(&authmethodlocal);
     check_authmethod_unspecified(&authmethodhost);
@@ -3179,15 +3055,9 @@ main(int argc, char *argv[])

         /* verify that wal segment size is valid */
         if (endptr == str_wal_segment_size_mb || *endptr != '\0')
-        {
-            pg_log_error("argument of --wal-segsize must be a number");
-            exit(1);
-        }
+            pg_fatal("argument of --wal-segsize must be a number");
         if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
-        {
-            pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-            exit(1);
-        }
+            pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
     }

     get_restricted_token();
@@ -3201,10 +3071,7 @@ main(int argc, char *argv[])
         username = effective_user;

     if (strncmp(username, "pg_", 3) == 0)
-    {
-        pg_log_error("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
-        exit(1);
-    }
+        pg_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);

     printf(_("The files belonging to this database system will be owned "
              "by user \"%s\".\n"
@@ -3247,8 +3114,8 @@ main(int argc, char *argv[])
     {
         printf("\n");
         pg_log_warning("enabling \"trust\" authentication for local connections");
-        fprintf(stderr, _("You can change this by editing pg_hba.conf or using the option -A, or\n"
-                          "--auth-local and --auth-host, the next time you run initdb.\n"));
+        pg_log_warning_hint("You can change this by editing pg_hba.conf or using the option -A, or "
+                            "--auth-local and --auth-host, the next time you run initdb.");
     }

     if (!noinstructions)
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 6607f72938..90471e096d 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -202,9 +202,9 @@ static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,

 #define log_no_match(...) do { \
         if (opts.strict_names) \
-            pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+            pg_log_error(__VA_ARGS__); \
         else \
-            pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+            pg_log_warning(__VA_ARGS__); \
     } while(0)

 #define FREE_AND_SET_NULL(x) do { \
@@ -396,39 +396,24 @@ main(int argc, char *argv[])
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     opts.skip = "none";
                 else
-                {
-                    pg_log_error("invalid argument for option %s", "--skip");
-                    exit(1);
-                }
+                    pg_fatal("invalid argument for option %s", "--skip");
                 break;
             case 7:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid start block");
-                    exit(1);
-                }
+                    pg_fatal("invalid start block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("start block out of bounds");
-                    exit(1);
-                }
+                    pg_fatal("start block out of bounds");
                 opts.startblock = optval;
                 break;
             case 8:
                 errno = 0;
                 optval = strtoul(optarg, &endptr, 10);
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("invalid end block");
-                    exit(1);
-                }
+                    pg_fatal("invalid end block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("end block out of bounds");
-                    exit(1);
-                }
+                    pg_fatal("end block out of bounds");
                 opts.endblock = optval;
                 break;
             case 9:
@@ -450,18 +435,14 @@ main(int argc, char *argv[])
                     opts.install_schema = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr,
-                        _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }

     if (opts.endblock >= 0 && opts.endblock < opts.startblock)
-    {
-        pg_log_error("end block precedes start block");
-        exit(1);
-    }
+        pg_fatal("end block precedes start block");

     /*
      * A single non-option arguments specifies a database name or connection
@@ -477,7 +458,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -495,19 +476,13 @@ main(int argc, char *argv[])
     if (opts.alldb)
     {
         if (db != NULL)
-        {
-            pg_log_error("cannot specify a database name with --all");
-            exit(1);
-        }
+            pg_fatal("cannot specify a database name with --all");
         cparams.dbname = maintenance_db;
     }
     else if (db != NULL)
     {
         if (opts.dbpattern)
-        {
-            pg_log_error("cannot specify both a database name and database patterns");
-            exit(1);
-        }
+            pg_fatal("cannot specify both a database name and database patterns");
         cparams.dbname = db;
     }

@@ -535,7 +510,7 @@ main(int argc, char *argv[])
     {
         if (conn != NULL)
             disconnectDatabase(conn);
-        pg_log_error("no databases to check");
+        pg_log_warning("no databases to check");
         exit(0);
     }

@@ -593,7 +568,7 @@ main(int argc, char *argv[])
             /* Querying the catalog failed. */
             pg_log_error("database \"%s\": %s",
                          PQdb(conn), PQerrorMessage(conn));
-            pg_log_info("query was: %s", amcheck_sql);
+            pg_log_error_detail("Query was: %s", amcheck_sql);
             PQclear(result);
             disconnectDatabase(conn);
             exit(1);
@@ -669,8 +644,7 @@ main(int argc, char *argv[])
     {
         if (conn != NULL)
             disconnectDatabase(conn);
-        pg_log_error("no relations to check");
-        exit(1);
+        pg_fatal("no relations to check");
     }
     progress_report(reltotal, relprogress, pagestotal, pageschecked,
                     NULL, true, false);
@@ -919,7 +893,7 @@ run_command(ParallelSlot *slot, const char *sql)
         pg_log_error("error sending command to database \"%s\": %s",
                      PQdb(slot->connection),
                      PQerrorMessage(slot->connection));
-        pg_log_error("command was: %s", sql);
+        pg_log_error_detail("Command was: %s", sql);
         exit(1);
     }
 }
@@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
             pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
                            rel->datinfo->datname, rel->nspname, rel->relname, ntups);
             if (opts.verbose)
-                pg_log_info("query was: %s", rel->sql);
-            pg_log_warning("Are %s's and amcheck's versions compatible?",
-                           progname);
+                pg_log_warning_detail("Query was: %s", rel->sql);
+            pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
+                                progname);
             progress_since_last_stderr = false;
         }
     }
@@ -1648,7 +1622,7 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -1673,11 +1647,8 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
              */
             fatal = opts.strict_names;
             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected database pattern_id %d",
-                             pattern_id);
-                exit(1);
-            }
+                pg_fatal("internal error: received unexpected database pattern_id %d",
+                         pattern_id);
             log_no_match("no connectable databases to check matching \"%s\"",
                          opts.include.data[pattern_id].pattern);
         }
@@ -2096,7 +2067,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", sql.data);
+        pg_log_error_detail("Query was: %s", sql.data);
         disconnectDatabase(conn);
         exit(1);
     }
@@ -2136,11 +2107,8 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
              */

             if (pattern_id >= opts.include.len)
-            {
-                pg_log_error("internal error: received unexpected relation pattern_id %d",
-                             pattern_id);
-                exit(1);
-            }
+                pg_fatal("internal error: received unexpected relation pattern_id %d",
+                         pattern_id);

             opts.include.data[pattern_id].matched = true;
         }
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 6c3e7f4e01..064cbb222f 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -148,33 +148,21 @@ CleanupPriorWALFiles(void)

                 rc = unlink(WALFilePath);
                 if (rc != 0)
-                {
-                    pg_log_error("could not remove file \"%s\": %m",
-                                 WALFilePath);
-                    exit(1);
-                }
+                    pg_fatal("could not remove file \"%s\": %m",
+                             WALFilePath);
             }
         }

         if (errno)
-        {
-            pg_log_error("could not read archive location \"%s\": %m",
-                         archiveLocation);
-            exit(1);
-        }
+            pg_fatal("could not read archive location \"%s\": %m",
+                     archiveLocation);
         if (closedir(xldir))
-        {
-            pg_log_error("could not close archive location \"%s\": %m",
-                         archiveLocation);
-            exit(1);
-        }
-    }
-    else
-    {
-        pg_log_error("could not open archive location \"%s\": %m",
+            pg_fatal("could not close archive location \"%s\": %m",
                      archiveLocation);
-        exit(1);
     }
+    else
+        pg_fatal("could not open archive location \"%s\": %m",
+                 archiveLocation);
 }

 /*
@@ -247,7 +235,7 @@ SetWALFileNameForCleanup(void)
     if (!fnameOK)
     {
         pg_log_error("invalid file name argument");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }
 }
@@ -321,9 +309,9 @@ main(int argc, char **argv)
                                                      * from xlogfile names */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(2);
-                break;
         }
     }

@@ -342,7 +330,7 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify archive location");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

@@ -354,14 +342,14 @@ main(int argc, char **argv)
     else
     {
         pg_log_error("must specify oldest kept WAL file");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

     if (optind < argc)
     {
         pg_log_error("too many command-line arguments");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(2);
     }

diff --git a/src/bin/pg_basebackup/bbstreamer_file.c b/src/bin/pg_basebackup/bbstreamer_file.c
index d721f87891..393e9f340c 100644
--- a/src/bin/pg_basebackup/bbstreamer_file.c
+++ b/src/bin/pg_basebackup/bbstreamer_file.c
@@ -90,10 +90,7 @@ bbstreamer_plain_writer_new(char *pathname, FILE *file)
     {
         streamer->file = fopen(pathname, "wb");
         if (streamer->file == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", pathname);
-            exit(1);
-        }
+            pg_fatal("could not create file \"%s\": %m", pathname);
         streamer->should_close_file = true;
     }

@@ -121,9 +118,8 @@ bbstreamer_plain_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
+        pg_fatal("could not write to file \"%s\": %m",
+                 mystreamer->pathname);
     }
 }

@@ -139,11 +135,8 @@ bbstreamer_plain_writer_finalize(bbstreamer *streamer)
     mystreamer = (bbstreamer_plain_writer *) streamer;

     if (mystreamer->should_close_file && fclose(mystreamer->file) != 0)
-    {
-        pg_log_error("could not close file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
-    }
+        pg_fatal("could not close file \"%s\": %m",
+                 mystreamer->pathname);

     mystreamer->file = NULL;
     mystreamer->should_close_file = false;
@@ -262,9 +255,8 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
                 /* if write didn't set errno, assume problem is no disk space */
                 if (errno == 0)
                     errno = ENOSPC;
-                pg_log_error("could not write to file \"%s\": %m",
-                             mystreamer->filename);
-                exit(1);
+                pg_fatal("could not write to file \"%s\": %m",
+                         mystreamer->filename);
             }
             break;

@@ -280,8 +272,7 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while extracting archive");
-            exit(1);
+            pg_fatal("unexpected state while extracting archive");
     }
 }

@@ -304,20 +295,14 @@ extract_directory(const char *filename, mode_t mode)
                pg_str_endswith(filename, "/pg_xlog") ||
                pg_str_endswith(filename, "/archive_status")) &&
               errno == EEXIST))
-        {
-            pg_log_error("could not create directory \"%s\": %m",
-                         filename);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m",
+                     filename);
     }

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on directory \"%s\": %m",
-                     filename);
-        exit(1);
-    }
+        pg_fatal("could not set permissions on directory \"%s\": %m",
+                 filename);
 #endif
 }

@@ -335,11 +320,8 @@ static void
 extract_link(const char *filename, const char *linktarget)
 {
     if (symlink(linktarget, filename) != 0)
-    {
-        pg_log_error("could not create symbolic link from \"%s\" to \"%s\": %m",
-                     filename, linktarget);
-        exit(1);
-    }
+        pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
+                 filename, linktarget);
 }

 /*
@@ -354,18 +336,12 @@ create_file_for_extract(const char *filename, mode_t mode)

     file = fopen(filename, "wb");
     if (file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not create file \"%s\": %m", filename);

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on file \"%s\": %m",
-                     filename);
-        exit(1);
-    }
+        pg_fatal("could not set permissions on file \"%s\": %m",
+                 filename);
 #endif

     return file;
diff --git a/src/bin/pg_basebackup/bbstreamer_gzip.c b/src/bin/pg_basebackup/bbstreamer_gzip.c
index 1979e95639..316c5d849d 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -92,42 +92,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file,
     {
         streamer->gzfile = gzopen(pathname, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not create compressed file \"%s\": %m",
-                         pathname);
-            exit(1);
-        }
+            pg_fatal("could not create compressed file \"%s\": %m",
+                     pathname);
     }
     else
     {
         int            fd = dup(fileno(file));

         if (fd < 0)
-        {
-            pg_log_error("could not duplicate stdout: %m");
-            exit(1);
-        }
+            pg_fatal("could not duplicate stdout: %m");

         streamer->gzfile = gzdopen(fd, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not open output file: %m");
-            exit(1);
-        }
+            pg_fatal("could not open output file: %m");
     }

     if (gzsetparams(streamer->gzfile, compress->level,
                     Z_DEFAULT_STRATEGY) != Z_OK)
-    {
-        pg_log_error("could not set compression level %d: %s",
-                     compress->level, get_gz_error(streamer->gzfile));
-        exit(1);
-    }
+        pg_fatal("could not set compression level %d: %s",
+                 compress->level, get_gz_error(streamer->gzfile));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

@@ -153,9 +140,8 @@ bbstreamer_gzip_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to compressed file \"%s\": %s",
-                     mystreamer->pathname, get_gz_error(mystreamer->gzfile));
-        exit(1);
+        pg_fatal("could not write to compressed file \"%s\": %s",
+                 mystreamer->pathname, get_gz_error(mystreamer->gzfile));
     }
 }

@@ -178,11 +164,8 @@ bbstreamer_gzip_writer_finalize(bbstreamer *streamer)

     errno = 0;                    /* in case gzclose() doesn't set it */
     if (gzclose(mystreamer->gzfile) != 0)
-    {
-        pg_log_error("could not close compressed file \"%s\": %m",
-                     mystreamer->pathname);
-        exit(1);
-    }
+        pg_fatal("could not close compressed file \"%s\": %m",
+                 mystreamer->pathname);

     mystreamer->gzfile = NULL;
 }
@@ -259,15 +242,11 @@ bbstreamer_gzip_decompressor_new(bbstreamer *next)
      * possible value for safety.
      */
     if (inflateInit2(zs, 15 + 16) != Z_OK)
-    {
-        pg_log_error("could not initialize compression library");
-        exit(1);
-    }
+        pg_fatal("could not initialize compression library");

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_inject.c b/src/bin/pg_basebackup/bbstreamer_inject.c
index 79c378d96e..cc804f1091 100644
--- a/src/bin/pg_basebackup/bbstreamer_inject.c
+++ b/src/bin/pg_basebackup/bbstreamer_inject.c
@@ -186,8 +186,7 @@ bbstreamer_recovery_injector_content(bbstreamer *streamer,

         default:
             /* Shouldn't happen. */
-            pg_log_error("unexpected state while injecting recovery settings");
-            exit(1);
+            pg_fatal("unexpected state while injecting recovery settings");
     }

     bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
diff --git a/src/bin/pg_basebackup/bbstreamer_lz4.c b/src/bin/pg_basebackup/bbstreamer_lz4.c
index a6ec317e2b..6773b9f06e 100644
--- a/src/bin/pg_basebackup/bbstreamer_lz4.c
+++ b/src/bin/pg_basebackup/bbstreamer_lz4.c
@@ -104,13 +104,12 @@ bbstreamer_lz4_compressor_new(bbstreamer *next, bc_specification *compress)

     ctxError = LZ4F_createCompressionContext(&streamer->cctx, LZ4F_VERSION);
     if (LZ4F_isError(ctxError))
-            pg_log_error("could not create lz4 compression context: %s",
-                         LZ4F_getErrorName(ctxError));
+        pg_log_error("could not create lz4 compression context: %s",
+                     LZ4F_getErrorName(ctxError));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

@@ -296,16 +295,12 @@ bbstreamer_lz4_decompressor_new(bbstreamer *next)
     /* Initialize internal stream state for decompression */
     ctxError = LZ4F_createDecompressionContext(&streamer->dctx, LZ4F_VERSION);
     if (LZ4F_isError(ctxError))
-    {
-        pg_log_error("could not initialize compression library: %s",
-                LZ4F_getErrorName(ctxError));
-        exit(1);
-    }
+        pg_fatal("could not initialize compression library: %s",
+                 LZ4F_getErrorName(ctxError));

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_fatal("this build does not support compression");
 #endif
 }

diff --git a/src/bin/pg_basebackup/bbstreamer_tar.c b/src/bin/pg_basebackup/bbstreamer_tar.c
index 6ab981156e..fcbad579df 100644
--- a/src/bin/pg_basebackup/bbstreamer_tar.c
+++ b/src/bin/pg_basebackup/bbstreamer_tar.c
@@ -241,16 +241,12 @@ bbstreamer_tar_parser_content(bbstreamer *streamer, bbstreamer_member *member,
                  */
                 bbstreamer_buffer_bytes(streamer, &data, &len, len);
                 if (len > 2 * TAR_BLOCK_SIZE)
-                {
-                    pg_log_error("tar file trailer exceeds 2 blocks");
-                    exit(1);
-                }
+                    pg_fatal("tar file trailer exceeds 2 blocks");
                 return;

             default:
                 /* Shouldn't happen. */
-                pg_log_error("unexpected state while parsing tar archive");
-                exit(1);
+                pg_fatal("unexpected state while parsing tar archive");
         }
     }
 }
@@ -297,10 +293,7 @@ bbstreamer_tar_header(bbstreamer_tar_parser *mystreamer)
      */
     strlcpy(member->pathname, &buffer[0], MAXPGPATH);
     if (member->pathname[0] == '\0')
-    {
-        pg_log_error("tar member has empty name");
-        exit(1);
-    }
+        pg_fatal("tar member has empty name");
     member->size = read_tar_number(&buffer[124], 12);
     member->mode = read_tar_number(&buffer[100], 8);
     member->uid = read_tar_number(&buffer[108], 8);
@@ -332,10 +325,7 @@ bbstreamer_tar_parser_finalize(bbstreamer *streamer)
     if (mystreamer->next_context != BBSTREAMER_ARCHIVE_TRAILER &&
         (mystreamer->next_context != BBSTREAMER_MEMBER_HEADER ||
          mystreamer->base.bbs_buffer.len > 0))
-    {
-        pg_log_error("COPY stream ended before last file was finished");
-        exit(1);
-    }
+        pg_fatal("COPY stream ended before last file was finished");

     /* Send the archive trailer, even if empty. */
     bbstreamer_content(streamer->bbs_next, NULL,
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index ed8d084d62..65dcfff0a0 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -322,20 +322,14 @@ tablespace_list_append(const char *arg)
     for (arg_ptr = arg; *arg_ptr; arg_ptr++)
     {
         if (dst_ptr - dst >= MAXPGPATH)
-        {
-            pg_log_error("directory name too long");
-            exit(1);
-        }
+            pg_fatal("directory name too long");

         if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
             ;                    /* skip backslash escaping = */
         else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
         {
             if (*cell->new_dir)
-            {
-                pg_log_error("multiple \"=\" signs in tablespace mapping");
-                exit(1);
-            }
+                pg_fatal("multiple \"=\" signs in tablespace mapping");
             else
                 dst = dst_ptr = cell->new_dir;
         }
@@ -344,10 +338,7 @@ tablespace_list_append(const char *arg)
     }

     if (!*cell->old_dir || !*cell->new_dir)
-    {
-        pg_log_error("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
-        exit(1);
-    }
+        pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);

     /*
      * This check isn't absolutely necessary.  But all tablespaces are created
@@ -356,18 +347,12 @@ tablespace_list_append(const char *arg)
      * consistent with the new_dir check.
      */
     if (!is_absolute_path(cell->old_dir))
-    {
-        pg_log_error("old directory is not an absolute path in tablespace mapping: %s",
-                     cell->old_dir);
-        exit(1);
-    }
+        pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
+                 cell->old_dir);

     if (!is_absolute_path(cell->new_dir))
-    {
-        pg_log_error("new directory is not an absolute path in tablespace mapping: %s",
-                     cell->new_dir);
-        exit(1);
-    }
+        pg_fatal("new directory is not an absolute path in tablespace mapping: %s",
+                 cell->new_dir);

     /*
      * Comparisons done with these values should involve similarly
@@ -483,17 +468,11 @@ reached_end_position(XLogRecPtr segendpos, uint32 timeline,
             MemSet(xlogend, 0, sizeof(xlogend));
             r = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
             if (r < 0)
-            {
-                pg_log_error("could not read from ready pipe: %m");
-                exit(1);
-            }
+                pg_fatal("could not read from ready pipe: %m");

             if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-            {
-                pg_log_error("could not parse write-ahead log location \"%s\"",
-                             xlogend);
-                exit(1);
-            }
+                pg_fatal("could not parse write-ahead log location \"%s\"",
+                         xlogend);
             xlogendptr = ((uint64) hi) << 32 | lo;
             has_xlogendptr = 1;

@@ -639,11 +618,8 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,

     /* Convert the starting position */
     if (sscanf(startpos, "%X/%X", &hi, &lo) != 2)
-    {
-        pg_log_error("could not parse write-ahead log location \"%s\"",
-                     startpos);
-        exit(1);
-    }
+        pg_fatal("could not parse write-ahead log location \"%s\"",
+                 startpos);
     param->startptr = ((uint64) hi) << 32 | lo;
     /* Round off to even segment position */
     param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
@@ -651,10 +627,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
 #ifndef WIN32
     /* Create our background pipe */
     if (pipe(bgpipe) < 0)
-    {
-        pg_log_error("could not create pipe for background process: %m");
-        exit(1);
-    }
+        pg_fatal("could not create pipe for background process: %m");
 #endif

     /* Get a second connection */
@@ -709,10 +682,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
                  "pg_xlog" : "pg_wal");

         if (pg_mkdir_p(statusdir, pg_dir_create_mode) != 0 && errno != EEXIST)
-        {
-            pg_log_error("could not create directory \"%s\": %m", statusdir);
-            exit(1);
-        }
+            pg_fatal("could not create directory \"%s\": %m", statusdir);
     }

     /*
@@ -735,10 +705,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
         exit(ret);
     }
     else if (bgchild < 0)
-    {
-        pg_log_error("could not create background process: %m");
-        exit(1);
-    }
+        pg_fatal("could not create background process: %m");

     /*
      * Else we are in the parent process and all is well.
@@ -747,10 +714,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
 #else                            /* WIN32 */
     bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
     if (bgchild == 0)
-    {
-        pg_log_error("could not create background thread: %m");
-        exit(1);
-    }
+        pg_fatal("could not create background thread: %m");
 #endif
 }

@@ -770,10 +734,7 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
              * Does not exist, so create
              */
             if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
-            {
-                pg_log_error("could not create directory \"%s\": %m", dirname);
-                exit(1);
-            }
+                pg_fatal("could not create directory \"%s\": %m", dirname);
             if (created)
                 *created = true;
             return;
@@ -792,15 +753,13 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
             /*
              * Exists, not empty
              */
-            pg_log_error("directory \"%s\" exists but is not empty", dirname);
-            exit(1);
+            pg_fatal("directory \"%s\" exists but is not empty", dirname);
         case -1:

             /*
              * Access problem
              */
-            pg_log_error("could not access directory \"%s\": %m", dirname);
-            exit(1);
+            pg_fatal("could not access directory \"%s\": %m", dirname);
     }
 }

@@ -929,23 +888,16 @@ parse_max_rate(char *src)
     errno = 0;
     result = strtod(src, &after_num);
     if (src == after_num)
-    {
-        pg_log_error("transfer rate \"%s\" is not a valid value", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" is not a valid value", src);
     if (errno != 0)
-    {
-        pg_log_error("invalid transfer rate \"%s\": %m", src);
-        exit(1);
-    }
+        pg_fatal("invalid transfer rate \"%s\": %m", src);

     if (result <= 0)
     {
         /*
          * Reject obviously wrong values here.
          */
-        pg_log_error("transfer rate must be greater than zero");
-        exit(1);
+        pg_fatal("transfer rate must be greater than zero");
     }

     /*
@@ -975,27 +927,18 @@ parse_max_rate(char *src)
         after_num++;

     if (*after_num != '\0')
-    {
-        pg_log_error("invalid --max-rate unit: \"%s\"", suffix);
-        exit(1);
-    }
+        pg_fatal("invalid --max-rate unit: \"%s\"", suffix);

     /* Valid integer? */
     if ((uint64) result != (uint64) ((uint32) result))
-    {
-        pg_log_error("transfer rate \"%s\" exceeds integer range", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" exceeds integer range", src);

     /*
      * The range is checked on the server side too, but avoid the server
      * connection if a nonsensical value was passed.
      */
     if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
-    {
-        pg_log_error("transfer rate \"%s\" is out of range", src);
-        exit(1);
-    }
+        pg_fatal("transfer rate \"%s\" is out of range", src);

     return (int32) result;
 }
@@ -1091,11 +1034,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
     /* Get the COPY data stream. */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_COPY_OUT)
-    {
-        pg_log_error("could not get COPY data stream: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not get COPY data stream: %s",
+                 PQerrorMessage(conn));
     PQclear(res);

     /* Loop over chunks until done. */
@@ -1111,17 +1051,11 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
             break;
         }
         else if (r == -2)
-        {
-            pg_log_error("could not read COPY data: %s",
-                         PQerrorMessage(conn));
-            exit(1);
-        }
+            pg_fatal("could not read COPY data: %s",
+                     PQerrorMessage(conn));

         if (bgchild_exited)
-        {
-            pg_log_error("background process terminated unexpectedly");
-            exit(1);
-        }
+            pg_fatal("background process terminated unexpectedly");

         (*callback) (r, copybuf, callback_data);

@@ -1209,13 +1143,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
     if (must_parse_archive && !is_tar && !is_compressed_tar)
     {
         pg_log_error("unable to parse archive: %s", archive_name);
-        pg_log_info("only tar archives can be parsed");
+        pg_log_error_detail("Only tar archives can be parsed.");
         if (format == 'p')
-            pg_log_info("plain format requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
         if (inject_manifest)
-            pg_log_info("using - as the output directory requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
         if (writerecoveryconf)
-            pg_log_info("the -R option requires pg_basebackup to parse the archive");
+            pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
         exit(1);
     }

@@ -1427,10 +1361,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 /* Sanity check. */
                 if (state->manifest_buffer != NULL ||
                     state->manifest_file !=NULL)
-                {
-                    pg_log_error("archives should precede manifest");
-                    exit(1);
-                }
+                    pg_fatal("archives should precede manifest");

                 /* Parse the rest of the CopyData message. */
                 archive_name = GetCopyDataString(r, copybuf, &cursor);
@@ -1445,11 +1376,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                 if (archive_name[0] == '\0' || archive_name[0] == '.' ||
                     strchr(archive_name, '/') != NULL ||
                     strchr(archive_name, '\\') != NULL)
-                {
-                    pg_log_error("invalid archive name: \"%s\"",
-                                 archive_name);
-                    exit(1);
-                }
+                    pg_fatal("invalid archive name: \"%s\"",
+                             archive_name);

                 /*
                  * An empty spclocation is treated as NULL. We expect this
@@ -1509,9 +1437,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                          */
                         if (errno == 0)
                             errno = ENOSPC;
-                        pg_log_error("could not write to file \"%s\": %m",
-                                     state->manifest_filename);
-                        exit(1);
+                        pg_fatal("could not write to file \"%s\": %m",
+                                 state->manifest_filename);
                     }
                 }
                 else if (state->streamer != NULL)
@@ -1521,10 +1448,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                                        r - 1, BBSTREAMER_UNKNOWN);
                 }
                 else
-                {
-                    pg_log_error("unexpected payload data");
-                    exit(1);
-                }
+                    pg_fatal("unexpected payload data");
                 break;
             }

@@ -1577,11 +1501,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                         state->manifest_file =
                             fopen(state->manifest_filename, "wb");
                         if (state->manifest_file == NULL)
-                        {
-                            pg_log_error("could not create file \"%s\": %m",
-                                         state->manifest_filename);
-                            exit(1);
-                        }
+                            pg_fatal("could not create file \"%s\": %m",
+                                     state->manifest_filename);
                     }
                 }
                 break;
@@ -1670,11 +1591,10 @@ static void
 ReportCopyDataParseError(size_t r, char *copybuf)
 {
     if (r == 0)
-        pg_log_error("empty COPY message");
+        pg_fatal("empty COPY message");
     else
-        pg_log_error("malformed COPY message of type %d, length %zu",
-                     copybuf[0], r);
-    exit(1);
+        pg_fatal("malformed COPY message of type %d, length %zu",
+                 copybuf[0], r);
 }

 /*
@@ -1720,10 +1640,7 @@ ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
         initPQExpBuffer(&buf);
         ReceiveBackupManifestInMemory(conn, &buf);
         if (PQExpBufferDataBroken(buf))
-        {
-            pg_log_error("out of memory");
-            exit(1);
-        }
+            pg_fatal("out of memory");

         /* Inject it into the output tarfile. */
         bbstreamer_inject_file(manifest_inject_streamer, "backup_manifest",
@@ -1793,10 +1710,7 @@ ReceiveBackupManifest(PGconn *conn)
              "%s/backup_manifest.tmp", basedir);
     state.file = fopen(state.filename, "wb");
     if (state.file == NULL)
-    {
-        pg_log_error("could not create file \"%s\": %m", state.filename);
-        exit(1);
-    }
+        pg_fatal("could not create file \"%s\": %m", state.filename);

     ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);

@@ -1817,8 +1731,7 @@ ReceiveBackupManifestChunk(size_t r, char *copybuf, void *callback_data)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to file \"%s\": %m", state->filename);
-        exit(1);
+        pg_fatal("could not write to file \"%s\": %m", state->filename);
     }
 }

@@ -1878,9 +1791,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
     {
         const char *serverver = PQparameterStatus(conn, "server_version");

-        pg_log_error("incompatible server version %s",
-                     serverver ? serverver : "'unknown'");
-        exit(1);
+        pg_fatal("incompatible server version %s",
+                 serverver ? serverver : "'unknown'");
     }
     if (serverMajor >= 1500)
         use_new_option_syntax = true;
@@ -1963,16 +1875,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
         char       *colon;

         if (serverMajor < 1500)
-        {
-            pg_log_error("backup targets are not supported by this server version");
-            exit(1);
-        }
+            pg_fatal("backup targets are not supported by this server version");

         if (writerecoveryconf)
-        {
-            pg_log_error("recovery configuration cannot be written when a backup target is used");
-            exit(1);
-        }
+            pg_fatal("recovery configuration cannot be written when a backup target is used");

         AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");

@@ -1999,10 +1905,7 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
     if (compressloc == COMPRESS_LOCATION_SERVER)
     {
         if (!use_new_option_syntax)
-        {
-            pg_log_error("server does not support server-side compression");
-            exit(1);
-        }
+            pg_fatal("server does not support server-side compression");
         AppendStringCommandOption(&buf, use_new_option_syntax,
                                   "COMPRESSION", compression_algorithm);
         if (compression_detail != NULL)
@@ -2029,28 +1932,19 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
         basebkp = psprintf("BASE_BACKUP %s", buf.data);

     if (PQsendQuery(conn, basebkp) == 0)
-    {
-        pg_log_error("could not send replication command \"%s\": %s",
-                     "BASE_BACKUP", PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not send replication command \"%s\": %s",
+                 "BASE_BACKUP", PQerrorMessage(conn));

     /*
      * Get the starting WAL location
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not initiate base backup: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not initiate base backup: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
-                     PQntuples(res), PQnfields(res), 1, 2);
-        exit(1);
-    }
+        pg_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d
rowsand %d fields", 
+                 PQntuples(res), PQnfields(res), 1, 2);

     strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));

@@ -2078,16 +1972,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not get backup header: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("could not get backup header: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) < 1)
-    {
-        pg_log_error("no data returned from server");
-        exit(1);
-    }
+        pg_fatal("no data returned from server");

     /*
      * Sum up the total size, for progress reporting
@@ -2122,11 +2010,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
     writing_to_stdout = format == 't' && basedir != NULL &&
         strcmp(basedir, "-") == 0;
     if (writing_to_stdout && PQntuples(res) > 1)
-    {
-        pg_log_error("can only write single tablespace to stdout, database has %d",
-                     PQntuples(res));
-        exit(1);
-    }
+        pg_fatal("can only write single tablespace to stdout, database has %d",
+                 PQntuples(res));

     /*
      * If we're streaming WAL, start the streaming session before we start
@@ -2221,16 +2106,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("backup failed: %s",
-                     PQerrorMessage(conn));
-        exit(1);
-    }
+        pg_fatal("backup failed: %s",
+                 PQerrorMessage(conn));
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("no write-ahead log end position returned from server");
-        exit(1);
-    }
+        pg_fatal("no write-ahead log end position returned from server");
     strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
     if (verbose && includewal != NO_WAL)
         pg_log_info("write-ahead log end point: %s", xlogend);
@@ -2277,28 +2156,16 @@ BaseBackup(char *compression_algorithm, char *compression_detail,

 #ifndef WIN32
         if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
-        {
-            pg_log_info("could not send command to background pipe: %m");
-            exit(1);
-        }
+            pg_fatal("could not send command to background pipe: %m");

         /* Just wait for the background process to exit */
         r = waitpid(bgchild, &status, 0);
         if (r == (pid_t) -1)
-        {
-            pg_log_error("could not wait for child process: %m");
-            exit(1);
-        }
+            pg_fatal("could not wait for child process: %m");
         if (r != bgchild)
-        {
-            pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
-            exit(1);
-        }
+            pg_fatal("child %d died, expected %d", (int) r, (int) bgchild);
         if (status != 0)
-        {
-            pg_log_error("%s", wait_result_to_str(status));
-            exit(1);
-        }
+            pg_fatal("%s", wait_result_to_str(status));
         /* Exited normally, we're happy! */
 #else                            /* WIN32 */

@@ -2308,11 +2175,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
          * it's there.
          */
         if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-        {
-            pg_log_error("could not parse write-ahead log location \"%s\"",
-                         xlogend);
-            exit(1);
-        }
+            pg_fatal("could not parse write-ahead log location \"%s\"",
+                     xlogend);
         xlogendptr = ((uint64) hi) << 32 | lo;
         InterlockedIncrement(&has_xlogendptr);

@@ -2321,21 +2185,16 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
             WAIT_OBJECT_0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not wait for child thread: %m");
-            exit(1);
+            pg_fatal("could not wait for child thread: %m");
         }
         if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not get child thread exit status: %m");
-            exit(1);
+            pg_fatal("could not get child thread exit status: %m");
         }
         if (status != 0)
-        {
-            pg_log_error("child thread exited with error %u",
-                         (unsigned int) status);
-            exit(1);
-        }
+            pg_fatal("child thread exited with error %u",
+                     (unsigned int) status);
         /* Exited normally, we're happy */
 #endif
     }
@@ -2402,11 +2261,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
         else
         {
             if (rename(tmp_filename, filename) != 0)
-            {
-                pg_log_error("could not rename file \"%s\" to \"%s\": %m",
-                             tmp_filename, filename);
-                exit(1);
-            }
+                pg_fatal("could not rename file \"%s\" to \"%s\": %m",
+                         tmp_filename, filename);
         }
     }

@@ -2500,11 +2356,8 @@ main(int argc, char **argv)
                 else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
                     format = 't';
                 else
-                {
-                    pg_log_error("invalid output format \"%s\", must be \"plain\" or \"tar\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
+                             optarg);
                 break;
             case 'r':
                 maxrate = parse_max_rate(optarg);
@@ -2547,11 +2400,8 @@ main(int argc, char **argv)
                     includewal = STREAM_WAL;
                 }
                 else
-                {
-                    pg_log_error("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
+                             optarg);
                 break;
             case 1:
                 xlog_dir = pg_strdup(optarg);
@@ -2580,11 +2430,8 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "spread") == 0)
                     fastcheckpoint = false;
                 else
-                {
-                    pg_log_error("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
+                             optarg);
                 break;
             case 'd':
                 connection_string = pg_strdup(optarg);
@@ -2633,12 +2480,8 @@ main(int argc, char **argv)
                 manifest_checksums = pg_strdup(optarg);
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -2650,8 +2493,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2673,8 +2515,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && format != '\0')
     {
         pg_log_error("cannot specify both format and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == '\0')
@@ -2686,15 +2527,13 @@ main(int argc, char **argv)
     if (basedir == NULL && backup_target == NULL)
     {
         pg_log_error("must specify output directory or backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (basedir != NULL && backup_target != NULL)
     {
         pg_log_error("cannot specify both output directory and backup target");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2723,20 +2562,14 @@ main(int argc, char **argv)
         char       *error_detail;

         if (!parse_bc_algorithm(compression_algorithm, &alg))
-        {
-            pg_log_error("unrecognized compression algorithm \"%s\"",
-                         compression_algorithm);
-            exit(1);
-        }
+            pg_fatal("unrecognized compression algorithm \"%s\"",
+                     compression_algorithm);

         parse_bc_specification(alg, compression_detail, &client_compress);
         error_detail = validate_bc_specification(&client_compress);
         if (error_detail != NULL)
-        {
-            pg_log_error("invalid compression specification: %s",
-                         error_detail);
-            exit(1);
-        }
+            pg_fatal("invalid compression specification: %s",
+                     error_detail);
     }
     else
     {
@@ -2752,8 +2585,7 @@ main(int argc, char **argv)
     if (backup_target != NULL && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("client-side compression is not possible when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2764,8 +2596,7 @@ main(int argc, char **argv)
         client_compress.algorithm != BACKUP_COMPRESSION_NONE)
     {
         pg_log_error("only tar mode backups can be compressed");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2775,23 +2606,20 @@ main(int argc, char **argv)
     if (backup_target != NULL && includewal == STREAM_WAL)
     {
         pg_log_error("WAL cannot be streamed when a backup target is specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)
     {
         pg_log_error("cannot stream write-ahead logs in tar mode to stdout");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (replication_slot && includewal != STREAM_WAL)
     {
         pg_log_error("replication slots can only be used with WAL streaming");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2803,8 +2631,7 @@ main(int argc, char **argv)
         if (replication_slot)
         {
             pg_log_error("--no-slot cannot be used with slot name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         temp_replication_slot = false;
@@ -2816,8 +2643,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s needs a slot to be specified using --slot",
                          "--create-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2825,8 +2651,7 @@ main(int argc, char **argv)
         {
             pg_log_error("%s and %s are incompatible options",
                          "--create-slot", "--no-slot");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2839,15 +2664,13 @@ main(int argc, char **argv)
         if (backup_target != NULL)
         {
             pg_log_error("WAL directory location cannot be specified along with a backup target");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         if (format != 'p')
         {
             pg_log_error("WAL directory location can only be specified in plain mode");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }

@@ -2856,8 +2679,7 @@ main(int argc, char **argv)
         if (!is_absolute_path(xlog_dir))
         {
             pg_log_error("WAL directory location must be an absolute path");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -2869,8 +2691,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--progress", "--no-estimate-size");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2881,8 +2702,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-checksums");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2890,8 +2710,7 @@ main(int argc, char **argv)
     {
         pg_log_error("%s and %s are incompatible options",
                      "--no-manifest", "--manifest-force-encode");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2959,13 +2778,9 @@ main(int argc, char **argv)

 #ifdef HAVE_SYMLINK
         if (symlink(xlog_dir, linkloc) != 0)
-        {
-            pg_log_error("could not create symbolic link \"%s\": %m", linkloc);
-            exit(1);
-        }
+            pg_fatal("could not create symbolic link \"%s\": %m", linkloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_fatal("symlinks are not supported on this platform");
 #endif
         free(linkloc);
     }
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index 8d2c1e6ce0..23e04741fd 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -239,10 +239,7 @@ get_destination_dir(char *dest_folder)
     Assert(dest_folder != NULL);
     dir = opendir(dest_folder);
     if (dir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", dest_folder);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", dest_folder);

     return dir;
 }
@@ -256,10 +253,7 @@ close_destination_dir(DIR *dest_dir, char *dest_folder)
 {
     Assert(dest_dir != NULL && dest_folder != NULL);
     if (closedir(dest_dir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", dest_folder);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", dest_folder);
 }


@@ -322,10 +316,7 @@ FindStreamingStart(uint32 *tli)

             snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
             if (stat(fullpath, &statbuf) != 0)
-            {
-                pg_log_error("could not stat file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_fatal("could not stat file \"%s\": %m", fullpath);

             if (statbuf.st_size != WalSegSz)
             {
@@ -346,27 +337,20 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open compressed file \"%s\": %m",
-                             fullpath);
-                exit(1);
-            }
+                pg_fatal("could not open compressed file \"%s\": %m",
+                         fullpath);
             if (lseek(fd, (off_t) (-4), SEEK_END) < 0)
-            {
-                pg_log_error("could not seek in compressed file \"%s\": %m",
-                             fullpath);
-                exit(1);
-            }
+                pg_fatal("could not seek in compressed file \"%s\": %m",
+                         fullpath);
             r = read(fd, (char *) buf, sizeof(buf));
             if (r != sizeof(buf))
             {
                 if (r < 0)
-                    pg_log_error("could not read compressed file \"%s\": %m",
-                                 fullpath);
+                    pg_fatal("could not read compressed file \"%s\": %m",
+                             fullpath);
                 else
-                    pg_log_error("could not read compressed file \"%s\": read %d of %zu",
-                                 fullpath, r, sizeof(buf));
-                exit(1);
+                    pg_fatal("could not read compressed file \"%s\": read %d of %zu",
+                             fullpath, r, sizeof(buf));
             }

             close(fd);
@@ -399,18 +383,12 @@ FindStreamingStart(uint32 *tli)

             fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
             if (fd < 0)
-            {
-                pg_log_error("could not open file \"%s\": %m", fullpath);
-                exit(1);
-            }
+                pg_fatal("could not open file \"%s\": %m", fullpath);

             status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not create LZ4 decompression context: %s",
-                             LZ4F_getErrorName(status));
-                exit(1);
-            }
+                pg_fatal("could not create LZ4 decompression context: %s",
+                         LZ4F_getErrorName(status));

             outbuf = pg_malloc0(LZ4_CHUNK_SZ);
             readbuf = pg_malloc0(LZ4_CHUNK_SZ);
@@ -421,10 +399,7 @@ FindStreamingStart(uint32 *tli)

                 r = read(fd, readbuf, LZ4_CHUNK_SZ);
                 if (r < 0)
-                {
-                    pg_log_error("could not read file \"%s\": %m", fullpath);
-                    exit(1);
-                }
+                    pg_fatal("could not read file \"%s\": %m", fullpath);

                 /* Done reading the file */
                 if (r == 0)
@@ -442,12 +417,9 @@ FindStreamingStart(uint32 *tli)
                     status = LZ4F_decompress(ctx, outbuf, &out_size,
                                              readp, &read_size, &dec_opt);
                     if (LZ4F_isError(status))
-                    {
-                        pg_log_error("could not decompress file \"%s\": %s",
-                                     fullpath,
-                                     LZ4F_getErrorName(status));
-                        exit(1);
-                    }
+                        pg_fatal("could not decompress file \"%s\": %s",
+                                 fullpath,
+                                 LZ4F_getErrorName(status));

                     readp += read_size;
                     uncompressed_size += out_size;
@@ -468,11 +440,8 @@ FindStreamingStart(uint32 *tli)

             status = LZ4F_freeDecompressionContext(ctx);
             if (LZ4F_isError(status))
-            {
-                pg_log_error("could not free LZ4 decompression context: %s",
-                             LZ4F_getErrorName(status));
-                exit(1);
-            }
+                pg_fatal("could not free LZ4 decompression context: %s",
+                         LZ4F_getErrorName(status));

             if (uncompressed_size != WalSegSz)
             {
@@ -483,8 +452,8 @@ FindStreamingStart(uint32 *tli)
 #else
             pg_log_error("could not check file \"%s\"",
                          dirent->d_name);
-            pg_log_error("this build does not support compression with %s",
-                         "LZ4");
+            pg_log_error_detail("This build does not support compression with %s.",
+                                "LZ4");
             exit(1);
 #endif
         }
@@ -501,10 +470,7 @@ FindStreamingStart(uint32 *tli)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", basedir);

     close_destination_dir(dir, basedir);

@@ -752,10 +718,7 @@ main(int argc, char **argv)
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'n':
@@ -793,19 +756,12 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "none") == 0)
                     compression_method = COMPRESSION_NONE;
                 else
-                {
-                    pg_log_error("invalid value \"%s\" for option %s",
-                                 optarg, "--compression-method");
-                    exit(1);
-                }
+                    pg_fatal("invalid value \"%s\" for option %s",
+                             optarg, "--compression-method");
                 break;
             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -817,16 +773,14 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && do_create_slot)
     {
         pg_log_error("cannot use --create-slot together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -835,16 +789,14 @@ main(int argc, char **argv)
         /* translator: second %s is an option name */
         pg_log_error("%s needs a slot to be specified using --slot",
                      do_drop_slot ? "--drop-slot" : "--create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (synchronous && !do_sync)
     {
         pg_log_error("cannot use --synchronous together with --no-sync");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -854,8 +806,7 @@ main(int argc, char **argv)
     if (basedir == NULL && !do_drop_slot && !do_create_slot)
     {
         pg_log_error("no target directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,8 +821,7 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
@@ -883,9 +833,8 @@ main(int argc, char **argv)
                 compresslevel = Z_DEFAULT_COMPRESSION;
             }
 #else
-            pg_log_error("this build does not support compression with %s",
-                         "gzip");
-            exit(1);
+            pg_fatal("this build does not support compression with %s",
+                     "gzip");
 #endif
             break;
         case COMPRESSION_LZ4:
@@ -894,20 +843,17 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "lz4");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
 #else
-            pg_log_error("this build does not support compression with %s",
-                         "LZ4");
-            exit(1);
+            pg_fatal("this build does not support compression with %s",
+                     "LZ4");
 #endif
             break;
         case COMPRESSION_ZSTD:
-            pg_log_error("compression with %s is not yet supported", "ZSTD");
-            exit(1);
-
+            pg_fatal("compression with %s is not yet supported", "ZSTD");
+            break;
     }


@@ -951,11 +897,8 @@ main(int argc, char **argv)
      * be defined in this context.
      */
     if (db_name)
-    {
-        pg_log_error("replication connection using slot \"%s\" is unexpectedly database specific",
-                     replication_slot);
-        exit(1);
-    }
+        pg_fatal("replication connection using slot \"%s\" is unexpectedly database specific",
+                 replication_slot);

     /*
      * Set umask so that directories/files are created with the same
@@ -1013,10 +956,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index cc35d16f32..b59ff23f61 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -193,10 +193,7 @@ OutputFsync(TimestampTz now)
         return true;

     if (fsync(outfd) != 0)
-    {
-        pg_log_fatal("could not fsync file \"%s\": %m", outfile);
-        exit(1);
-    }
+        pg_fatal("could not fsync file \"%s\": %m", outfile);

     return true;
 }
@@ -780,18 +777,12 @@ main(int argc, char **argv)
 /* replication options */
             case 'I':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse start position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse start position \"%s\"", optarg);
                 startpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'E':
                 if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-                {
-                    pg_log_error("could not parse end position \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("could not parse end position \"%s\"", optarg);
                 endpos = ((uint64) hi) << 32 | lo;
                 break;
             case 'o':
@@ -842,12 +833,8 @@ main(int argc, char **argv)
                 break;

             default:
-
-                /*
-                 * getopt_long already emitted a complaint
-                 */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -859,8 +846,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -870,64 +856,56 @@ main(int argc, char **argv)
     if (replication_slot == NULL)
     {
         pg_log_error("no slot specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_start_slot && outfile == NULL)
     {
         pg_log_error("no target file specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && dbname == NULL)
     {
         pg_log_error("no database specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (!do_drop_slot && !do_create_slot && !do_start_slot)
     {
         pg_log_error("at least one action needs to be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (do_drop_slot && (do_create_slot || do_start_slot))
     {
         pg_log_error("cannot use --create-slot or --start together with --drop-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (startpos != InvalidXLogRecPtr && (do_create_slot || do_drop_slot))
     {
         pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (endpos != InvalidXLogRecPtr && !do_start_slot)
     {
         pg_log_error("--endpos may only be specified with --start");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (two_phase && !do_create_slot)
     {
         pg_log_error("--two-phase may only be specified with --create-slot");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -958,10 +936,7 @@ main(int argc, char **argv)
         exit(1);

     if (db_name == NULL)
-    {
-        pg_log_error("could not establish database-specific replication connection");
-        exit(1);
-    }
+        pg_fatal("could not establish database-specific replication connection");

     /*
      * Set umask so that directories/files are created with the same
@@ -1011,10 +986,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_fatal("disconnected");
         else
         {
             /* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c
index d39e4b11a1..42d50931d3 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
             /* fsync file in case of a previous crash */
             if (stream->walmethod->sync(f) != 0)
             {
-                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
+                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
                              fn, stream->walmethod->getlasterror());
                 stream->walmethod->close(f, CLOSE_UNLINK);
                 exit(1);
@@ -778,11 +778,8 @@ HandleCopyStream(PGconn *conn, StreamCtl *stream,
         if (stream->synchronous && lastFlushPosition < blockpos && walfile != NULL)
         {
             if (stream->walmethod->sync(walfile) != 0)
-            {
-                pg_log_fatal("could not fsync file \"%s\": %s",
-                             current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
+                pg_fatal("could not fsync file \"%s\": %s",
+                         current_walfile_name, stream->walmethod->getlasterror());
             lastFlushPosition = blockpos;

             /*
@@ -1030,11 +1027,8 @@ ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
              * shutdown of the server.
              */
             if (stream->walmethod->sync(walfile) != 0)
-            {
-                pg_log_fatal("could not fsync file \"%s\": %s",
-                             current_walfile_name, stream->walmethod->getlasterror());
-                exit(1);
-            }
+                pg_fatal("could not fsync file \"%s\": %s",
+                         current_walfile_name, stream->walmethod->getlasterror());
             lastFlushPosition = blockpos;
         }

diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4a6afd1a06..86c0493a94 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -88,10 +88,7 @@ GetConnection(void)
     {
         conn_opts = PQconninfoParse(connection_string, &err_msg);
         if (conn_opts == NULL)
-        {
-            pg_log_error("%s", err_msg);
-            exit(1);
-        }
+            pg_fatal("%s", err_msg);

         for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
         {
@@ -182,10 +179,7 @@ GetConnection(void)
          * and PQconnectdbParams returns NULL, we call exit(1) directly.
          */
         if (!tmpconn)
-        {
-            pg_log_error("could not connect to server");
-            exit(1);
-        }
+            pg_fatal("could not connect to server");

         /* If we need a password and -w wasn't given, loop back and get one */
         if (PQstatus(tmpconn) == CONNECTION_BAD &&
diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c
index 1e0ff760eb..acd242d2c9 100644
--- a/src/bin/pg_basebackup/walmethods.c
+++ b/src/bin/pg_basebackup/walmethods.c
@@ -1195,9 +1195,8 @@ tar_close(Walfile f, WalCloseMethod method)
     if (tar_sync(f) < 0)
     {
         /* XXX this seems pretty bogus; why is only this case fatal? */
-        pg_log_fatal("could not fsync file \"%s\": %s",
-                     tf->pathname, tar_getlasterror());
-        exit(1);
+        pg_fatal("could not fsync file \"%s\": %s",
+                 tf->pathname, tar_getlasterror());
     }

     /* Clean up and done */
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 5f0f5ee62d..21dfe1b6ee 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -197,10 +197,7 @@ scan_file(const char *fn, int segmentno)
     f = open(fn, PG_BINARY | flags, 0);

     if (f < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", fn);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", fn);

     files_scanned++;

@@ -214,12 +211,11 @@ scan_file(const char *fn, int segmentno)
         if (r != BLCKSZ)
         {
             if (r < 0)
-                pg_log_error("could not read block %u in file \"%s\": %m",
-                             blockno, fn);
+                pg_fatal("could not read block %u in file \"%s\": %m",
+                         blockno, fn);
             else
-                pg_log_error("could not read block %u in file \"%s\": read %d of %d",
-                             blockno, fn, r, BLCKSZ);
-            exit(1);
+                pg_fatal("could not read block %u in file \"%s\": read %d of %d",
+                         blockno, fn, r, BLCKSZ);
         }
         blocks_scanned++;

@@ -264,22 +260,18 @@ scan_file(const char *fn, int segmentno)

             /* Seek back to beginning of block */
             if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
-            {
-                pg_log_error("seek failed for block %u in file \"%s\": %m", blockno, fn);
-                exit(1);
-            }
+                pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);

             /* Write block with checksum */
             w = write(f, buf.data, BLCKSZ);
             if (w != BLCKSZ)
             {
                 if (w < 0)
-                    pg_log_error("could not write block %u in file \"%s\": %m",
-                                 blockno, fn);
+                    pg_fatal("could not write block %u in file \"%s\": %m",
+                             blockno, fn);
                 else
-                    pg_log_error("could not write block %u in file \"%s\": wrote %d of %d",
-                                 blockno, fn, w, BLCKSZ);
-                exit(1);
+                    pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
+                             blockno, fn, w, BLCKSZ);
             }
         }

@@ -323,10 +315,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
     dir = opendir(path);
     if (!dir)
-    {
-        pg_log_error("could not open directory \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", path);
     while ((de = readdir(dir)) != NULL)
     {
         char        fn[MAXPGPATH];
@@ -350,10 +339,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)

         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
         if (lstat(fn, &st) < 0)
-        {
-            pg_log_error("could not stat file \"%s\": %m", fn);
-            exit(1);
-        }
+            pg_fatal("could not stat file \"%s\": %m", fn);
         if (S_ISREG(st.st_mode))
         {
             char        fnonly[MAXPGPATH];
@@ -377,11 +363,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                 *segmentpath++ = '\0';
                 segmentno = atoi(segmentpath);
                 if (segmentno == 0)
-                {
-                    pg_log_error("invalid segment number %d in file name \"%s\"",
-                                 segmentno, fn);
-                    exit(1);
-                }
+                    pg_fatal("invalid segment number %d in file name \"%s\"",
+                             segmentno, fn);
             }

             forkpath = strchr(fnonly, '_');
@@ -429,11 +412,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);

                 if (lstat(tblspc_path, &tblspc_st) < 0)
-                {
-                    pg_log_error("could not stat file \"%s\": %m",
-                                 tblspc_path);
-                    exit(1);
-                }
+                    pg_fatal("could not stat file \"%s\": %m",
+                             tblspc_path);

                 /*
                  * Move backwards once as the scan needs to happen for the
@@ -528,7 +508,8 @@ main(int argc, char *argv[])
                 showprogress = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -544,7 +525,7 @@ main(int argc, char *argv[])
         if (DataDir == NULL)
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -554,8 +535,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -563,30 +543,23 @@ main(int argc, char *argv[])
     if (mode != PG_MODE_CHECK && only_filenode)
     {
         pg_log_error("option -f/--filenode can only be used with --check");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     /* Read the control file and check compatibility */
     ControlFile = get_controlfile(DataDir, &crc_ok);
     if (!crc_ok)
-    {
-        pg_log_error("pg_control CRC value is incorrect");
-        exit(1);
-    }
+        pg_fatal("pg_control CRC value is incorrect");

     if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
-    {
-        pg_log_error("cluster is not compatible with this version of pg_checksums");
-        exit(1);
-    }
+        pg_fatal("cluster is not compatible with this version of pg_checksums");

     if (ControlFile->blcksz != BLCKSZ)
     {
         pg_log_error("database cluster is not compatible");
-        fprintf(stderr, _("The database cluster was initialized with block size %u, but pg_checksums was compiled with
blocksize %u.\n"), 
-                ControlFile->blcksz, BLCKSZ);
+        pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled
withblock size %u.", 
+                            ControlFile->blcksz, BLCKSZ);
         exit(1);
     }

@@ -597,31 +570,19 @@ main(int argc, char *argv[])
      */
     if (ControlFile->state != DB_SHUTDOWNED &&
         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
-    {
-        pg_log_error("cluster must be shut down");
-        exit(1);
-    }
+        pg_fatal("cluster must be shut down");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_CHECK)
-    {
-        pg_log_error("data checksums are not enabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are not enabled in cluster");

     if (ControlFile->data_checksum_version == 0 &&
         mode == PG_MODE_DISABLE)
-    {
-        pg_log_error("data checksums are already disabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are already disabled in cluster");

     if (ControlFile->data_checksum_version > 0 &&
         mode == PG_MODE_ENABLE)
-    {
-        pg_log_error("data checksums are already enabled in cluster");
-        exit(1);
-    }
+        pg_fatal("data checksums are already enabled in cluster");

     /* Operate on all files if checking or enabling checksums */
     if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d94..c390ec51ce 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -134,7 +134,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -152,15 +153,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b9a25442f5..794e6e7ce9 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -340,9 +340,9 @@ flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,

             /* With partitions there can only be one parent */
             if (tblinfo[i].numParents != 1)
-                fatal("invalid number of parents %d for table \"%s\"",
-                      tblinfo[i].numParents,
-                      tblinfo[i].dobj.name);
+                pg_fatal("invalid number of parents %d for table \"%s\"",
+                         tblinfo[i].numParents,
+                         tblinfo[i].dobj.name);

             attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo));
             attachinfo->dobj.objType = DO_TABLE_ATTACH;
@@ -1001,13 +1001,10 @@ findParentsByOid(TableInfo *self,

                 parent = findTableByOid(inhinfo[i].inhparent);
                 if (parent == NULL)
-                {
-                    pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
-                                 inhinfo[i].inhparent,
-                                 self->dobj.name,
-                                 oid);
-                    exit_nicely(1);
-                }
+                    pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
+                             inhinfo[i].inhparent,
+                             self->dobj.name,
+                             oid);
                 self->parents[j++] = parent;
             }
         }
@@ -1043,10 +1040,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
             if (j > 0)
             {
                 if (argNum >= arraysize)
-                {
-                    pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
-                    exit_nicely(1);
-                }
+                    pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
                 temp[j] = '\0';
                 array[argNum++] = atooid(temp);
                 j = 0;
@@ -1058,10 +1052,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
         {
             if (!(isdigit((unsigned char) s) || s == '-') ||
                 j >= sizeof(temp) - 1)
-            {
-                pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
-                exit_nicely(1);
-            }
+                pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
             temp[j++] = s;
         }
     }
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 9077fdb74d..62f940ff7a 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -108,7 +108,7 @@ ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
         *alg = COMPR_ALG_NONE;
     else
     {
-        fatal("invalid compression code: %d", compression);
+        pg_fatal("invalid compression code: %d", compression);
         *alg = COMPR_ALG_NONE;    /* keep compiler quiet */
     }

@@ -131,7 +131,7 @@ AllocateCompressor(int compression, WriteFunc writeF)

 #ifndef HAVE_LIBZ
     if (alg == COMPR_ALG_LIBZ)
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif

     cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
@@ -167,7 +167,7 @@ ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
 #ifdef HAVE_LIBZ
         ReadDataFromArchiveZlib(AH, readF);
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif
     }
 }
@@ -185,7 +185,7 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
 #ifdef HAVE_LIBZ
             WriteDataToArchiveZlib(AH, cs, data, dLen);
 #else
-            fatal("not built with zlib support");
+            pg_fatal("not built with zlib support");
 #endif
             break;
         case COMPR_ALG_NONE:
@@ -233,8 +233,8 @@ InitCompressorZlib(CompressorState *cs, int level)
     cs->zlibOutSize = ZLIB_OUT_SIZE;

     if (deflateInit(zp, level) != Z_OK)
-        fatal("could not initialize compression library: %s",
-              zp->msg);
+        pg_fatal("could not initialize compression library: %s",
+                 zp->msg);

     /* Just be paranoid - maybe End is called after Start, with no Write */
     zp->next_out = (void *) cs->zlibOut;
@@ -253,7 +253,7 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
     DeflateCompressorZlib(AH, cs, true);

     if (deflateEnd(zp) != Z_OK)
-        fatal("could not close compression stream: %s", zp->msg);
+        pg_fatal("could not close compression stream: %s", zp->msg);

     free(cs->zlibOut);
     free(cs->zp);
@@ -270,7 +270,7 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
     {
         res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
         if (res == Z_STREAM_ERROR)
-            fatal("could not compress data: %s", zp->msg);
+            pg_fatal("could not compress data: %s", zp->msg);
         if ((flush && (zp->avail_out < cs->zlibOutSize))
             || (zp->avail_out == 0)
             || (zp->avail_in != 0)
@@ -330,8 +330,8 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
     out = pg_malloc(ZLIB_OUT_SIZE + 1);

     if (inflateInit(zp) != Z_OK)
-        fatal("could not initialize compression library: %s",
-              zp->msg);
+        pg_fatal("could not initialize compression library: %s",
+                 zp->msg);

     /* no minimal chunk size for zlib */
     while ((cnt = readF(AH, &buf, &buflen)))
@@ -346,7 +346,7 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)

             res = inflate(zp, 0);
             if (res != Z_OK && res != Z_STREAM_END)
-                fatal("could not uncompress data: %s", zp->msg);
+                pg_fatal("could not uncompress data: %s", zp->msg);

             out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
             ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
@@ -361,14 +361,14 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
         zp->avail_out = ZLIB_OUT_SIZE;
         res = inflate(zp, 0);
         if (res != Z_OK && res != Z_STREAM_END)
-            fatal("could not uncompress data: %s", zp->msg);
+            pg_fatal("could not uncompress data: %s", zp->msg);

         out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
         ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
     }

     if (inflateEnd(zp) != Z_OK)
-        fatal("could not close compression library: %s", zp->msg);
+        pg_fatal("could not close compression library: %s", zp->msg);

     free(buf);
     free(out);
@@ -501,7 +501,7 @@ cfopen_write(const char *path, const char *mode, int compression)
         fp = cfopen(fname, mode, compression);
         free_keep_errno(fname);
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
         fp = NULL;                /* keep compiler quiet */
 #endif
     }
@@ -544,7 +544,7 @@ cfopen(const char *path, const char *mode, int compression)
             fp = NULL;
         }
 #else
-        fatal("not built with zlib support");
+        pg_fatal("not built with zlib support");
 #endif
     }
     else
@@ -581,8 +581,8 @@ cfread(void *ptr, int size, cfp *fp)
             int            errnum;
             const char *errmsg = gzerror(fp->compressedfp, &errnum);

-            fatal("could not read from input file: %s",
-                  errnum == Z_ERRNO ? strerror(errno) : errmsg);
+            pg_fatal("could not read from input file: %s",
+                     errnum == Z_ERRNO ? strerror(errno) : errmsg);
         }
     }
     else
@@ -618,9 +618,9 @@ cfgetc(cfp *fp)
         if (ret == EOF)
         {
             if (!gzeof(fp->compressedfp))
-                fatal("could not read from input file: %s", strerror(errno));
+                pg_fatal("could not read from input file: %s", strerror(errno));
             else
-                fatal("could not read from input file: end of file");
+                pg_fatal("could not read from input file: end of file");
         }
     }
     else
diff --git a/src/bin/pg_dump/nls.mk b/src/bin/pg_dump/nls.mk
index 6276fd443b..220d1ec75f 100644
--- a/src/bin/pg_dump/nls.mk
+++ b/src/bin/pg_dump/nls.mk
@@ -11,8 +11,7 @@ GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) \
                    ../../common/exec.c ../../common/fe_memutils.c \
                    ../../common/wait_error.c
 GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) \
-                   fatal simple_prompt \
+                   simple_prompt \
                    ExecuteSqlCommand:3 warn_or_exit_horribly:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    fatal:1:c-format \
     warn_or_exit_horribly:2:c-format
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index bc5251be82..c9f6b86bb0 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -250,10 +250,7 @@ init_parallel_dump_utils(void)
         /* Initialize socket access */
         err = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (err != 0)
-        {
-            pg_log_error("%s() failed: error code %d", "WSAStartup", err);
-            exit_nicely(1);
-        }
+            pg_fatal("%s() failed: error code %d", "WSAStartup", err);

         parallel_init_done = true;
     }
@@ -393,7 +390,7 @@ archive_close_connection(int code, void *arg)
  *
  * Note that we don't expect to come here during normal exit (the workers
  * should be long gone, and the ParallelState too).  We're only here in a
- * fatal() situation, so intervening to cancel active commands is
+ * pg_fatal() situation, so intervening to cancel active commands is
  * appropriate.
  */
 static void
@@ -961,7 +958,7 @@ ParallelBackupStart(ArchiveHandle *AH)

         /* Create communication pipes for this worker */
         if (pgpipe(pipeMW) < 0 || pgpipe(pipeWM) < 0)
-            fatal("could not create communication channels: %m");
+            pg_fatal("could not create communication channels: %m");

         /* leader's ends of the pipes */
         slot->pipeRead = pipeWM[PIPE_READ];
@@ -1018,7 +1015,7 @@ ParallelBackupStart(ArchiveHandle *AH)
         else if (pid < 0)
         {
             /* fork failed */
-            fatal("could not create worker process: %m");
+            pg_fatal("could not create worker process: %m");
         }

         /* In Leader after successful fork */
@@ -1148,8 +1145,8 @@ parseWorkerCommand(ArchiveHandle *AH, TocEntry **te, T_Action *act,
         Assert(*te != NULL);
     }
     else
-        fatal("unrecognized command received from leader: \"%s\"",
-              msg);
+        pg_fatal("unrecognized command received from leader: \"%s\"",
+                 msg);
 }

 /*
@@ -1191,8 +1188,8 @@ parseWorkerResponse(ArchiveHandle *AH, TocEntry *te,
         AH->public.n_errors += n_errors;
     }
     else
-        fatal("invalid message received from worker: \"%s\"",
-              msg);
+        pg_fatal("invalid message received from worker: \"%s\"",
+                 msg);

     return status;
 }
@@ -1323,10 +1320,10 @@ lockTableForWorker(ArchiveHandle *AH, TocEntry *te)
     res = PQexec(AH->connection, query->data);

     if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-        fatal("could not obtain lock on relation \"%s\"\n"
-              "This usually means that someone requested an ACCESS EXCLUSIVE lock "
-              "on the table after the pg_dump parent process had gotten the "
-              "initial ACCESS SHARE lock on the table.", qualId);
+        pg_fatal("could not obtain lock on relation \"%s\"\n"
+                 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
+                 "on the table after the pg_dump parent process had gotten the "
+                 "initial ACCESS SHARE lock on the table.", qualId);

     PQclear(res);
     destroyPQExpBuffer(query);
@@ -1412,7 +1409,7 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
     {
         /* If do_wait is true, we must have detected EOF on some socket */
         if (do_wait)
-            fatal("a worker process died unexpectedly");
+            pg_fatal("a worker process died unexpectedly");
         return false;
     }

@@ -1429,8 +1426,8 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
         pstate->te[worker] = NULL;
     }
     else
-        fatal("invalid message received from worker: \"%s\"",
-              msg);
+        pg_fatal("invalid message received from worker: \"%s\"",
+                 msg);

     /* Free the string returned from getMessageFromWorker */
     free(msg);
@@ -1534,7 +1531,7 @@ sendMessageToLeader(int pipefd[2], const char *str)
     int            len = strlen(str) + 1;

     if (pipewrite(pipefd[PIPE_WRITE], str, len) != len)
-        fatal("could not write to the communication channel: %m");
+        pg_fatal("could not write to the communication channel: %m");
 }

 /*
@@ -1611,7 +1608,7 @@ getMessageFromWorker(ParallelState *pstate, bool do_wait, int *worker)
     }

     if (i < 0)
-        fatal("%s() failed: %m", "select");
+        pg_fatal("%s() failed: %m", "select");

     for (i = 0; i < pstate->numWorkers; i++)
     {
@@ -1652,7 +1649,7 @@ sendMessageToWorker(ParallelState *pstate, int worker, const char *str)

     if (pipewrite(pstate->parallelSlot[worker].pipeWrite, str, len) != len)
     {
-        fatal("could not write to the communication channel: %m");
+        pg_fatal("could not write to the communication channel: %m");
     }
 }

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..24e42fa5d7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -276,7 +276,7 @@ CloseArchive(Archive *AHX)
         res = fclose(AH->OF);

     if (res != 0)
-        fatal("could not close output file: %m");
+        pg_fatal("could not close output file: %m");
 }

 /* Public */
@@ -330,8 +330,8 @@ ProcessArchiveRestoreOptions(Archive *AHX)
                     /* ok no matter which section we were in */
                     break;
                 default:
-                    fatal("unexpected section code %d",
-                          (int) te->section);
+                    pg_fatal("unexpected section code %d",
+                             (int) te->section);
                     break;
             }
         }
@@ -367,11 +367,11 @@ RestoreArchive(Archive *AHX)
     {
         /* We haven't got round to making this work for all archive formats */
         if (AH->ClonePtr == NULL || AH->ReopenPtr == NULL)
-            fatal("parallel restore is not supported with this archive file format");
+            pg_fatal("parallel restore is not supported with this archive file format");

         /* Doesn't work if the archive represents dependencies as OIDs */
         if (AH->version < K_VERS_1_8)
-            fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");
+            pg_fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");

         /*
          * It's also not gonna work if we can't reopen the input file, so
@@ -389,7 +389,7 @@ RestoreArchive(Archive *AHX)
         for (te = AH->toc->next; te != AH->toc; te = te->next)
         {
             if (te->hadDumper && (te->reqs & REQ_DATA) != 0)
-                fatal("cannot restore from compressed archive (compression not supported in this installation)");
+                pg_fatal("cannot restore from compressed archive (compression not supported in this installation)");
         }
     }
 #endif
@@ -408,7 +408,7 @@ RestoreArchive(Archive *AHX)
     {
         pg_log_info("connecting to database for restore");
         if (AH->version < K_VERS_1_3)
-            fatal("direct database connections are not supported in pre-1.3 archives");
+            pg_fatal("direct database connections are not supported in pre-1.3 archives");

         /*
          * We don't want to guess at whether the dump will successfully
@@ -1037,7 +1037,7 @@ WriteData(Archive *AHX, const void *data, size_t dLen)
     ArchiveHandle *AH = (ArchiveHandle *) AHX;

     if (!AH->currToc)
-        fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");
+        pg_fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");

     AH->WriteDataPtr(AH, data, dLen);
 }
@@ -1220,7 +1220,7 @@ StartBlob(Archive *AHX, Oid oid)
     ArchiveHandle *AH = (ArchiveHandle *) AHX;

     if (!AH->StartBlobPtr)
-        fatal("large-object output not supported in chosen format");
+        pg_fatal("large-object output not supported in chosen format");

     AH->StartBlobPtr(AH, AH->currToc, oid);

@@ -1311,13 +1311,13 @@ StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
         {
             loOid = lo_create(AH->connection, oid);
             if (loOid == 0 || loOid != oid)
-                fatal("could not create large object %u: %s",
-                      oid, PQerrorMessage(AH->connection));
+                pg_fatal("could not create large object %u: %s",
+                         oid, PQerrorMessage(AH->connection));
         }
         AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
         if (AH->loFd == -1)
-            fatal("could not open large object %u: %s",
-                  oid, PQerrorMessage(AH->connection));
+            pg_fatal("could not open large object %u: %s",
+                     oid, PQerrorMessage(AH->connection));
     }
     else
     {
@@ -1372,7 +1372,7 @@ SortTocFromFile(Archive *AHX)
     /* Setup the file */
     fh = fopen(ropt->tocFile, PG_BINARY_R);
     if (!fh)
-        fatal("could not open TOC file \"%s\": %m", ropt->tocFile);
+        pg_fatal("could not open TOC file \"%s\": %m", ropt->tocFile);

     initStringInfo(&linebuf);

@@ -1407,8 +1407,8 @@ SortTocFromFile(Archive *AHX)
         /* Find TOC entry */
         te = getTocEntryByDumpId(AH, id);
         if (!te)
-            fatal("could not find entry for ID %d",
-                  id);
+            pg_fatal("could not find entry for ID %d",
+                     id);

         /* Mark it wanted */
         ropt->idWanted[id - 1] = true;
@@ -1430,7 +1430,7 @@ SortTocFromFile(Archive *AHX)
     pg_free(linebuf.data);

     if (fclose(fh) != 0)
-        fatal("could not close TOC file: %m");
+        pg_fatal("could not close TOC file: %m");
 }

 /**********************
@@ -1544,9 +1544,9 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
     if (!AH->OF)
     {
         if (filename)
-            fatal("could not open output file \"%s\": %m", filename);
+            pg_fatal("could not open output file \"%s\": %m", filename);
         else
-            fatal("could not open output file: %m");
+            pg_fatal("could not open output file: %m");
     }
 }

@@ -1573,7 +1573,7 @@ RestoreOutput(ArchiveHandle *AH, OutputContext savedContext)
         res = fclose(AH->OF);

     if (res != 0)
-        fatal("could not close output file: %m");
+        pg_fatal("could not close output file: %m");

     AH->gzOut = savedContext.gzOut;
     AH->OF = savedContext.OF;
@@ -1736,34 +1736,34 @@ warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt,...)

         case STAGE_INITIALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while INITIALIZING:");
+                pg_log_info("while INITIALIZING:");
             break;

         case STAGE_PROCESSING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while PROCESSING TOC:");
+                pg_log_info("while PROCESSING TOC:");
             break;

         case STAGE_FINALIZING:
             if (AH->stage != AH->lastErrorStage)
-                pg_log_generic(PG_LOG_INFO, "while FINALIZING:");
+                pg_log_info("while FINALIZING:");
             break;
     }
     if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
     {
-        pg_log_generic(PG_LOG_INFO, "from TOC entry %d; %u %u %s %s %s",
-                       AH->currentTE->dumpId,
-                       AH->currentTE->catalogId.tableoid,
-                       AH->currentTE->catalogId.oid,
-                       AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
-                       AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
-                       AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
+        pg_log_info("from TOC entry %d; %u %u %s %s %s",
+                    AH->currentTE->dumpId,
+                    AH->currentTE->catalogId.tableoid,
+                    AH->currentTE->catalogId.oid,
+                    AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
+                    AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
+                    AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
     }
     AH->lastErrorStage = AH->stage;
     AH->lastErrorTE = AH->currentTE;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, fmt, ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
     va_end(ap);

     if (AH->public.exit_on_error)
@@ -1827,7 +1827,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
     {
         /* this check is purely paranoia, maxDumpId should be correct */
         if (te->dumpId <= 0 || te->dumpId > maxDumpId)
-            fatal("bad dumpId");
+            pg_fatal("bad dumpId");

         /* tocsByDumpId indexes all TOCs by their dump ID */
         AH->tocsByDumpId[te->dumpId] = te;
@@ -1848,7 +1848,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
              * item's dump ID, so there should be a place for it in the array.
              */
             if (tableId <= 0 || tableId > maxDumpId)
-                fatal("bad table dumpId for TABLE DATA item");
+                pg_fatal("bad table dumpId for TABLE DATA item");

             AH->tableDataId[tableId] = te->dumpId;
         }
@@ -1940,7 +1940,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
             break;

         default:
-            fatal("unexpected data offset flag %d", offsetFlg);
+            pg_fatal("unexpected data offset flag %d", offsetFlg);
     }

     /*
@@ -1953,7 +1953,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
         else
         {
             if (AH->ReadBytePtr(AH) != 0)
-                fatal("file offset in dump file is too large");
+                pg_fatal("file offset in dump file is too large");
         }
     }

@@ -2091,8 +2091,8 @@ _discoverArchiveFormat(ArchiveHandle *AH)
             char        buf[MAXPGPATH];

             if (snprintf(buf, MAXPGPATH, "%s/toc.dat", AH->fSpec) >= MAXPGPATH)
-                fatal("directory name too long: \"%s\"",
-                      AH->fSpec);
+                pg_fatal("directory name too long: \"%s\"",
+                         AH->fSpec);
             if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
             {
                 AH->format = archDirectory;
@@ -2101,39 +2101,39 @@ _discoverArchiveFormat(ArchiveHandle *AH)

 #ifdef HAVE_LIBZ
             if (snprintf(buf, MAXPGPATH, "%s/toc.dat.gz", AH->fSpec) >= MAXPGPATH)
-                fatal("directory name too long: \"%s\"",
-                      AH->fSpec);
+                pg_fatal("directory name too long: \"%s\"",
+                         AH->fSpec);
             if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
             {
                 AH->format = archDirectory;
                 return AH->format;
             }
 #endif
-            fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
-                  AH->fSpec);
+            pg_fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
+                     AH->fSpec);
             fh = NULL;            /* keep compiler quiet */
         }
         else
         {
             fh = fopen(AH->fSpec, PG_BINARY_R);
             if (!fh)
-                fatal("could not open input file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
         }
     }
     else
     {
         fh = stdin;
         if (!fh)
-            fatal("could not open input file: %m");
+            pg_fatal("could not open input file: %m");
     }

     if ((cnt = fread(sig, 1, 5, fh)) != 5)
     {
         if (ferror(fh))
-            fatal("could not read input file: %m");
+            pg_fatal("could not read input file: %m");
         else
-            fatal("input file is too short (read %lu, expected 5)",
-                  (unsigned long) cnt);
+            pg_fatal("input file is too short (read %lu, expected 5)",
+                     (unsigned long) cnt);
     }

     /* Save it, just in case we need it later */
@@ -2164,19 +2164,19 @@ _discoverArchiveFormat(ArchiveHandle *AH)
              * looks like it's probably a text format dump. so suggest they
              * try psql
              */
-            fatal("input file appears to be a text format dump. Please use psql.");
+            pg_fatal("input file appears to be a text format dump. Please use psql.");
         }

         if (AH->lookaheadLen != 512)
         {
             if (feof(fh))
-                fatal("input file does not appear to be a valid archive (too short?)");
+                pg_fatal("input file does not appear to be a valid archive (too short?)");
             else
                 READ_ERROR_EXIT(fh);
         }

         if (!isValidTarHeader(AH->lookahead))
-            fatal("input file does not appear to be a valid archive");
+            pg_fatal("input file does not appear to be a valid archive");

         AH->format = archTar;
     }
@@ -2185,7 +2185,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
     if (wantClose)
     {
         if (fclose(fh) != 0)
-            fatal("could not close input file: %m");
+            pg_fatal("could not close input file: %m");
         /* Forget lookahead, since we'll re-read header after re-opening */
         AH->readHeader = 0;
         AH->lookaheadLen = 0;
@@ -2302,7 +2302,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
             break;

         default:
-            fatal("unrecognized file format \"%d\"", fmt);
+            pg_fatal("unrecognized file format \"%d\"", fmt);
     }

     return AH;
@@ -2388,8 +2388,8 @@ mark_dump_job_done(ArchiveHandle *AH,
                 te->dumpId, te->desc, te->tag);

     if (status != 0)
-        fatal("worker process failed: exit code %d",
-              status);
+        pg_fatal("worker process failed: exit code %d",
+                 status);
 }


@@ -2509,8 +2509,8 @@ ReadToc(ArchiveHandle *AH)

         /* Sanity check */
         if (te->dumpId <= 0)
-            fatal("entry ID %d out of range -- perhaps a corrupt TOC",
-                  te->dumpId);
+            pg_fatal("entry ID %d out of range -- perhaps a corrupt TOC",
+                     te->dumpId);

         te->hadDumper = ReadInt(AH);

@@ -2671,13 +2671,13 @@ processEncodingEntry(ArchiveHandle *AH, TocEntry *te)
         *ptr2 = '\0';
         encoding = pg_char_to_encoding(ptr1);
         if (encoding < 0)
-            fatal("unrecognized encoding \"%s\"",
-                  ptr1);
+            pg_fatal("unrecognized encoding \"%s\"",
+                     ptr1);
         AH->public.encoding = encoding;
     }
     else
-        fatal("invalid ENCODING item: %s",
-              te->defn);
+        pg_fatal("invalid ENCODING item: %s",
+                 te->defn);

     free(defn);
 }
@@ -2694,8 +2694,8 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
     else if (ptr1 && strncmp(ptr1, "'off'", 5) == 0)
         AH->public.std_strings = false;
     else
-        fatal("invalid STDSTRINGS item: %s",
-              te->defn);
+        pg_fatal("invalid STDSTRINGS item: %s",
+                 te->defn);
 }

 static void
@@ -2719,35 +2719,35 @@ StrictNamesCheck(RestoreOptions *ropt)
     {
         missing_name = simple_string_list_not_touched(&ropt->schemaNames);
         if (missing_name != NULL)
-            fatal("schema \"%s\" not found", missing_name);
+            pg_fatal("schema \"%s\" not found", missing_name);
     }

     if (ropt->tableNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->tableNames);
         if (missing_name != NULL)
-            fatal("table \"%s\" not found", missing_name);
+            pg_fatal("table \"%s\" not found", missing_name);
     }

     if (ropt->indexNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->indexNames);
         if (missing_name != NULL)
-            fatal("index \"%s\" not found", missing_name);
+            pg_fatal("index \"%s\" not found", missing_name);
     }

     if (ropt->functionNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->functionNames);
         if (missing_name != NULL)
-            fatal("function \"%s\" not found", missing_name);
+            pg_fatal("function \"%s\" not found", missing_name);
     }

     if (ropt->triggerNames.head != NULL)
     {
         missing_name = simple_string_list_not_touched(&ropt->triggerNames);
         if (missing_name != NULL)
-            fatal("trigger \"%s\" not found", missing_name);
+            pg_fatal("trigger \"%s\" not found", missing_name);
     }
 }

@@ -3140,8 +3140,8 @@ _doSetSessionAuth(ArchiveHandle *AH, const char *user)

         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
             /* NOT warn_or_exit_horribly... use -O instead to skip this. */
-            fatal("could not set session user to \"%s\": %s",
-                  user, PQerrorMessage(AH->connection));
+            pg_fatal("could not set session user to \"%s\": %s",
+                     user, PQerrorMessage(AH->connection));

         PQclear(res);
     }
@@ -3751,7 +3751,7 @@ ReadHead(ArchiveHandle *AH)
         AH->ReadBufPtr(AH, tmpMag, 5);

         if (strncmp(tmpMag, "PGDMP", 5) != 0)
-            fatal("did not find magic string in file header");
+            pg_fatal("did not find magic string in file header");
     }

     vmaj = AH->ReadBytePtr(AH);
@@ -3765,13 +3765,13 @@ ReadHead(ArchiveHandle *AH)
     AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);

     if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
-        fatal("unsupported version (%d.%d) in file header",
-              vmaj, vmin);
+        pg_fatal("unsupported version (%d.%d) in file header",
+                 vmaj, vmin);

     AH->intSize = AH->ReadBytePtr(AH);
     if (AH->intSize > 32)
-        fatal("sanity check on integer size (%lu) failed",
-              (unsigned long) AH->intSize);
+        pg_fatal("sanity check on integer size (%lu) failed",
+                 (unsigned long) AH->intSize);

     if (AH->intSize > sizeof(int))
         pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
@@ -3784,8 +3784,8 @@ ReadHead(ArchiveHandle *AH)
     fmt = AH->ReadBytePtr(AH);

     if (AH->format != fmt)
-        fatal("expected format (%d) differs from format found in file (%d)",
-              AH->format, fmt);
+        pg_fatal("expected format (%d) differs from format found in file (%d)",
+                 AH->format, fmt);

     if (AH->version >= K_VERS_1_2)
     {
@@ -4455,8 +4455,8 @@ mark_restore_job_done(ArchiveHandle *AH,
     else if (status == WORKER_IGNORED_ERRORS)
         AH->public.n_errors++;
     else if (status != 0)
-        fatal("worker process failed: exit code %d",
-              status);
+        pg_fatal("worker process failed: exit code %d",
+                 status);

     reduce_dependencies(AH, te, ready_list);
 }
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 540d4f6a83..084cd87e8d 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -121,14 +121,14 @@ struct ParallelState;
 #define READ_ERROR_EXIT(fd) \
     do { \
         if (feof(fd)) \
-            fatal("could not read from input file: end of file"); \
+            pg_fatal("could not read from input file: end of file"); \
         else \
-            fatal("could not read from input file: %m"); \
+            pg_fatal("could not read from input file: %m"); \
     } while (0)

 #define WRITE_ERROR_EXIT \
     do { \
-        fatal("could not write to output file: %m"); \
+        pg_fatal("could not write to output file: %m"); \
     } while (0)

 typedef enum T_Action
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..c3b9c365d5 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -153,13 +153,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
         {
             AH->FH = fopen(AH->fSpec, PG_BINARY_W);
             if (!AH->FH)
-                fatal("could not open output file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open output file \"%s\": %m", AH->fSpec);
         }
         else
         {
             AH->FH = stdout;
             if (!AH->FH)
-                fatal("could not open output file: %m");
+                pg_fatal("could not open output file: %m");
         }

         ctx->hasSeek = checkSeek(AH->FH);
@@ -170,13 +170,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
         {
             AH->FH = fopen(AH->fSpec, PG_BINARY_R);
             if (!AH->FH)
-                fatal("could not open input file \"%s\": %m", AH->fSpec);
+                pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
         }
         else
         {
             AH->FH = stdin;
             if (!AH->FH)
-                fatal("could not open input file: %m");
+                pg_fatal("could not open input file: %m");
         }

         ctx->hasSeek = checkSeek(AH->FH);
@@ -373,7 +373,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     lclContext *ctx = (lclContext *) AH->formatData;

     if (oid == 0)
-        fatal("invalid OID for large object");
+        pg_fatal("invalid OID for large object");

     WriteInt(AH, oid);

@@ -436,7 +436,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
         if (ctx->hasSeek)
         {
             if (fseeko(AH->FH, ctx->lastFilePos, SEEK_SET) != 0)
-                fatal("error during file seek: %m");
+                pg_fatal("error during file seek: %m");
         }

         for (;;)
@@ -492,8 +492,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
                     break;

                 default:        /* Always have a default */
-                    fatal("unrecognized data block type (%d) while searching archive",
-                          blkType);
+                    pg_fatal("unrecognized data block type (%d) while searching archive",
+                             blkType);
                     break;
             }
         }
@@ -502,7 +502,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     {
         /* We can just seek to the place we need to be. */
         if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
-            fatal("error during file seek: %m");
+            pg_fatal("error during file seek: %m");

         _readBlockHeader(AH, &blkType, &id);
     }
@@ -514,20 +514,20 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     if (blkType == EOF)
     {
         if (!ctx->hasSeek)
-            fatal("could not find block ID %d in archive -- "
-                  "possibly due to out-of-order restore request, "
-                  "which cannot be handled due to non-seekable input file",
-                  te->dumpId);
+            pg_fatal("could not find block ID %d in archive -- "
+                     "possibly due to out-of-order restore request, "
+                     "which cannot be handled due to non-seekable input file",
+                     te->dumpId);
         else
-            fatal("could not find block ID %d in archive -- "
-                  "possibly corrupt archive",
-                  te->dumpId);
+            pg_fatal("could not find block ID %d in archive -- "
+                     "possibly corrupt archive",
+                     te->dumpId);
     }

     /* Are we sane? */
     if (id != te->dumpId)
-        fatal("found unexpected block ID (%d) when reading data -- expected %d",
-              id, te->dumpId);
+        pg_fatal("found unexpected block ID (%d) when reading data -- expected %d",
+                 id, te->dumpId);

     switch (blkType)
     {
@@ -540,8 +540,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
             break;

         default:                /* Always have a default */
-            fatal("unrecognized data block type %d while restoring archive",
-                  blkType);
+            pg_fatal("unrecognized data block type %d while restoring archive",
+                     blkType);
             break;
     }

@@ -626,7 +626,7 @@ _skipData(ArchiveHandle *AH)
         if (ctx->hasSeek)
         {
             if (fseeko(AH->FH, blkLen, SEEK_CUR) != 0)
-                fatal("error during file seek: %m");
+                pg_fatal("error during file seek: %m");
         }
         else
         {
@@ -640,9 +640,9 @@ _skipData(ArchiveHandle *AH)
             if (fread(buf, 1, blkLen, AH->FH) != blkLen)
             {
                 if (feof(AH->FH))
-                    fatal("could not read from input file: end of file");
+                    pg_fatal("could not read from input file: end of file");
                 else
-                    fatal("could not read from input file: %m");
+                    pg_fatal("could not read from input file: %m");
             }
         }

@@ -743,7 +743,7 @@ _CloseArchive(ArchiveHandle *AH)
         /* Remember TOC's seek position for use below */
         tpos = ftello(AH->FH);
         if (tpos < 0 && ctx->hasSeek)
-            fatal("could not determine seek position in archive file: %m");
+            pg_fatal("could not determine seek position in archive file: %m");
         WriteToc(AH);
         WriteDataChunks(AH, NULL);

@@ -759,7 +759,7 @@ _CloseArchive(ArchiveHandle *AH)
     }

     if (fclose(AH->FH) != 0)
-        fatal("could not close archive file: %m");
+        pg_fatal("could not close archive file: %m");

     /* Sync the output file if one is defined */
     if (AH->dosync && AH->mode == archModeWrite && AH->fSpec)
@@ -782,32 +782,32 @@ _ReopenArchive(ArchiveHandle *AH)
     pgoff_t        tpos;

     if (AH->mode == archModeWrite)
-        fatal("can only reopen input archives");
+        pg_fatal("can only reopen input archives");

     /*
      * These two cases are user-facing errors since they represent unsupported
      * (but not invalid) use-cases.  Word the error messages appropriately.
      */
     if (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)
-        fatal("parallel restore from standard input is not supported");
+        pg_fatal("parallel restore from standard input is not supported");
     if (!ctx->hasSeek)
-        fatal("parallel restore from non-seekable file is not supported");
+        pg_fatal("parallel restore from non-seekable file is not supported");

     tpos = ftello(AH->FH);
     if (tpos < 0)
-        fatal("could not determine seek position in archive file: %m");
+        pg_fatal("could not determine seek position in archive file: %m");

 #ifndef WIN32
     if (fclose(AH->FH) != 0)
-        fatal("could not close archive file: %m");
+        pg_fatal("could not close archive file: %m");
 #endif

     AH->FH = fopen(AH->fSpec, PG_BINARY_R);
     if (!AH->FH)
-        fatal("could not open input file \"%s\": %m", AH->fSpec);
+        pg_fatal("could not open input file \"%s\": %m", AH->fSpec);

     if (fseeko(AH->FH, tpos, SEEK_SET) != 0)
-        fatal("could not set seek position in archive file: %m");
+        pg_fatal("could not set seek position in archive file: %m");
 }

 /*
@@ -862,7 +862,7 @@ _PrepParallelRestore(ArchiveHandle *AH)
         pgoff_t        endpos;

         if (fseeko(AH->FH, 0, SEEK_END) != 0)
-            fatal("error during file seek: %m");
+            pg_fatal("error during file seek: %m");
         endpos = ftello(AH->FH);
         if (endpos > prev_tctx->dataPos)
             prev_te->dataLength = endpos - prev_tctx->dataPos;
@@ -886,7 +886,7 @@ _Clone(ArchiveHandle *AH)

     /* sanity check, shouldn't happen */
     if (ctx->cs != NULL)
-        fatal("compressor active");
+        pg_fatal("compressor active");

     /*
      * We intentionally do not clone TOC-entry-local state: it's useful to
@@ -940,7 +940,7 @@ _getFilePos(ArchiveHandle *AH, lclContext *ctx)
     {
         /* Not expected if we found we can seek. */
         if (ctx->hasSeek)
-            fatal("could not determine seek position in archive file: %m");
+            pg_fatal("could not determine seek position in archive file: %m");
     }
     return pos;
 }
@@ -956,7 +956,7 @@ _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
     int            byt;

     /*
-     * Note: if we are at EOF with a pre-1.3 input file, we'll fatal() inside
+     * Note: if we are at EOF with a pre-1.3 input file, we'll pg_fatal() inside
      * ReadInt rather than returning EOF.  It doesn't seem worth jumping
      * through hoops to deal with that case better, because no such files are
      * likely to exist in the wild: only some 7.1 development versions of
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 3184eda3e7..89cdbf80e0 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -39,7 +39,7 @@ _check_database_version(ArchiveHandle *AH)
     remoteversion_str = PQparameterStatus(AH->connection, "server_version");
     remoteversion = PQserverVersion(AH->connection);
     if (remoteversion == 0 || !remoteversion_str)
-        fatal("could not get server_version from libpq");
+        pg_fatal("could not get server_version from libpq");

     AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
     AH->public.remoteVersion = remoteversion;
@@ -50,9 +50,10 @@ _check_database_version(ArchiveHandle *AH)
         && (remoteversion < AH->public.minRemoteVersion ||
             remoteversion > AH->public.maxRemoteVersion))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
-        fatal("aborting because of server version mismatch");
+        pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
+        exit(1);
     }

     /*
@@ -116,7 +117,7 @@ ConnectDatabase(Archive *AHX,
     bool        new_pass;

     if (AH->connection)
-        fatal("already connected to a database");
+        pg_fatal("already connected to a database");

     /* Never prompt for a password during a reconnection */
     prompt_password = isReconnect ? TRI_NO : cparams->promptPassword;
@@ -166,7 +167,7 @@ ConnectDatabase(Archive *AHX,
         AH->connection = PQconnectdbParams(keywords, values, true);

         if (!AH->connection)
-            fatal("could not connect to database");
+            pg_fatal("could not connect to database");

         if (PQstatus(AH->connection) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(AH->connection) &&
@@ -183,11 +184,11 @@ ConnectDatabase(Archive *AHX,
     if (PQstatus(AH->connection) == CONNECTION_BAD)
     {
         if (isReconnect)
-            fatal("reconnection failed: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("reconnection failed: %s",
+                     PQerrorMessage(AH->connection));
         else
-            fatal("%s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("%s",
+                     PQerrorMessage(AH->connection));
     }

     /* Start strict; later phases may override this. */
@@ -235,7 +236,7 @@ DisconnectDatabase(Archive *AHX)
         /*
          * If we have an active query, send a cancel before closing, ignoring
          * any errors.  This is of no use for a normal exit, but might be
-         * helpful during fatal().
+         * helpful during pg_fatal().
          */
         if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
             (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
@@ -261,16 +262,17 @@ GetConnection(Archive *AHX)
 static void
 notice_processor(void *arg, const char *message)
 {
-    pg_log_generic(PG_LOG_INFO, "%s", message);
+    pg_log_info("%s", message);
 }

-/* Like fatal(), but with a complaint about a particular query. */
+/* Like pg_fatal(), but with a complaint about a particular query. */
 static void
 die_on_query_failure(ArchiveHandle *AH, const char *query)
 {
     pg_log_error("query failed: %s",
                  PQerrorMessage(AH->connection));
-    fatal("query was: %s", query);
+    pg_log_error_detail("Query was: %s", query);
+    exit(1);
 }

 void
@@ -311,10 +313,10 @@ ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
     /* Expecting a single result only */
     ntups = PQntuples(res);
     if (ntups != 1)
-        fatal(ngettext("query returned %d row instead of one: %s",
-                       "query returned %d rows instead of one: %s",
-                       ntups),
-              ntups, query);
+        pg_fatal(ngettext("query returned %d row instead of one: %s",
+                          "query returned %d rows instead of one: %s",
+                          ntups),
+                 ntups, query);

     return res;
 }
@@ -456,8 +458,8 @@ ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
          */
         if (AH->pgCopyIn &&
             PQputCopyData(AH->connection, buf, bufLen) <= 0)
-            fatal("error returned by PQputCopyData: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("error returned by PQputCopyData: %s",
+                     PQerrorMessage(AH->connection));
     }
     else if (AH->outputKind == OUTPUT_OTHERDATA)
     {
@@ -505,8 +507,8 @@ EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
         PGresult   *res;

         if (PQputCopyEnd(AH->connection, NULL) <= 0)
-            fatal("error returned by PQputCopyEnd: %s",
-                  PQerrorMessage(AH->connection));
+            pg_fatal("error returned by PQputCopyEnd: %s",
+                     PQerrorMessage(AH->connection));

         /* Check command status and return to normal libpq state */
         res = PQgetResult(AH->connection);
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 7f4e340dea..3f46f7988a 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -153,7 +153,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
      */

     if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
-        fatal("no output directory specified");
+        pg_fatal("no output directory specified");

     ctx->directory = AH->fSpec;

@@ -182,18 +182,18 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
                 }

                 if (errno)
-                    fatal("could not read directory \"%s\": %m",
-                          ctx->directory);
+                    pg_fatal("could not read directory \"%s\": %m",
+                             ctx->directory);

                 if (closedir(dir))
-                    fatal("could not close directory \"%s\": %m",
-                          ctx->directory);
+                    pg_fatal("could not close directory \"%s\": %m",
+                             ctx->directory);
             }
         }

         if (!is_empty && mkdir(ctx->directory, 0700) < 0)
-            fatal("could not create directory \"%s\": %m",
-                  ctx->directory);
+            pg_fatal("could not create directory \"%s\": %m",
+                     ctx->directory);
     }
     else
     {                            /* Read Mode */
@@ -204,7 +204,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)

         tocFH = cfopen_read(fname, PG_BINARY_R);
         if (tocFH == NULL)
-            fatal("could not open input file \"%s\": %m", fname);
+            pg_fatal("could not open input file \"%s\": %m", fname);

         ctx->dataFH = tocFH;

@@ -219,7 +219,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)

         /* Nothing else in the file, so close it again... */
         if (cfclose(tocFH) != 0)
-            fatal("could not close TOC file: %m");
+            pg_fatal("could not close TOC file: %m");
         ctx->dataFH = NULL;
     }
 }
@@ -329,7 +329,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)

     ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
     if (ctx->dataFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -352,8 +352,8 @@ _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }
 }

@@ -370,7 +370,7 @@ _EndData(ArchiveHandle *AH, TocEntry *te)

     /* Close the file */
     if (cfclose(ctx->dataFH) != 0)
-        fatal("could not close data file: %m");
+        pg_fatal("could not close data file: %m");

     ctx->dataFH = NULL;
 }
@@ -392,7 +392,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
     cfp = cfopen_read(filename, PG_BINARY_R);

     if (!cfp)
-        fatal("could not open input file \"%s\": %m", filename);
+        pg_fatal("could not open input file \"%s\": %m", filename);

     buf = pg_malloc(ZLIB_OUT_SIZE);
     buflen = ZLIB_OUT_SIZE;
@@ -404,7 +404,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)

     free(buf);
     if (cfclose(cfp) != 0)
-        fatal("could not close data file \"%s\": %m", filename);
+        pg_fatal("could not close data file \"%s\": %m", filename);
 }

 /*
@@ -444,8 +444,8 @@ _LoadBlobs(ArchiveHandle *AH)
     ctx->blobsTocFH = cfopen_read(tocfname, PG_BINARY_R);

     if (ctx->blobsTocFH == NULL)
-        fatal("could not open large object TOC file \"%s\" for input: %m",
-              tocfname);
+        pg_fatal("could not open large object TOC file \"%s\" for input: %m",
+                 tocfname);

     /* Read the blobs TOC file line-by-line, and process each blob */
     while ((cfgets(ctx->blobsTocFH, line, MAXPGPATH)) != NULL)
@@ -455,8 +455,8 @@ _LoadBlobs(ArchiveHandle *AH)

         /* Can't overflow because line and blobfname are the same length */
         if (sscanf(line, "%u %" CppAsString2(MAXPGPATH) "s\n", &oid, blobfname) != 2)
-            fatal("invalid line in large object TOC file \"%s\": \"%s\"",
-                  tocfname, line);
+            pg_fatal("invalid line in large object TOC file \"%s\": \"%s\"",
+                     tocfname, line);

         StartRestoreBlob(AH, oid, AH->public.ropt->dropSchema);
         snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, blobfname);
@@ -464,12 +464,12 @@ _LoadBlobs(ArchiveHandle *AH)
         EndRestoreBlob(AH, oid);
     }
     if (!cfeof(ctx->blobsTocFH))
-        fatal("error reading large object TOC file \"%s\"",
-              tocfname);
+        pg_fatal("error reading large object TOC file \"%s\"",
+                 tocfname);

     if (cfclose(ctx->blobsTocFH) != 0)
-        fatal("could not close large object TOC file \"%s\": %m",
-              tocfname);
+        pg_fatal("could not close large object TOC file \"%s\": %m",
+                 tocfname);

     ctx->blobsTocFH = NULL;

@@ -494,8 +494,8 @@ _WriteByte(ArchiveHandle *AH, const int i)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }

     return 1;
@@ -530,8 +530,8 @@ _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        fatal("could not write to output file: %s",
-              get_cfp_error(ctx->dataFH));
+        pg_fatal("could not write to output file: %s",
+                 get_cfp_error(ctx->dataFH));
     }
 }

@@ -550,7 +550,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
      * exit on short reads.
      */
     if (cfread(buf, len, ctx->dataFH) != len)
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");
 }

 /*
@@ -583,7 +583,7 @@ _CloseArchive(ArchiveHandle *AH)
         /* The TOC is always created uncompressed */
         tocFH = cfopen_write(fname, PG_BINARY_W, 0);
         if (tocFH == NULL)
-            fatal("could not open output file \"%s\": %m", fname);
+            pg_fatal("could not open output file \"%s\": %m", fname);
         ctx->dataFH = tocFH;

         /*
@@ -596,7 +596,7 @@ _CloseArchive(ArchiveHandle *AH)
         AH->format = archDirectory;
         WriteToc(AH);
         if (cfclose(tocFH) != 0)
-            fatal("could not close TOC file: %m");
+            pg_fatal("could not close TOC file: %m");
         WriteDataChunks(AH, ctx->pstate);

         ParallelBackupEnd(AH, ctx->pstate);
@@ -646,7 +646,7 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
     /* The blob TOC file is never compressed */
     ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
     if (ctx->blobsTocFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -665,7 +665,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);

     if (ctx->dataFH == NULL)
-        fatal("could not open output file \"%s\": %m", fname);
+        pg_fatal("could not open output file \"%s\": %m", fname);
 }

 /*
@@ -682,13 +682,13 @@ _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)

     /* Close the BLOB data file itself */
     if (cfclose(ctx->dataFH) != 0)
-        fatal("could not close blob data file: %m");
+        pg_fatal("could not close blob data file: %m");
     ctx->dataFH = NULL;

     /* register the blob in blobs.toc */
     len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
     if (cfwrite(buf, len, ctx->blobsTocFH) != len)
-        fatal("could not write to blobs TOC file");
+        pg_fatal("could not write to blobs TOC file");
 }

 /*
@@ -702,7 +702,7 @@ _EndBlobs(ArchiveHandle *AH, TocEntry *te)
     lclContext *ctx = (lclContext *) AH->formatData;

     if (cfclose(ctx->blobsTocFH) != 0)
-        fatal("could not close blobs TOC file: %m");
+        pg_fatal("could not close blobs TOC file: %m");
     ctx->blobsTocFH = NULL;
 }

@@ -721,7 +721,7 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
     dname = ctx->directory;

     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
-        fatal("file name too long: \"%s\"", dname);
+        pg_fatal("file name too long: \"%s\"", dname);

     strcpy(buf, dname);
     strcat(buf, "/");
diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c
index 0458979f3c..541306d991 100644
--- a/src/bin/pg_dump/pg_backup_null.c
+++ b/src/bin/pg_dump/pg_backup_null.c
@@ -71,7 +71,7 @@ InitArchiveFmt_Null(ArchiveHandle *AH)
      * Now prevent reading...
      */
     if (AH->mode == archModeRead)
-        fatal("this format cannot be read");
+        pg_fatal("this format cannot be read");
 }

 /*
@@ -144,7 +144,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     bool        old_blob_style = (AH->version < K_VERS_1_12);

     if (oid == 0)
-        fatal("invalid OID for large object");
+        pg_fatal("invalid OID for large object");

     /* With an old archive we must do drop and create logic here */
     if (old_blob_style && AH->public.ropt->dropSchema)
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index ccfbe346be..d886f7e36f 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -174,14 +174,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
         {
             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file \"%s\" for output: %m",
-                      AH->fSpec);
+                pg_fatal("could not open TOC file \"%s\" for output: %m",
+                         AH->fSpec);
         }
         else
         {
             ctx->tarFH = stdout;
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file for output: %m");
+                pg_fatal("could not open TOC file for output: %m");
         }

         ctx->tarFHpos = 0;
@@ -200,7 +200,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
          * positioning.
          */
         if (AH->compression != 0)
-            fatal("compression is not supported by tar archive format");
+            pg_fatal("compression is not supported by tar archive format");
     }
     else
     {                            /* Read Mode */
@@ -208,14 +208,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
         {
             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file \"%s\" for input: %m",
-                      AH->fSpec);
+                pg_fatal("could not open TOC file \"%s\" for input: %m",
+                         AH->fSpec);
         }
         else
         {
             ctx->tarFH = stdin;
             if (ctx->tarFH == NULL)
-                fatal("could not open TOC file for input: %m");
+                pg_fatal("could not open TOC file for input: %m");
         }

         /*
@@ -335,7 +335,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
                  * Couldn't find the requested file. Future: do SEEK(0) and
                  * retry.
                  */
-                fatal("could not find file \"%s\" in archive", filename);
+                pg_fatal("could not find file \"%s\" in archive", filename);
             }
             else
             {
@@ -349,7 +349,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
         if (AH->compression == 0)
             tm->nFH = ctx->tarFH;
         else
-            fatal("compression is not supported by tar archive format");
+            pg_fatal("compression is not supported by tar archive format");
         /* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
 #else
         tm->nFH = ctx->tarFH;
@@ -401,7 +401,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 #endif

         if (tm->tmpFH == NULL)
-            fatal("could not generate temporary file name: %m");
+            pg_fatal("could not generate temporary file name: %m");

         umask(old_umask);

@@ -412,7 +412,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
             sprintf(fmode, "wb%d", AH->compression);
             tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
             if (tm->zFH == NULL)
-                fatal("could not open temporary file");
+                pg_fatal("could not open temporary file");
         }
         else
             tm->nFH = tm->tmpFH;
@@ -441,7 +441,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
     {
         errno = 0;                /* in case gzclose() doesn't set it */
         if (GZCLOSE(th->zFH) != 0)
-            fatal("could not close tar member: %m");
+            pg_fatal("could not close tar member: %m");
     }

     if (th->mode == 'w')
@@ -551,11 +551,11 @@ _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
                     int            errnum;
                     const char *errmsg = gzerror(th->zFH, &errnum);

-                    fatal("could not read from input file: %s",
-                          errnum == Z_ERRNO ? strerror(errno) : errmsg);
+                    pg_fatal("could not read from input file: %s",
+                             errnum == Z_ERRNO ? strerror(errno) : errmsg);
 #else
-                    fatal("could not read from input file: %s",
-                          strerror(errno));
+                    pg_fatal("could not read from input file: %s",
+                             strerror(errno));
 #endif
                 }
             }
@@ -685,8 +685,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
             pos1 = (int) strlen(te->copyStmt) - 13;
             if (pos1 < 6 || strncmp(te->copyStmt, "COPY ", 5) != 0 ||
                 strcmp(te->copyStmt + pos1, " FROM stdin;\n") != 0)
-                fatal("unexpected COPY statement syntax: \"%s\"",
-                      te->copyStmt);
+                pg_fatal("unexpected COPY statement syntax: \"%s\"",
+                         te->copyStmt);

             /* Emit all but the FROM part ... */
             ahwrite(te->copyStmt, 1, pos1, AH);
@@ -787,7 +787,7 @@ _ReadByte(ArchiveHandle *AH)
     res = tarRead(&c, 1, ctx->FH);
     if (res != 1)
         /* We already would have exited for errors on reads, must be EOF */
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");
     ctx->filePos += 1;
     return c;
 }
@@ -810,7 +810,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)

     if (tarRead(buf, len, ctx->FH) != len)
         /* We already would have exited for errors on reads, must be EOF */
-        fatal("could not read from input file: end of file");
+        pg_fatal("could not read from input file: end of file");

     ctx->filePos += len;
 }
@@ -952,7 +952,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
     char       *sfx;

     if (oid == 0)
-        fatal("invalid OID for large object (%u)", oid);
+        pg_fatal("invalid OID for large object (%u)", oid);

     if (AH->compression != 0)
         sfx = ".gz";
@@ -1080,12 +1080,12 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
      * Find file len & go back to start.
      */
     if (fseeko(tmp, 0, SEEK_END) != 0)
-        fatal("error during file seek: %m");
+        pg_fatal("error during file seek: %m");
     th->fileLen = ftello(tmp);
     if (th->fileLen < 0)
-        fatal("could not determine seek position in archive file: %m");
+        pg_fatal("could not determine seek position in archive file: %m");
     if (fseeko(tmp, 0, SEEK_SET) != 0)
-        fatal("error during file seek: %m");
+        pg_fatal("error during file seek: %m");

     _tarWriteHeader(th);

@@ -1099,11 +1099,11 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
         READ_ERROR_EXIT(tmp);

     if (fclose(tmp) != 0)        /* This *should* delete it... */
-        fatal("could not close temporary file: %m");
+        pg_fatal("could not close temporary file: %m");

     if (len != th->fileLen)
-        fatal("actual file length (%lld) does not match expected (%lld)",
-              (long long) len, (long long) th->fileLen);
+        pg_fatal("actual file length (%lld) does not match expected (%lld)",
+                 (long long) len, (long long) th->fileLen);

     pad = tarPaddingBytesRequired(len);
     for (i = 0; i < pad; i++)
@@ -1148,7 +1148,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
     if (!_tarGetHeader(AH, th))
     {
         if (filename)
-            fatal("could not find header for file \"%s\" in tar archive", filename);
+            pg_fatal("could not find header for file \"%s\" in tar archive", filename);
         else
         {
             /*
@@ -1166,9 +1166,9 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)

         id = atoi(th->targetFile);
         if ((TocIDRequired(AH, id) & REQ_DATA) != 0)
-            fatal("restoring data out of order is not supported in this archive format: "
-                  "\"%s\" is required, but comes before \"%s\" in the archive file.",
-                  th->targetFile, filename);
+            pg_fatal("restoring data out of order is not supported in this archive format: "
+                     "\"%s\" is required, but comes before \"%s\" in the archive file.",
+                     th->targetFile, filename);

         /* Header doesn't match, so read to next header */
         len = th->fileLen;
@@ -1179,7 +1179,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
             _tarReadRaw(AH, &header[0], TAR_BLOCK_SIZE, NULL, ctx->tarFH);

         if (!_tarGetHeader(AH, th))
-            fatal("could not find header for file \"%s\" in tar archive", filename);
+            pg_fatal("could not find header for file \"%s\" in tar archive", filename);
     }

     ctx->tarNextMember = ctx->tarFHpos + th->fileLen
@@ -1213,10 +1213,10 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
             return 0;

         if (len != TAR_BLOCK_SIZE)
-            fatal(ngettext("incomplete tar header found (%lu byte)",
-                           "incomplete tar header found (%lu bytes)",
-                           len),
-                  (unsigned long) len);
+            pg_fatal(ngettext("incomplete tar header found (%lu byte)",
+                              "incomplete tar header found (%lu bytes)",
+                              len),
+                     (unsigned long) len);

         /* Calc checksum */
         chk = tarChecksum(h);
@@ -1252,8 +1252,8 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
                  tag, (unsigned long long) hPos, (unsigned long long) len, sum);

     if (chk != sum)
-        fatal("corrupt tar header found in %s (expected %d, computed %d) file position %llu",
-              tag, sum, chk, (unsigned long long) ftello(ctx->tarFH));
+        pg_fatal("corrupt tar header found in %s (expected %d, computed %d) file position %llu",
+                 tag, sum, chk, (unsigned long long) ftello(ctx->tarFH));

     th->targetFile = pg_strdup(tag);
     th->fileLen = len;
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 57140a5504..e40890cb26 100644
--- a/src/bin/pg_dump/pg_backup_utils.c
+++ b/src/bin/pg_dump/pg_backup_utils.c
@@ -52,8 +52,7 @@ set_dump_section(const char *arg, int *dumpSections)
     else
     {
         pg_log_error("unrecognized section name: \"%s\"", arg);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }
 }
@@ -64,10 +63,7 @@ void
 on_exit_nicely(on_exit_nicely_callback function, void *arg)
 {
     if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
-    {
-        pg_log_fatal("out of on_exit_nicely slots");
-        exit_nicely(1);
-    }
+        pg_fatal("out of on_exit_nicely slots");
     on_exit_nicely_list[on_exit_nicely_index].function = function;
     on_exit_nicely_list[on_exit_nicely_index].arg = arg;
     on_exit_nicely_index++;
diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index 6ebc3afee4..5b1c51554d 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -31,6 +31,12 @@ extern void set_dump_section(const char *arg, int *dumpSections);
 extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);
 extern void exit_nicely(int code) pg_attribute_noreturn();

-#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)
+/* In pg_dump, we modify pg_fatal to call exit_nicely instead of exit */
+#undef pg_fatal
+#define pg_fatal(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit_nicely(1); \
+    } while(0)

 #endif                            /* PG_BACKUP_UTILS_H */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 535b160165..ecac4808d3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -620,7 +620,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -637,8 +638,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -655,32 +655,26 @@ main(int argc, char **argv)
         dopt.sequence_data = 1;

     if (dopt.dataOnly && dopt.schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
-        fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
+        pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");

     if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
-        fatal("option --include-foreign-data is not supported with parallel backup");
+        pg_fatal("option --include-foreign-data is not supported with parallel backup");

     if (dopt.dataOnly && dopt.outputClean)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -c/--clean and -a/--data-only cannot be used together");

     if (dopt.if_exists && !dopt.outputClean)
-        fatal("option --if-exists requires option -c/--clean");
+        pg_fatal("option --if-exists requires option -c/--clean");

     /*
      * --inserts are already implied above if --column-inserts or
      * --rows-per-insert were specified.
      */
     if (dopt.do_nothing && dopt.dump_inserts == 0)
-        fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
+        pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");

     /* Identify archive format to emit */
     archiveFormat = parseArchiveFormat(format, &archiveMode);
@@ -715,7 +709,7 @@ main(int argc, char **argv)

     /* Parallel backup only in the directory archive format so far */
     if (archiveFormat != archDirectory && numWorkers > 1)
-        fatal("parallel backup only supported by the directory format");
+        pg_fatal("parallel backup only supported by the directory format");

     /* Open the output file */
     fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
@@ -770,7 +764,7 @@ main(int argc, char **argv)
                                     &schema_include_oids,
                                     strict_names);
         if (schema_include_oids.head == NULL)
-            fatal("no matching schemas were found");
+            pg_fatal("no matching schemas were found");
     }
     expand_schema_name_patterns(fout, &schema_exclude_patterns,
                                 &schema_exclude_oids,
@@ -784,7 +778,7 @@ main(int argc, char **argv)
                                    &table_include_oids,
                                    strict_names);
         if (table_include_oids.head == NULL)
-            fatal("no matching tables were found");
+            pg_fatal("no matching tables were found");
     }
     expand_table_name_patterns(fout, &table_exclude_patterns,
                                &table_exclude_oids,
@@ -806,7 +800,7 @@ main(int argc, char **argv)
                                        &extension_include_oids,
                                        strict_names);
         if (extension_include_oids.head == NULL)
-            fatal("no matching extensions were found");
+            pg_fatal("no matching extensions were found");
     }

     /*
@@ -1087,8 +1081,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
     if (dumpencoding)
     {
         if (PQsetClientEncoding(conn, dumpencoding) < 0)
-            fatal("invalid client encoding \"%s\" specified",
-                  dumpencoding);
+            pg_fatal("invalid client encoding \"%s\" specified",
+                     dumpencoding);
     }

     /*
@@ -1225,7 +1219,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
     else if (AH->numWorkers > 1)
     {
         if (AH->isStandby && AH->remoteVersion < 100000)
-            fatal("parallel dumps from standby servers are not supported by this server version");
+            pg_fatal("parallel dumps from standby servers are not supported by this server version");
         AH->sync_snapshot_id = get_synchronized_snapshot(AH);
     }
 }
@@ -1290,7 +1284,7 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
     else if (pg_strcasecmp(format, "tar") == 0)
         archiveFormat = archTar;
     else
-        fatal("invalid output format \"%s\" specified", format);
+        pg_fatal("invalid output format \"%s\" specified", format);
     return archiveFormat;
 }

@@ -1328,7 +1322,7 @@ expand_schema_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching schemas were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -1375,7 +1369,7 @@ expand_extension_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching extensions were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -1422,7 +1416,7 @@ expand_foreign_server_name_patterns(Archive *fout,

         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
         if (PQntuples(res) == 0)
-            fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
             simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
@@ -1485,7 +1479,7 @@ expand_table_name_patterns(Archive *fout,
         PQclear(ExecuteSqlQueryForSingleRow(fout,
                                             ALWAYS_SECURE_SEARCH_PATH_SQL));
         if (strict_names && PQntuples(res) == 0)
-            fatal("no matching tables were found for pattern \"%s\"", cell->val);
+            pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);

         for (i = 0; i < PQntuples(res); i++)
         {
@@ -2033,8 +2027,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     {
         /* copy data transfer failed */
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("Command was: %s", q->data);
         exit_nicely(1);
     }

@@ -2043,8 +2037,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
-        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-        pg_log_error("The command was: %s", q->data);
+        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+        pg_log_error_detail("Command was: %s", q->data);
         exit_nicely(1);
     }
     PQclear(res);
@@ -2124,8 +2118,8 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
         /* cross-check field count, allowing for dummy NULL if any */
         if (nfields != PQnfields(res) &&
             !(nfields == 0 && PQnfields(res) == 1))
-            fatal("wrong number of fields retrieved from table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("wrong number of fields retrieved from table \"%s\"",
+                     tbinfo->dobj.name);

         /*
          * First time through, we build as much of the INSERT statement as
@@ -2877,8 +2871,8 @@ dumpDatabase(Archive *fout)
     else if (datlocprovider[0] == 'i')
         appendPQExpBufferStr(creaQry, "icu");
     else
-        fatal("unrecognized locale provider: %s",
-              datlocprovider);
+        pg_fatal("unrecognized locale provider: %s",
+                 datlocprovider);

     if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
     {
@@ -3257,7 +3251,7 @@ dumpSearchPath(Archive *AH)
                                       "SELECT pg_catalog.current_schemas(false)");

     if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
-        fatal("could not parse result of current_schemas()");
+        pg_fatal("could not parse result of current_schemas()");

     /*
      * We use set_config(), not a simple "SET search_path" command, because
@@ -3483,8 +3477,8 @@ dumpBlobs(Archive *fout, const void *arg)
             /* Open the BLOB */
             loFd = lo_open(conn, blobOid, INV_READ);
             if (loFd == -1)
-                fatal("could not open large object %u: %s",
-                      blobOid, PQerrorMessage(conn));
+                pg_fatal("could not open large object %u: %s",
+                         blobOid, PQerrorMessage(conn));

             StartBlob(fout, blobOid);

@@ -3493,8 +3487,8 @@ dumpBlobs(Archive *fout, const void *arg)
             {
                 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
                 if (cnt < 0)
-                    fatal("error reading large object %u: %s",
-                          blobOid, PQerrorMessage(conn));
+                    pg_fatal("error reading large object %u: %s",
+                             blobOid, PQerrorMessage(conn));

                 WriteData(fout, buf, cnt);
             } while (cnt > 0);
@@ -3740,11 +3734,8 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
     else if (polinfo->polcmd == 'd')
         cmd = " FOR DELETE";
     else
-    {
-        pg_log_error("unexpected policy command type: %c",
-                     polinfo->polcmd);
-        exit_nicely(1);
-    }
+        pg_fatal("unexpected policy command type: %c",
+                 polinfo->polcmd);

     query = createPQExpBuffer();
     delqry = createPQExpBuffer();
@@ -4223,7 +4214,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)

             if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
                               &attnames, &nattnames))
-                fatal("could not parse %s array", "prattrs");
+                pg_fatal("could not parse %s array", "prattrs");
             attribs = createPQExpBuffer();
             for (int k = 0; k < nattnames; k++)
             {
@@ -4556,7 +4547,7 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)

     /* Build list of quoted publications and append them to query. */
     if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
-        fatal("could not parse %s array", "subpublications");
+        pg_fatal("could not parse %s array", "subpublications");

     publications = createPQExpBuffer();
     for (i = 0; i < npubnames; i++)
@@ -4938,8 +4929,8 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
         extobj = NULL;
     }
     if (extobj == NULL)
-        fatal("could not find parent extension for %s %s",
-              objtype, objname);
+        pg_fatal("could not find parent extension for %s %s",
+                 objtype, objname);

     appendPQExpBufferStr(upgrade_buffer,
                          "\n-- For binary upgrade, handle extension membership the hard way\n");
@@ -5083,7 +5074,7 @@ findNamespace(Oid nsoid)

     nsinfo = findNamespaceByOid(nsoid);
     if (nsinfo == NULL)
-        fatal("schema with OID %u does not exist", nsoid);
+        pg_fatal("schema with OID %u does not exist", nsoid);
     return nsinfo;
 }

@@ -6537,8 +6528,8 @@ getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)

         owning_tab = findTableByOid(seqinfo->owning_tab);
         if (owning_tab == NULL)
-            fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-                  seqinfo->owning_tab, seqinfo->dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+                     seqinfo->owning_tab, seqinfo->dobj.catId.oid);

         /*
          * Only dump identity sequences if we're going to dump the table that
@@ -6841,12 +6832,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", indrelid);
+            pg_fatal("unrecognized table OID %u", indrelid);
         /* cross-check that we only got requested tables */
         if (!tbinfo->hasindex ||
             !tbinfo->interesting)
-            fatal("unexpected index data for table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("unexpected index data for table \"%s\"",
+                     tbinfo->dobj.name);

         /* Save data for this table */
         tbinfo->indexes = indxinfo + j;
@@ -7108,7 +7099,7 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
                     break;
             }
             if (curtblindx >= numTables)
-                fatal("unrecognized table OID %u", conrelid);
+                pg_fatal("unrecognized table OID %u", conrelid);
         }

         constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
@@ -7340,8 +7331,8 @@ getRules(Archive *fout, int *numRules)
         ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
         ruleinfo[i].ruletable = findTableByOid(ruletableoid);
         if (ruleinfo[i].ruletable == NULL)
-            fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
-                  ruletableoid, ruleinfo[i].dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
+                     ruletableoid, ruleinfo[i].dobj.catId.oid);
         ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
         ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
         ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
@@ -7579,7 +7570,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", tgrelid);
+            pg_fatal("unrecognized table OID %u", tgrelid);

         /* Save data for this table */
         tbinfo->triggers = tginfo + j;
@@ -7631,10 +7622,10 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
                     if (OidIsValid(tginfo[j].tgconstrrelid))
                     {
                         if (PQgetisnull(res, j, i_tgconstrrelname))
-                            fatal("query produced null referenced table name for foreign key trigger \"%s\" on table
\"%s\"(OID of table: %u)", 
-                                  tginfo[j].dobj.name,
-                                  tbinfo->dobj.name,
-                                  tginfo[j].tgconstrrelid);
+                            pg_fatal("query produced null referenced table name for foreign key trigger \"%s\" on
table\"%s\" (OID of table: %u)", 
+                                     tginfo[j].dobj.name,
+                                     tbinfo->dobj.name,
+                                     tginfo[j].tgconstrrelid);
                         tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
                     }
                     else
@@ -8246,12 +8237,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                 break;
         }
         if (curtblindx >= numTables)
-            fatal("unrecognized table OID %u", attrelid);
+            pg_fatal("unrecognized table OID %u", attrelid);
         /* cross-check that we only got requested tables */
         if (tbinfo->relkind == RELKIND_SEQUENCE ||
             !tbinfo->interesting)
-            fatal("unexpected column data for table \"%s\"",
-                  tbinfo->dobj.name);
+            pg_fatal("unexpected column data for table \"%s\"",
+                     tbinfo->dobj.name);

         /* Save data for this table */
         tbinfo->numatts = numatts;
@@ -8280,8 +8271,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
         for (int j = 0; j < numatts; j++, r++)
         {
             if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
-                fatal("invalid column numbering in table \"%s\"",
-                      tbinfo->dobj.name);
+                pg_fatal("invalid column numbering in table \"%s\"",
+                         tbinfo->dobj.name);
             tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
             tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
             tbinfo->atttypmod[j] = atoi(PQgetvalue(res, r, i_atttypmod));
@@ -8367,12 +8358,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                         break;
                 }
                 if (curtblindx >= numTables)
-                    fatal("unrecognized table OID %u", adrelid);
+                    pg_fatal("unrecognized table OID %u", adrelid);
             }

             if (adnum <= 0 || adnum > tbinfo->numatts)
-                fatal("invalid adnum value %d for table \"%s\"",
-                      adnum, tbinfo->dobj.name);
+                pg_fatal("invalid adnum value %d for table \"%s\"",
+                         adnum, tbinfo->dobj.name);

             /*
              * dropped columns shouldn't have defaults, but just in case,
@@ -8521,7 +8512,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                     break;
             }
             if (curtblindx >= numTables)
-                fatal("unrecognized table OID %u", conrelid);
+                pg_fatal("unrecognized table OID %u", conrelid);

             if (numcons != tbinfo->ncheck)
             {
@@ -8529,7 +8520,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                                       "expected %d check constraints on table \"%s\" but found %d",
                                       tbinfo->ncheck),
                              tbinfo->ncheck, tbinfo->dobj.name, numcons);
-                pg_log_error("(The system catalogs might be corrupted.)");
+                pg_log_error_hint("The system catalogs might be corrupted.");
                 exit_nicely(1);
             }

@@ -9219,7 +9210,7 @@ getRoleName(const char *roleoid_str)
         }
     }

-    fatal("role with OID %u does not exist", roleoid);
+    pg_fatal("role with OID %u does not exist", roleoid);
     return NULL;                /* keep compiler quiet */
 }

@@ -11687,7 +11678,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
     if (*proconfig)
     {
         if (!parsePGArray(proconfig, &configitems, &nconfigitems))
-            fatal("could not parse %s array", "proconfig");
+            pg_fatal("could not parse %s array", "proconfig");
     }
     else
     {
@@ -11756,8 +11747,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
         else if (provolatile[0] == PROVOLATILE_STABLE)
             appendPQExpBufferStr(q, " STABLE");
         else if (provolatile[0] != PROVOLATILE_VOLATILE)
-            fatal("unrecognized provolatile value for function \"%s\"",
-                  finfo->dobj.name);
+            pg_fatal("unrecognized provolatile value for function \"%s\"",
+                     finfo->dobj.name);
     }

     if (proisstrict[0] == 't')
@@ -11806,8 +11797,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
         else if (proparallel[0] == PROPARALLEL_RESTRICTED)
             appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
         else if (proparallel[0] != PROPARALLEL_UNSAFE)
-            fatal("unrecognized proparallel value for function \"%s\"",
-                  finfo->dobj.name);
+            pg_fatal("unrecognized proparallel value for function \"%s\"",
+                     finfo->dobj.name);
     }

     for (i = 0; i < nconfigitems; i++)
@@ -11937,8 +11928,8 @@ dumpCast(Archive *fout, const CastInfo *cast)
     {
         funcInfo = findFuncByOid(cast->castfunc);
         if (funcInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  cast->castfunc);
+            pg_fatal("could not find function definition for function with OID %u",
+                     cast->castfunc);
     }

     defqry = createPQExpBuffer();
@@ -12043,15 +12034,15 @@ dumpTransform(Archive *fout, const TransformInfo *transform)
     {
         fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
         if (fromsqlFuncInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  transform->trffromsql);
+            pg_fatal("could not find function definition for function with OID %u",
+                     transform->trffromsql);
     }
     if (OidIsValid(transform->trftosql))
     {
         tosqlFuncInfo = findFuncByOid(transform->trftosql);
         if (tosqlFuncInfo == NULL)
-            fatal("could not find function definition for function with OID %u",
-                  transform->trftosql);
+            pg_fatal("could not find function definition for function with OID %u",
+                     transform->trftosql);
     }

     defqry = createPQExpBuffer();
@@ -13109,8 +13100,8 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
         /* to allow dumping pg_catalog; not accepted on input */
         appendPQExpBufferStr(q, "default");
     else
-        fatal("unrecognized collation provider: %s",
-              collprovider);
+        pg_fatal("unrecognized collation provider: %s",
+                 collprovider);

     if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
         appendPQExpBufferStr(q, ", deterministic = false");
@@ -13516,8 +13507,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
                     appendPQExpBufferStr(details, ",\n    FINALFUNC_MODIFY = READ_WRITE");
                     break;
                 default:
-                    fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
-                          agginfo->aggfn.dobj.name);
+                    pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
+                             agginfo->aggfn.dobj.name);
                     break;
             }
         }
@@ -13572,8 +13563,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
                     appendPQExpBufferStr(details, ",\n    MFINALFUNC_MODIFY = READ_WRITE");
                     break;
                 default:
-                    fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
-                          agginfo->aggfn.dobj.name);
+                    pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
+                             agginfo->aggfn.dobj.name);
                     break;
             }
         }
@@ -13597,8 +13588,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
         else if (proparallel[0] == PROPARALLEL_RESTRICTED)
             appendPQExpBufferStr(details, ",\n    PARALLEL = restricted");
         else if (proparallel[0] != PROPARALLEL_UNSAFE)
-            fatal("unrecognized proparallel value for function \"%s\"",
-                  agginfo->aggfn.dobj.name);
+            pg_fatal("unrecognized proparallel value for function \"%s\"",
+                     agginfo->aggfn.dobj.name);
     }

     appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
@@ -14290,8 +14281,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
             break;
         default:
             /* shouldn't get here */
-            fatal("unrecognized object type in default privileges: %d",
-                  (int) daclinfo->defaclobjtype);
+            pg_fatal("unrecognized object type in default privileges: %d",
+                     (int) daclinfo->defaclobjtype);
             type = "";            /* keep compiler quiet */
     }

@@ -14306,8 +14297,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
                                  daclinfo->defaclrole,
                                  fout->remoteVersion,
                                  q))
-        fatal("could not parse default ACL list (%s)",
-              daclinfo->dacl.acl);
+        pg_fatal("could not parse default ACL list (%s)",
+                 daclinfo->dacl.acl);

     if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
         ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
@@ -14388,8 +14379,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
         if (!buildACLCommands(name, subname, nspname, type,
                               initprivs, acldefault, owner,
                               "", fout->remoteVersion, sql))
-            fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
-                  initprivs, acldefault, name, type);
+            pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
+                     initprivs, acldefault, name, type);
         appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
     }

@@ -14413,8 +14404,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
     if (!buildACLCommands(name, subname, nspname, type,
                           acls, baseacls, owner,
                           "", fout->remoteVersion, sql))
-        fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
-              acls, baseacls, name, type);
+        pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
+                 acls, baseacls, name, type);

     if (sql->len > 0)
     {
@@ -14951,18 +14942,18 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
     if (PQntuples(res) != 1)
     {
         if (PQntuples(res) < 1)
-            fatal("query to obtain definition of view \"%s\" returned no data",
-                  tbinfo->dobj.name);
+            pg_fatal("query to obtain definition of view \"%s\" returned no data",
+                     tbinfo->dobj.name);
         else
-            fatal("query to obtain definition of view \"%s\" returned more than one definition",
-                  tbinfo->dobj.name);
+            pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
+                     tbinfo->dobj.name);
     }

     len = PQgetlength(res, 0, 0);

     if (len == 0)
-        fatal("definition of view \"%s\" appears to be empty (length zero)",
-              tbinfo->dobj.name);
+        pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
+                 tbinfo->dobj.name);

     /* Strip off the trailing semicolon so that other things may follow. */
     Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
@@ -15974,8 +15965,8 @@ getAttrName(int attrnum, const TableInfo *tblInfo)
         case TableOidAttributeNumber:
             return "tableoid";
     }
-    fatal("invalid column number %d for table \"%s\"",
-          attrnum, tblInfo->dobj.name);
+    pg_fatal("invalid column number %d for table \"%s\"",
+             attrnum, tblInfo->dobj.name);
     return NULL;                /* keep compiler quiet */
 }

@@ -16052,11 +16043,11 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
             int            j;

             if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
-                fatal("could not parse index statistic columns");
+                pg_fatal("could not parse index statistic columns");
             if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
-                fatal("could not parse index statistic values");
+                pg_fatal("could not parse index statistic values");
             if (nstatcols != nstatvals)
-                fatal("mismatched number of columns and values for index statistics");
+                pg_fatal("mismatched number of columns and values for index statistics");

             for (j = 0; j < nstatcols; j++)
             {
@@ -16274,8 +16265,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
         indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);

         if (indxinfo == NULL)
-            fatal("missing index for constraint \"%s\"",
-                  coninfo->dobj.name);
+            pg_fatal("missing index for constraint \"%s\"",
+                     coninfo->dobj.name);

         if (dopt->binary_upgrade)
             binary_upgrade_set_pg_class_oids(fout, q,
@@ -16502,8 +16493,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
     }
     else
     {
-        fatal("unrecognized constraint type: %c",
-              coninfo->contype);
+        pg_fatal("unrecognized constraint type: %c",
+                 coninfo->contype);
     }

     /* Dump Constraint Comments --- only works for table constraints */
@@ -16602,13 +16593,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-                              PQntuples(res)),
-                     tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }
+        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+                          PQntuples(res)),
+                 tbinfo->dobj.name, PQntuples(res));

     seqtype = PQgetvalue(res, 0, 0);
     startv = PQgetvalue(res, 0, 1);
@@ -16637,7 +16625,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     }
     else
     {
-        fatal("unrecognized sequence type: %s", seqtype);
+        pg_fatal("unrecognized sequence type: %s", seqtype);
         default_minv = default_maxv = 0;    /* keep compiler quiet */
     }

@@ -16760,8 +16748,8 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
         TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);

         if (owning_tab == NULL)
-            fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-                  tbinfo->owning_tab, tbinfo->dobj.catId.oid);
+            pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+                     tbinfo->owning_tab, tbinfo->dobj.catId.oid);

         if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
         {
@@ -16824,13 +16812,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-                              PQntuples(res)),
-                     tbinfo->dobj.name, PQntuples(res));
-        exit_nicely(1);
-    }
+        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+                          PQntuples(res)),
+                 tbinfo->dobj.name, PQntuples(res));

     last = PQgetvalue(res, 0, 0);
     called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
@@ -16919,10 +16904,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
         else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
             appendPQExpBufferStr(query, "INSTEAD OF");
         else
-        {
-            pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
-            exit_nicely(1);
-        }
+            pg_fatal("unexpected tgtype value: %d", tginfo->tgtype);

         findx = 0;
         if (TRIGGER_FOR_INSERT(tginfo->tgtype))
@@ -16994,11 +16976,10 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
             if (p + tlen >= tgargs + lentgargs)
             {
                 /* hm, not found before end of bytea value... */
-                pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
-                             tginfo->tgargs,
-                             tginfo->dobj.name,
-                             tbinfo->dobj.name);
-                exit_nicely(1);
+                pg_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
+                         tginfo->tgargs,
+                         tginfo->dobj.name,
+                         tbinfo->dobj.name);
             }

             if (findx > 0)
@@ -17264,11 +17245,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

         if (PQntuples(res) != 1)
-        {
-            pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
-                         rinfo->dobj.name, tbinfo->dobj.name);
-            exit_nicely(1);
-        }
+            pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+                     rinfo->dobj.name, tbinfo->dobj.name);

         printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

@@ -17506,11 +17484,11 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
             int            j;

             if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
-                fatal("could not parse %s array", "extconfig");
+                pg_fatal("could not parse %s array", "extconfig");
             if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
-                fatal("could not parse %s array", "extcondition");
+                pg_fatal("could not parse %s array", "extcondition");
             if (nconfigitems != nconditionitems)
-                fatal("mismatched number of configurations and conditions for extension");
+                pg_fatal("mismatched number of configurations and conditions for extension");

             for (j = 0; j < nconfigitems; j++)
             {
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 1592090839..5de3241eb4 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -419,13 +419,13 @@ TopoSort(DumpableObject **objs,
         obj = objs[i];
         j = obj->dumpId;
         if (j <= 0 || j > maxDumpId)
-            fatal("invalid dumpId %d", j);
+            pg_fatal("invalid dumpId %d", j);
         idMap[j] = i;
         for (j = 0; j < obj->nDeps; j++)
         {
             k = obj->dependencies[j];
             if (k <= 0 || k > maxDumpId)
-                fatal("invalid dependency %d", k);
+                pg_fatal("invalid dependency %d", k);
             beforeConstraints[k]++;
         }
     }
@@ -658,7 +658,7 @@ findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)

     /* We'd better have fixed at least one loop */
     if (!fixedloop)
-        fatal("could not identify dependency loop");
+        pg_fatal("could not identify dependency loop");

     free(workspace);
     free(searchFailed);
@@ -1233,9 +1233,9 @@ repairDependencyLoop(DumpableObject **loop,
                                 "there are circular foreign-key constraints among these tables:",
                                 nLoop));
         for (i = 0; i < nLoop; i++)
-            pg_log_generic(PG_LOG_INFO, "  %s", loop[i]->name);
-        pg_log_generic(PG_LOG_INFO, "You might not be able to restore the dump without using --disable-triggers or
temporarilydropping the constraints."); 
-        pg_log_generic(PG_LOG_INFO, "Consider using a full dump instead of a --data-only dump to avoid this
problem.");
+            pg_log_info("  %s", loop[i]->name);
+        pg_log_info("You might not be able to restore the dump without using --disable-triggers or temporarily
droppingthe constraints."); 
+        pg_log_info("Consider using a full dump instead of a --data-only dump to avoid this problem.");
         if (nLoop > 1)
             removeObjectDependency(loop[0], loop[1]->dumpId);
         else                    /* must be a self-dependency */
@@ -1253,7 +1253,7 @@ repairDependencyLoop(DumpableObject **loop,
         char        buf[1024];

         describeDumpableObject(loop[i], buf, sizeof(buf));
-        pg_log_generic(PG_LOG_INFO, "  %s", buf);
+        pg_log_info("  %s", buf);
     }

     if (nLoop > 1)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..b5beb96b43 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -200,16 +200,15 @@ main(int argc, char *argv[])
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
-                         "pg_dump", progname, full_path);
+            pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                     "same directory as \"%s\".\n"
+                     "Check your installation.",
+                     "pg_dump", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
-                         "pg_dump", full_path, progname);
-        exit_nicely(1);
+            pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                     "but was not the same version as %s.\n"
+                     "Check your installation.",
+                     "pg_dump", full_path, progname);
     }

     pgdumpopts = createPQExpBuffer();
@@ -339,7 +338,8 @@ main(int argc, char *argv[])
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -349,8 +349,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -358,8 +357,7 @@ main(int argc, char *argv[])
         (globals_only || roles_only || tablespaces_only))
     {
         pg_log_error("option --exclude-database cannot be used together with -g/--globals-only, -r/--roles-only, or
-t/--tablespaces-only");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -367,30 +365,24 @@ main(int argc, char *argv[])
     if (globals_only && roles_only)
     {
         pg_log_error("options -g/--globals-only and -r/--roles-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (globals_only && tablespaces_only)
     {
         pg_log_error("options -g/--globals-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     if (if_exists && !output_clean)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_fatal("option --if-exists requires option -c/--clean");

     if (roles_only && tablespaces_only)
     {
         pg_log_error("options -r/--roles-only and -t/--tablespaces-only cannot be used together");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

@@ -451,10 +443,7 @@ main(int argc, char *argv[])
                                prompt_password, false);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", pgdb);
-            exit_nicely(1);
-        }
+            pg_fatal("could not connect to database \"%s\"", pgdb);
     }
     else
     {
@@ -468,8 +457,7 @@ main(int argc, char *argv[])
         {
             pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n"
                          "Please specify an alternative database.");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
     }
@@ -487,11 +475,8 @@ main(int argc, char *argv[])
     {
         OPF = fopen(filename, PG_BINARY_W);
         if (!OPF)
-        {
-            pg_log_error("could not open output file \"%s\": %m",
-                         filename);
-            exit_nicely(1);
-        }
+            pg_fatal("could not open output file \"%s\": %m",
+                     filename);
     }
     else
         OPF = stdout;
@@ -502,11 +487,8 @@ main(int argc, char *argv[])
     if (dumpencoding)
     {
         if (PQsetClientEncoding(conn, dumpencoding) < 0)
-        {
-            pg_log_error("invalid client encoding \"%s\" specified",
-                         dumpencoding);
-            exit_nicely(1);
-        }
+            pg_fatal("invalid client encoding \"%s\" specified",
+                     dumpencoding);
     }

     /*
@@ -1321,20 +1303,14 @@ dumpDatabases(PGconn *conn)

         ret = runPgDump(dbname, create_opts);
         if (ret != 0)
-        {
-            pg_log_error("pg_dump failed on database \"%s\", exiting", dbname);
-            exit_nicely(1);
-        }
+            pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);

         if (filename)
         {
             OPF = fopen(filename, PG_BINARY_A);
             if (!OPF)
-            {
-                pg_log_error("could not re-open the output file \"%s\": %m",
-                             filename);
-                exit_nicely(1);
-            }
+                pg_fatal("could not re-open the output file \"%s\": %m",
+                         filename);
         }

     }
@@ -1470,10 +1446,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         {
             conn_opts = PQconninfoParse(connection_string, &err_msg);
             if (conn_opts == NULL)
-            {
-                pg_log_error("%s", err_msg);
-                exit_nicely(1);
-            }
+                pg_fatal("%s", err_msg);

             for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
             {
@@ -1540,10 +1513,7 @@ connectDatabase(const char *dbname, const char *connection_string,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database \"%s\"", dbname);
-            exit_nicely(1);
-        }
+            pg_fatal("could not connect to database \"%s\"", dbname);

         if (PQstatus(conn) == CONNECTION_BAD &&
             PQconnectionNeedsPassword(conn) &&
@@ -1560,10 +1530,7 @@ connectDatabase(const char *dbname, const char *connection_string,
     if (PQstatus(conn) == CONNECTION_BAD)
     {
         if (fail_on_error)
-        {
-            pg_log_error("%s", PQerrorMessage(conn));
-            exit_nicely(1);
-        }
+            pg_fatal("%s", PQerrorMessage(conn));
         else
         {
             PQfinish(conn);
@@ -1589,17 +1556,11 @@ connectDatabase(const char *dbname, const char *connection_string,
     /* Check version */
     remoteversion_str = PQparameterStatus(conn, "server_version");
     if (!remoteversion_str)
-    {
-        pg_log_error("could not get server version");
-        exit_nicely(1);
-    }
+        pg_fatal("could not get server version");
     server_version = PQserverVersion(conn);
     if (server_version == 0)
-    {
-        pg_log_error("could not parse server version \"%s\"",
-                     remoteversion_str);
-        exit_nicely(1);
-    }
+        pg_fatal("could not parse server version \"%s\"",
+                 remoteversion_str);

     my_version = PG_VERSION_NUM;

@@ -1611,9 +1572,9 @@ connectDatabase(const char *dbname, const char *connection_string,
         && (server_version < 90200 ||
             (server_version / 100) > (my_version / 100)))
     {
-        pg_log_error("server version: %s; %s version: %s",
-                     remoteversion_str, progname, PG_VERSION);
         pg_log_error("aborting because of server version mismatch");
+        pg_log_error_detail("server version: %s; %s version: %s",
+                            remoteversion_str, progname, PG_VERSION);
         exit_nicely(1);
     }

@@ -1675,7 +1636,7 @@ executeQuery(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
@@ -1698,7 +1659,7 @@ executeCommand(PGconn *conn, const char *query)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_error("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit_nicely(1);
     }
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 55bf1b6975..049a100634 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -287,7 +287,8 @@ main(int argc, char **argv)
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit_nicely(1);
         }
     }
@@ -303,17 +304,13 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit_nicely(1);
     }

     /* Complain if neither -f nor -d was specified (except if dumping TOC) */
     if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
-    {
-        pg_log_error("one of -d/--dbname and -f/--file must be specified");
-        exit_nicely(1);
-    }
+        pg_fatal("one of -d/--dbname and -f/--file must be specified");

     /* Should get at most one of -d and -f, else user is confused */
     if (opts->cparams.dbname)
@@ -321,41 +318,28 @@ main(int argc, char **argv)
         if (opts->filename)
         {
             pg_log_error("options -d/--dbname and -f/--file cannot be used together");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit_nicely(1);
         }
         opts->useDB = 1;
     }

     if (opts->dataOnly && opts->schemaOnly)
-    {
-        pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");

     if (opts->dataOnly && opts->dropSchema)
-    {
-        pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -c/--clean and -a/--data-only cannot be used together");

     /*
      * -C is not compatible with -1, because we can't create a database inside
      * a transaction block.
      */
     if (opts->createDB && opts->single_txn)
-    {
-        pg_log_error("options -C/--create and -1/--single-transaction cannot be used together");
-        exit_nicely(1);
-    }
+        pg_fatal("options -C/--create and -1/--single-transaction cannot be used together");

     /* Can't do single-txn mode with multiple connections */
     if (opts->single_txn && numWorkers > 1)
-    {
-        pg_log_error("cannot specify both --single-transaction and multiple jobs");
-        exit_nicely(1);
-    }
+        pg_fatal("cannot specify both --single-transaction and multiple jobs");

     opts->disable_triggers = disable_triggers;
     opts->enable_row_security = enable_row_security;
@@ -369,10 +353,7 @@ main(int argc, char **argv)
     opts->no_subscriptions = no_subscriptions;

     if (if_exists && !opts->dropSchema)
-    {
-        pg_log_error("option --if-exists requires option -c/--clean");
-        exit_nicely(1);
-    }
+        pg_fatal("option --if-exists requires option -c/--clean");
     opts->if_exists = if_exists;
     opts->strict_names = strict_names;

@@ -396,9 +377,8 @@ main(int argc, char **argv)
                 break;

             default:
-                pg_log_error("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
-                             opts->formatName);
-                exit_nicely(1);
+                pg_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
+                         opts->formatName);
         }
     }

diff --git a/src/bin/pg_dump/t/003_pg_dump_with_server.pl b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
index 528db179cb..c284866326 100644
--- a/src/bin/pg_dump/t/003_pg_dump_with_server.pl
+++ b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
@@ -30,7 +30,7 @@ my ($cmd, $stdout, $stderr, $result);

 command_fails_like(
     [ "pg_dump", '-p', $port, '--include-foreign-data=s0', 'postgres' ],
-    qr/foreign-data wrapper \"dummy\" has no handler\r?\npg_dump: error: query was:.*t0/,
+    qr/foreign-data wrapper \"dummy\" has no handler\r?\ndetail: Query was: .*t0/,
     "correctly fails to dump a foreign table from a dummy FDW");

 command_ok(
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 1eb4509fca..d4772a2965 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -161,14 +161,11 @@ main(int argc, char *argv[])
                     /*------
                       translator: the second %s is a command line argument (-e, etc) */
                     pg_log_error("invalid argument for option %s", "-e");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_xid_epoch == -1)
-                {
-                    pg_log_error("transaction ID epoch (-e) must not be -1");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID epoch (-e) must not be -1");
                 break;

             case 'u':
@@ -177,14 +174,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-u");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_oldest_xid))
-                {
-                    pg_log_error("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u",
FirstNormalTransactionId);
                 break;

             case 'x':
@@ -193,14 +187,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-x");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (!TransactionIdIsNormal(set_xid))
-                {
-                    pg_log_error("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
                 break;

             case 'c':
@@ -209,30 +200,24 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-c");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 if (set_oldest_commit_ts_xid < 2 &&
                     set_oldest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");

                 if (set_newest_commit_ts_xid < 2 &&
                     set_newest_commit_ts_xid != 0)
-                {
-                    pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-                    exit(1);
-                }
+                    pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");
                 break;

             case 'o':
@@ -241,14 +226,11 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-o");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_oid == 0)
-                {
-                    pg_log_error("OID (-o) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("OID (-o) must not be 0");
                 break;

             case 'm':
@@ -257,7 +239,7 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != ',' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -265,24 +247,18 @@ main(int argc, char *argv[])
                 if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-m");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxid == 0)
-                {
-                    pg_log_error("multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("multitransaction ID (-m) must not be 0");

                 /*
                  * XXX It'd be nice to have more sanity checks here, e.g. so
                  * that oldest is not wrapped around w.r.t. nextMulti.
                  */
                 if (set_oldestmxid == 0)
-                {
-                    pg_log_error("oldest multitransaction ID (-m) must not be 0");
-                    exit(1);
-                }
+                    pg_fatal("oldest multitransaction ID (-m) must not be 0");
                 break;

             case 'O':
@@ -291,21 +267,18 @@ main(int argc, char *argv[])
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
                 {
                     pg_log_error("invalid argument for option %s", "-O");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }
                 if (set_mxoff == -1)
-                {
-                    pg_log_error("multitransaction offset (-O) must not be -1");
-                    exit(1);
-                }
+                    pg_fatal("multitransaction offset (-O) must not be -1");
                 break;

             case 'l':
                 if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
                 {
                     pg_log_error("invalid argument for option %s", "-l");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

@@ -320,19 +293,14 @@ main(int argc, char *argv[])
                 errno = 0;
                 set_wal_segsize = strtol(optarg, &endptr, 10) * 1024 * 1024;
                 if (endptr == optarg || *endptr != '\0' || errno != 0)
-                {
-                    pg_log_error("argument of --wal-segsize must be a number");
-                    exit(1);
-                }
+                    pg_fatal("argument of --wal-segsize must be a number");
                 if (!IsValidWalSegSize(set_wal_segsize))
-                {
-                    pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-                    exit(1);
-                }
+                    pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -345,15 +313,14 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (DataDir == NULL)
     {
         pg_log_error("no data directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -367,8 +334,8 @@ main(int argc, char *argv[])
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        pg_log_info("You must run %s as the PostgreSQL superuser.",
-                    progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+                          progname);
         exit(1);
     }
 #endif
@@ -377,20 +344,14 @@ main(int argc, char *argv[])

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(DataDir))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
-                     DataDir);
-        exit(1);
-    }
+        pg_fatal("could not read permissions of directory \"%s\": %m",
+                 DataDir);

     umask(pg_mode_mask);

     if (chdir(DataDir) < 0)
-    {
-        pg_log_error("could not change directory to \"%s\": %m",
-                     DataDir);
-        exit(1);
-    }
+        pg_fatal("could not change directory to \"%s\": %m",
+                 DataDir);

     /* Check that data directory matches our server version */
     CheckDataVersion();
@@ -402,16 +363,13 @@ main(int argc, char *argv[])
     if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
     {
         if (errno != ENOENT)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
-                         "postmaster.pid");
-            exit(1);
-        }
+            pg_fatal("could not open file \"%s\" for reading: %m",
+                     "postmaster.pid");
     }
     else
     {
         pg_log_error("lock file \"%s\" exists", "postmaster.pid");
-        pg_log_info("Is a server running?  If not, delete the lock file and try again.");
+        pg_log_error_hint("Is a server running?  If not, delete the lock file and try again.");
         exit(1);
     }

@@ -557,20 +515,16 @@ CheckDataVersion(void)
     char        rawline[64];

     if ((ver_fd = fopen(ver_file, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m",
-                     ver_file);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m",
+                 ver_file);

     /* version number has to be the first line read */
     if (!fgets(rawline, sizeof(rawline), ver_fd))
     {
         if (!ferror(ver_fd))
-            pg_log_error("unexpected empty file \"%s\"", ver_file);
+            pg_fatal("unexpected empty file \"%s\"", ver_file);
         else
-            pg_log_error("could not read file \"%s\": %m", ver_file);
-        exit(1);
+            pg_fatal("could not read file \"%s\": %m", ver_file);
     }

     /* strip trailing newline and carriage return */
@@ -579,8 +533,8 @@ CheckDataVersion(void)
     if (strcmp(rawline, PG_MAJORVERSION) != 0)
     {
         pg_log_error("data directory is of wrong version");
-        pg_log_info("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
-                    ver_file, rawline, PG_MAJORVERSION);
+        pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version
\"%s\".",
+                            ver_file, rawline, PG_MAJORVERSION);
         exit(1);
     }

@@ -612,10 +566,10 @@ read_controlfile(void)
         pg_log_error("could not open file \"%s\" for reading: %m",
                      XLOG_CONTROL_FILE);
         if (errno == ENOENT)
-            pg_log_info("If you are sure the data directory path is correct, execute\n"
-                        "  touch %s\n"
-                        "and try again.",
-                        XLOG_CONTROL_FILE);
+            pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
+                              "  touch %s\n"
+                              "and try again.",
+                              XLOG_CONTROL_FILE);
         exit(1);
     }

@@ -624,10 +578,7 @@ read_controlfile(void)

     len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
     if (len < 0)
-    {
-        pg_log_error("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
-        exit(1);
-    }
+        pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
     close(fd);

     if (len >= sizeof(ControlFileData) &&
@@ -968,10 +919,7 @@ FindEndOfXLOG(void)
      */
     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1003,16 +951,10 @@ FindEndOfXLOG(void)
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", XLOGDIR);

     /*
      * Finally, convert to new xlog seg size, and advance by one to ensure we
@@ -1036,10 +978,7 @@ KillExistingXLOG(void)

     xldir = opendir(XLOGDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", XLOGDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1048,24 +987,15 @@ KillExistingXLOG(void)
         {
             snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
 }


@@ -1083,10 +1013,7 @@ KillExistingArchiveStatus(void)

     xldir = opendir(ARCHSTATDIR);
     if (xldir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);

     while (errno = 0, (xlde = readdir(xldir)) != NULL)
     {
@@ -1098,24 +1025,15 @@ KillExistingArchiveStatus(void)
         {
             snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
             if (unlink(path) < 0)
-            {
-                pg_log_error("could not delete file \"%s\": %m", path);
-                exit(1);
-            }
+                pg_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
 }


@@ -1179,10 +1097,7 @@ WriteEmptyXLOG(void)
     fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
               pg_file_create_mode);
     if (fd < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", path);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", path);

     errno = 0;
     if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
@@ -1190,8 +1105,7 @@ WriteEmptyXLOG(void)
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write file \"%s\": %m", path);
-        exit(1);
+        pg_fatal("could not write file \"%s\": %m", path);
     }

     /* Fill the rest of the file with zeroes */
@@ -1203,16 +1117,12 @@ WriteEmptyXLOG(void)
         {
             if (errno == 0)
                 errno = ENOSPC;
-            pg_log_error("could not write file \"%s\": %m", path);
-            exit(1);
+            pg_fatal("could not write file \"%s\": %m", path);
         }
     }

     if (fsync(fd) != 0)
-    {
-        pg_log_error("fsync error: %m");
-        exit(1);
-    }
+        pg_fatal("fsync error: %m");

     close(fd);
 }
diff --git a/src/bin/pg_rewind/nls.mk b/src/bin/pg_rewind/nls.mk
index a561f965df..a50f9139df 100644
--- a/src/bin/pg_rewind/nls.mk
+++ b/src/bin/pg_rewind/nls.mk
@@ -2,7 +2,6 @@
 CATALOG_NAME     = pg_rewind
 AVAIL_LANGUAGES  = cs de es fr it ja ko pl pt_BR ru sv tr uk zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) datapagemap.c file_ops.c filemap.c libpq_source.c local_source.c
parsexlog.cpg_rewind.c timeline.c xlogreader.c ../../common/fe_memutils.c ../../common/restricted_token.c
../../fe_utils/archive.c../../fe_utils/recovery_gen.c 
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) pg_fatal report_invalid_record:2
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) report_invalid_record:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    pg_fatal:1:c-format \
     report_invalid_record:2:c-format
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index b39b5c1aac..6516ecaade 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -161,10 +161,6 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
-
             case 'c':
                 restore_wal = true;
                 break;
@@ -205,34 +201,39 @@ main(int argc, char **argv)
             case 4:
                 no_ensure_shutdown = true;
                 break;
+
+            default:
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+                exit(1);
         }
     }

     if (datadir_source == NULL && connstr_source == NULL)
     {
         pg_log_error("no source specified (--source-pgdata or --source-server)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_source != NULL && connstr_source != NULL)
     {
         pg_log_error("only one of --source-pgdata or --source-server can be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_target == NULL)
     {
         pg_log_error("no target data directory specified (--target-pgdata)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (writerecoveryconf && connstr_source == NULL)
     {
         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -240,7 +241,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -254,8 +255,8 @@ main(int argc, char **argv)
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
-                progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+                          progname);
         exit(1);
     }
 #endif
@@ -264,11 +265,8 @@ main(int argc, char **argv)

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(datadir_target))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
-                     datadir_target);
-        exit(1);
-    }
+        pg_fatal("could not read permissions of directory \"%s\": %m",
+                 datadir_target);

     umask(pg_mode_mask);

@@ -1036,16 +1034,15 @@ getRestoreCommand(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (rc == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
-                         "postgres", progname, full_path);
+            pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                     "same directory as \"%s\".\n"
+                     "Check your installation.",
+                     "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
-                         "postgres", full_path, progname);
-        exit(1);
+            pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                     "but was not the same version as %s.\n"
+                     "Check your installation.",
+                     "postgres", full_path, progname);
     }

     /*
@@ -1146,7 +1143,8 @@ ensureCleanShutdown(const char *argv0)
     if (system(postgres_cmd->data) != 0)
     {
         pg_log_error("postgres single-user mode in target cluster failed");
-        pg_fatal("Command was: %s", postgres_cmd->data);
+        pg_log_error_detail("Command was: %s", postgres_cmd->data);
+        exit(1);
     }

     destroyPQExpBuffer(postgres_cmd);
diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 388870ce95..393182fe2a 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -33,9 +33,6 @@ extern int    targetNentries;
 extern uint64 fetch_size;
 extern uint64 fetch_done;

-/* logging support */
-#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
-
 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
                            int tliIndex, XLogRecPtr endpoint,
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c
index df8f82a50c..983388c92b 100644
--- a/src/bin/pg_rewind/timeline.c
+++ b/src/bin/pg_rewind/timeline.c
@@ -73,19 +73,19 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
         {
             /* expect a numeric timeline ID as first field of line */
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a numeric timeline ID.");
+            pg_log_error_detail("Expected a numeric timeline ID.");
             exit(1);
         }
         if (nfields != 3)
         {
             pg_log_error("syntax error in history file: %s", fline);
-            pg_log_error("Expected a write-ahead log switchpoint location.");
+            pg_log_error_detail("Expected a write-ahead log switchpoint location.");
             exit(1);
         }
         if (entries && tli <= lasttli)
         {
             pg_log_error("invalid data in history file: %s", fline);
-            pg_log_error("Timeline IDs must be in increasing sequence.");
+            pg_log_error_detail("Timeline IDs must be in increasing sequence.");
             exit(1);
         }

@@ -106,7 +106,7 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
     if (entries && targetTLI <= lasttli)
     {
         pg_log_error("invalid data in history file");
-        pg_log_error("Timeline IDs must be less than child timeline's ID.");
+        pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
         exit(1);
     }

diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index ddabf64c58..f7bc199a30 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -47,10 +47,7 @@ do { \
     alarm_triggered = false; \
     if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
         INVALID_HANDLE_VALUE) \
-    { \
-        pg_log_error("could not create thread for alarm"); \
-        exit(1); \
-    } \
+        pg_fatal("could not create thread for alarm"); \
     gettimeofday(&start_t, NULL); \
 } while (0)
 #endif
@@ -95,7 +92,7 @@ static int    pg_fsync_writethrough(int fd);
 #endif
 static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops);

-#define die(msg) do { pg_log_error("%s: %m", _(msg)); exit(1); } while(0)
+#define die(msg) pg_fatal("%s: %m", _(msg))


 int
@@ -186,24 +183,20 @@ handle_args(int argc, char *argv[])
                     errno != 0 || optval != (unsigned int) optval)
                 {
                     pg_log_error("invalid argument for option %s", "--secs-per-test");
-                    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                     exit(1);
                 }

                 secs_per_test = (unsigned int) optval;
                 if (secs_per_test == 0)
-                {
-                    pg_log_error("%s must be in range %u..%u",
-                                 "--secs-per-test", 1, UINT_MAX);
-                    exit(1);
-                }
+                    pg_fatal("%s must be in range %u..%u",
+                             "--secs-per-test", 1, UINT_MAX);
                 break;

             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -211,8 +204,7 @@ handle_args(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 6d7fd88c0c..86d3dc46fa 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -12,6 +12,9 @@

 #include "libpq-fe.h"

+/* For now, pg_upgrade does not use common/logging.c; use our own pg_fatal */
+#undef pg_fatal
+
 /* Use port in the private/dynamic port number range */
 #define DEF_PGUPORT            50432

diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05cb520c11..299aba7c2c 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -252,8 +252,8 @@ main(int argc, char **argv)
                 canonicalize_path(wal_directory);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -261,9 +261,8 @@ main(int argc, char **argv)
     /* Get backup directory name */
     if (optind >= argc)
     {
-        pg_log_fatal("no backup directory specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error("no backup directory specified");
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }
     context.backup_directory = pstrdup(argv[optind++]);
@@ -272,10 +271,9 @@ main(int argc, char **argv)
     /* Complain if any arguments remain */
     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -295,16 +293,15 @@ main(int argc, char **argv)
             if (find_my_exec(argv[0], full_path) < 0)
                 strlcpy(full_path, progname, sizeof(full_path));
             if (ret == -1)
-                pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
-                             "same directory as \"%s\".\n"
-                             "Check your installation.",
-                             "pg_waldump", "pg_verifybackup", full_path);
+                pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+                         "same directory as \"%s\".\n"
+                         "Check your installation.",
+                         "pg_waldump", "pg_verifybackup", full_path);
             else
-                pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
-                             "but was not the same version as %s.\n"
-                             "Check your installation.",
-                             "pg_waldump", full_path, "pg_verifybackup");
-            exit(1);
+                pg_fatal("The program \"%s\" was found by \"%s\"\n"
+                         "but was not the same version as %s.\n"
+                         "Check your installation.",
+                         "pg_waldump", full_path, "pg_verifybackup");
         }
     }

@@ -449,7 +446,7 @@ report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
@@ -840,7 +837,7 @@ report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     context->saw_any_error = true;
@@ -857,7 +854,7 @@ report_fatal_error(const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 74d0a8d1b2..48fecfa315 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -190,7 +190,7 @@ sub test_fatal_error

     my ($test_name, $manifest_contents) = @_;

-    test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+    test_bad_manifest($test_name, qr/error: $test_name/, $manifest_contents);
     return;
 }

diff --git a/src/bin/pg_waldump/nls.mk b/src/bin/pg_waldump/nls.mk
index a3d5e88e4f..159638fc00 100644
--- a/src/bin/pg_waldump/nls.mk
+++ b/src/bin/pg_waldump/nls.mk
@@ -2,5 +2,5 @@
 CATALOG_NAME     = pg_waldump
 AVAIL_LANGUAGES  = cs de el es fr ja ko ru sv tr uk vi zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) pg_waldump.c
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) fatal_error
-GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) fatal_error:1:c-format
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS)
+GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 9ffe9e55bd..1925fee166 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -84,7 +84,6 @@ typedef struct XLogDumpStats
     Stats        record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
 } XLogDumpStats;

-#define fatal_error(...) do { pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)

 /*
  * When sigint is called, just tell the system to exit at the next possible
@@ -170,7 +169,7 @@ open_file_in_directory(const char *directory, const char *fname)
     fd = open(fpath, O_RDONLY | PG_BINARY, 0);

     if (fd < 0 && errno != ENOENT)
-        fatal_error("could not open file \"%s\": %m", fname);
+        pg_fatal("could not open file \"%s\": %m", fname);
     return fd;
 }

@@ -226,17 +225,17 @@ search_directory(const char *directory, const char *fname)
             WalSegSz = longhdr->xlp_seg_size;

             if (!IsValidWalSegSize(WalSegSz))
-                fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d byte", 
-                                     "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d bytes", 
-                                     WalSegSz),
-                            fname, WalSegSz);
+                pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d byte", 
+                                  "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file
\"%s\"header specifies %d bytes", 
+                                  WalSegSz),
+                         fname, WalSegSz);
         }
         else if (r < 0)
-            fatal_error("could not read file \"%s\": %m",
-                        fname);
+            pg_fatal("could not read file \"%s\": %m",
+                     fname);
         else
-            fatal_error("could not read file \"%s\": read %d of %d",
-                        fname, r, XLOG_BLCKSZ);
+            pg_fatal("could not read file \"%s\": read %d of %d",
+                     fname, r, XLOG_BLCKSZ);
         close(fd);
         return true;
     }
@@ -296,9 +295,9 @@ identify_target_directory(char *directory, char *fname)

     /* could not locate WAL file */
     if (fname)
-        fatal_error("could not locate WAL file \"%s\"", fname);
+        pg_fatal("could not locate WAL file \"%s\"", fname);
     else
-        fatal_error("could not find any WAL file");
+        pg_fatal("could not find any WAL file");

     return NULL;                /* not reached */
 }
@@ -339,7 +338,7 @@ WALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo,
         break;
     }

-    fatal_error("could not find file \"%s\": %m", fname);
+    pg_fatal("could not find file \"%s\": %m", fname);
 }

 /*
@@ -388,13 +387,13 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
         if (errinfo.wre_errno != 0)
         {
             errno = errinfo.wre_errno;
-            fatal_error("could not read from file %s, offset %d: %m",
-                        fname, errinfo.wre_off);
+            pg_fatal("could not read from file %s, offset %d: %m",
+                     fname, errinfo.wre_off);
         }
         else
-            fatal_error("could not read from file %s, offset %d: read %d of %d",
-                        fname, errinfo.wre_off, errinfo.wre_read,
-                        errinfo.wre_req);
+            pg_fatal("could not read from file %s, offset %d: read %d of %d",
+                     fname, errinfo.wre_off, errinfo.wre_read,
+                     errinfo.wre_req);
     }

     return count;
@@ -1129,13 +1128,13 @@ main(int argc, char **argv)
             waldir = directory;

             if (!verify_directory(waldir))
-                fatal_error("could not open directory \"%s\": %m", waldir);
+                pg_fatal("could not open directory \"%s\": %m", waldir);
         }

         waldir = identify_target_directory(waldir, fname);
         fd = open_file_in_directory(waldir, fname);
         if (fd < 0)
-            fatal_error("could not open file \"%s\"", fname);
+            pg_fatal("could not open file \"%s\"", fname);
         close(fd);

         /* parse position from file */
@@ -1165,15 +1164,15 @@ main(int argc, char **argv)

             fd = open_file_in_directory(waldir, fname);
             if (fd < 0)
-                fatal_error("could not open file \"%s\"", fname);
+                pg_fatal("could not open file \"%s\"", fname);
             close(fd);

             /* parse position from file */
             XLogFromFileName(fname, &private.timeline, &endsegno, WalSegSz);

             if (endsegno < segno)
-                fatal_error("ENDSEG %s is before STARTSEG %s",
-                            argv[optind + 1], argv[optind]);
+                pg_fatal("ENDSEG %s is before STARTSEG %s",
+                         argv[optind + 1], argv[optind]);

             if (XLogRecPtrIsInvalid(private.endptr))
                 XLogSegNoOffsetToRecPtr(endsegno + 1, 0, WalSegSz,
@@ -1213,14 +1212,14 @@ main(int argc, char **argv)
                                       .segment_close = WALDumpCloseSegment),
                            &private);
     if (!xlogreader_state)
-        fatal_error("out of memory while allocating a WAL reading processor");
+        pg_fatal("out of memory while allocating a WAL reading processor");

     /* first find a valid recptr to start from */
     first_record = XLogFindNextRecord(xlogreader_state, private.startptr);

     if (first_record == InvalidXLogRecPtr)
-        fatal_error("could not find a valid record after %X/%X",
-                    LSN_FORMAT_ARGS(private.startptr));
+        pg_fatal("could not find a valid record after %X/%X",
+                 LSN_FORMAT_ARGS(private.startptr));

     /*
      * Display a message that we're skipping data if `from` wasn't a pointer
@@ -1310,15 +1309,15 @@ main(int argc, char **argv)
         exit(0);

     if (errormsg)
-        fatal_error("error in WAL record at %X/%X: %s",
-                    LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
-                    errormsg);
+        pg_fatal("error in WAL record at %X/%X: %s",
+                 LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
+                 errormsg);

     XLogReaderFree(xlogreader_state);

     return EXIT_SUCCESS;

 bad_argument:
-    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     return EXIT_FAILURE;
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index acf3e56413..72e7677ae2 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1502,8 +1502,7 @@ accumStats(StatsData *stats, bool skipped, double lat, double lag,
             break;
         default:
             /* internal error which should never occur */
-            pg_log_fatal("unexpected error status: %d", estatus);
-            exit(1);
+            pg_fatal("unexpected error status: %d", estatus);
     }
 }

@@ -1516,8 +1515,8 @@ executeStatement(PGconn *con, const char *sql)
     res = PQexec(con, sql);
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
-        pg_log_fatal("query failed: %s", PQerrorMessage(con));
-        pg_log_info("query was: %s", sql);
+        pg_log_error("query failed: %s", PQerrorMessage(con));
+        pg_log_error_detail("Query was: %s", sql);
         exit(1);
     }
     PQclear(res);
@@ -1533,7 +1532,7 @@ tryExecuteStatement(PGconn *con, const char *sql)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("%s", PQerrorMessage(con));
-        pg_log_info("(ignoring this error and continuing anyway)");
+        pg_log_error_detail("(ignoring this error and continuing anyway)");
     }
     PQclear(res);
 }
@@ -2878,8 +2877,7 @@ evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)

         default:
             /* internal error which should never occur */
-            pg_log_fatal("unexpected enode type in evaluation: %d", expr->etype);
-            exit(1);
+            pg_fatal("unexpected enode type in evaluation: %d", expr->etype);
     }
 }

@@ -4447,8 +4445,7 @@ getResultString(bool skipped, EStatus estatus)
                 return "deadlock";
             default:
                 /* internal error which should never occur */
-                pg_log_fatal("unexpected error status: %d", estatus);
-                exit(1);
+                pg_fatal("unexpected error status: %d", estatus);
         }
     }
     else
@@ -4876,10 +4873,7 @@ initGenerateDataClientSide(PGconn *con)
     res = PQexec(con, copy_statement);

     if (PQresultStatus(res) != PGRES_COPY_IN)
-    {
-        pg_log_fatal("unexpected copy in result: %s", PQerrorMessage(con));
-        exit(1);
-    }
+        pg_fatal("unexpected copy in result: %s", PQerrorMessage(con));
     PQclear(res);

     start = pg_time_now();
@@ -4893,10 +4887,7 @@ initGenerateDataClientSide(PGconn *con)
                           INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",
                           j, k / naccounts + 1, 0);
         if (PQputline(con, sql.data))
-        {
-            pg_log_fatal("PQputline failed");
-            exit(1);
-        }
+            pg_fatal("PQputline failed");

         if (CancelRequested)
             break;
@@ -4938,15 +4929,9 @@ initGenerateDataClientSide(PGconn *con)
         fputc('\n', stderr);    /* Need to move to next line */

     if (PQputline(con, "\\.\n"))
-    {
-        pg_log_fatal("very last PQputline failed");
-        exit(1);
-    }
+        pg_fatal("very last PQputline failed");
     if (PQendcopy(con))
-    {
-        pg_log_fatal("PQendcopy failed");
-        exit(1);
-    }
+        pg_fatal("PQendcopy failed");

     termPQExpBuffer(&sql);

@@ -5086,17 +5071,14 @@ static void
 checkInitSteps(const char *initialize_steps)
 {
     if (initialize_steps[0] == '\0')
-    {
-        pg_log_fatal("no initialization steps specified");
-        exit(1);
-    }
+        pg_fatal("no initialization steps specified");

     for (const char *step = initialize_steps; *step != '\0'; step++)
     {
         if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
         {
-            pg_log_fatal("unrecognized initialization step \"%c\"", *step);
-            pg_log_info("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
+            pg_log_error("unrecognized initialization step \"%c\"", *step);
+            pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
             exit(1);
         }
     }
@@ -5117,10 +5099,7 @@ runInitSteps(const char *initialize_steps)
     initPQExpBuffer(&stats);

     if ((con = doConnect()) == NULL)
-    {
-        pg_log_fatal("could not create connection for initialization");
-        exit(1);
-    }
+        pg_fatal("could not create connection for initialization");

     setup_cancel_handler(NULL);
     SetCancelConn(con);
@@ -5163,7 +5142,7 @@ runInitSteps(const char *initialize_steps)
             case ' ':
                 break;            /* ignore */
             default:
-                pg_log_fatal("unrecognized initialization step \"%c\"", *step);
+                pg_log_error("unrecognized initialization step \"%c\"", *step);
                 PQfinish(con);
                 exit(1);
         }
@@ -5207,21 +5186,18 @@ GetTableInfo(PGconn *con, bool scale_given)
     {
         char       *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);

-        pg_log_fatal("could not count number of branches: %s", PQerrorMessage(con));
+        pg_log_error("could not count number of branches: %s", PQerrorMessage(con));

         if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
-            pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\"",
-                        PQdb(con));
+            pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
+                              PQdb(con));

         exit(1);
     }
     scale = atoi(PQgetvalue(res, 0, 0));
     if (scale < 0)
-    {
-        pg_log_fatal("invalid count(*) from pgbench_branches: \"%s\"",
-                     PQgetvalue(res, 0, 0));
-        exit(1);
-    }
+        pg_fatal("invalid count(*) from pgbench_branches: \"%s\"",
+                 PQgetvalue(res, 0, 0));
     PQclear(res);

     /* warn if we override user-given -s switch */
@@ -5268,8 +5244,8 @@ GetTableInfo(PGconn *con, bool scale_given)
          * This case is unlikely as pgbench already found "pgbench_branches"
          * above to compute the scale.
          */
-        pg_log_fatal("no pgbench_accounts table found in search_path");
-        pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
+        pg_log_error("no pgbench_accounts table found in search_path");
+        pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
         exit(1);
     }
     else                        /* PQntupes(res) == 1 */
@@ -5291,8 +5267,7 @@ GetTableInfo(PGconn *con, bool scale_given)
             else
             {
                 /* possibly a newer version with new partition method */
-                pg_log_fatal("unexpected partition method: \"%s\"", ps);
-                exit(1);
+                pg_fatal("unexpected partition method: \"%s\"", ps);
             }
         }

@@ -5384,7 +5359,7 @@ syntax_error(const char *source, int lineno,
     if (command != NULL)
         appendPQExpBuffer(&buf, " in command \"%s\"", command);

-    pg_log_fatal("%s", buf.data);
+    pg_log_error("%s", buf.data);

     termPQExpBuffer(&buf);

@@ -5734,9 +5709,8 @@ process_backslash_command(PsqlScanState sstate, const char *source)
 static void
 ConditionError(const char *desc, int cmdn, const char *msg)
 {
-    pg_log_fatal("condition error in script \"%s\" command %d: %s",
-                 desc, cmdn, msg);
-    exit(1);
+    pg_fatal("condition error in script \"%s\" command %d: %s",
+             desc, cmdn, msg);
 }

 /*
@@ -5972,18 +5946,12 @@ process_file(const char *filename, int weight)
     if (strcmp(filename, "-") == 0)
         fd = stdin;
     else if ((fd = fopen(filename, "r")) == NULL)
-    {
-        pg_log_fatal("could not open file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", filename);

     buf = read_file_contents(fd);

     if (ferror(fd))
-    {
-        pg_log_fatal("could not read file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not read file \"%s\": %m", filename);

     if (fd != stdin)
         fclose(fd);
@@ -6036,9 +6004,9 @@ findBuiltin(const char *name)

     /* error cases */
     if (found == 0)
-        pg_log_fatal("no builtin script found for name \"%s\"", name);
+        pg_log_error("no builtin script found for name \"%s\"", name);
     else                        /* found > 1 */
-        pg_log_fatal("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
+        pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);

     listAvailableScripts();
     exit(1);
@@ -6070,16 +6038,10 @@ parseScriptWeight(const char *option, char **script)
         errno = 0;
         wtmp = strtol(sep + 1, &badp, 10);
         if (errno != 0 || badp == sep + 1 || *badp != '\0')
-        {
-            pg_log_fatal("invalid weight specification: %s", sep);
-            exit(1);
-        }
+            pg_fatal("invalid weight specification: %s", sep);
         if (wtmp > INT_MAX || wtmp < 0)
-        {
-            pg_log_fatal("weight specification out of range (0 .. %d): %lld",
-                         INT_MAX, (long long) wtmp);
-            exit(1);
-        }
+            pg_fatal("weight specification out of range (0 .. %d): %lld",
+                     INT_MAX, (long long) wtmp);
         weight = wtmp;
     }
     else
@@ -6096,16 +6058,10 @@ static void
 addScript(const ParsedScript *script)
 {
     if (script->commands == NULL || script->commands[0] == NULL)
-    {
-        pg_log_fatal("empty command list for script \"%s\"", script->desc);
-        exit(1);
-    }
+        pg_fatal("empty command list for script \"%s\"", script->desc);

     if (num_scripts >= MAX_SCRIPTS)
-    {
-        pg_log_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
-        exit(1);
-    }
+        pg_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);

     CheckConditional(script);

@@ -6505,7 +6461,7 @@ set_random_seed(const char *seed)
         if (sscanf(seed, "%lu%c", &ulseed, &garbage) != 1)
         {
             pg_log_error("unrecognized random seed option \"%s\"", seed);
-            pg_log_info("Expecting an unsigned integer, \"time\" or \"rand\"");
+            pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
             return false;
         }
         iseed = (uint64) ulseed;
@@ -6639,10 +6595,7 @@ main(int argc, char **argv)

     /* set random seed early, because it may be used while parsing scripts. */
     if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
-    {
-        pg_log_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
-        exit(1);
-    }
+        pg_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");

     while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) !=
-1)
     {
@@ -6689,15 +6642,12 @@ main(int argc, char **argv)
 #else                            /* but BSD doesn't ... */
                 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
 #endif                            /* RLIMIT_NOFILE */
-                {
-                    pg_log_fatal("getrlimit failed: %m");
-                    exit(1);
-                }
+                    pg_fatal("getrlimit failed: %m");
                 if (rlim.rlim_cur < nclients + 3)
                 {
-                    pg_log_fatal("need at least %d open files, but system limit is %ld",
+                    pg_log_error("need at least %d open files, but system limit is %ld",
                                  nclients + 3, (long) rlim.rlim_cur);
-                    pg_log_info("Reduce number of clients, or use limit/ulimit to increase the system limit.");
+                    pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
                     exit(1);
                 }
 #endif                            /* HAVE_GETRLIMIT */
@@ -6711,10 +6661,7 @@ main(int argc, char **argv)
                 }
 #ifndef ENABLE_THREAD_SAFETY
                 if (nthreads != 1)
-                {
-                    pg_log_fatal("threads are not supported on this platform; use -j1");
-                    exit(1);
-                }
+                    pg_fatal("threads are not supported on this platform; use -j1");
 #endif                            /* !ENABLE_THREAD_SAFETY */
                 break;
             case 'C':
@@ -6787,10 +6734,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
-                    {
-                        pg_log_fatal("invalid variable definition: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid variable definition: \"%s\"", optarg);

                     *p++ = '\0';
                     if (!putVariable(&state[0].variables, "option", optarg, p))
@@ -6809,10 +6753,7 @@ main(int argc, char **argv)
                     if (strcmp(optarg, QUERYMODE[querymode]) == 0)
                         break;
                 if (querymode >= NUM_QUERYMODE)
-                {
-                    pg_log_fatal("invalid query mode (-M): \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid query mode (-M): \"%s\"", optarg);
                 break;
             case 'P':
                 benchmarking_option_set = true;
@@ -6828,10 +6769,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if (throttle_value <= 0.0)
-                    {
-                        pg_log_fatal("invalid rate limit: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid rate limit: \"%s\"", optarg);
                     /* Invert rate limit into per-transaction delay in usec */
                     throttle_delay = 1000000.0 / throttle_value;
                 }
@@ -6841,10 +6779,7 @@ main(int argc, char **argv)
                     double        limit_ms = atof(optarg);

                     if (limit_ms <= 0.0)
-                    {
-                        pg_log_fatal("invalid latency limit: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid latency limit: \"%s\"", optarg);
                     benchmarking_option_set = true;
                     latency_limit = (int64) (limit_ms * 1000);
                 }
@@ -6865,10 +6800,7 @@ main(int argc, char **argv)
                 benchmarking_option_set = true;
                 sample_rate = atof(optarg);
                 if (sample_rate <= 0.0 || sample_rate > 1.0)
-                {
-                    pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid sampling rate: \"%s\"", optarg);
                 break;
             case 5:                /* aggregate-interval */
                 benchmarking_option_set = true;
@@ -6891,10 +6823,7 @@ main(int argc, char **argv)
             case 9:                /* random-seed */
                 benchmarking_option_set = true;
                 if (!set_random_seed(optarg))
-                {
-                    pg_log_fatal("error while setting random seed from --random-seed option");
-                    exit(1);
-                }
+                    pg_fatal("error while setting random seed from --random-seed option");
                 break;
             case 10:            /* list */
                 {
@@ -6917,11 +6846,8 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "hash") == 0)
                     partition_method = PART_HASH;
                 else
-                {
-                    pg_log_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
-                                 optarg);
-                    exit(1);
-                }
+                    pg_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
+                             optarg);
                 break;
             case 13:            /* failures-detailed */
                 benchmarking_option_set = true;
@@ -6932,10 +6858,7 @@ main(int argc, char **argv)
                     int32        max_tries_arg = atoi(optarg);

                     if (max_tries_arg < 0)
-                    {
-                        pg_log_fatal("invalid number of maximum tries: \"%s\"", optarg);
-                        exit(1);
-                    }
+                        pg_fatal("invalid number of maximum tries: \"%s\"", optarg);

                     benchmarking_option_set = true;
                     max_tries = (uint32) max_tries_arg;
@@ -6946,9 +6869,9 @@ main(int argc, char **argv)
                 verbose_errors = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -6974,10 +6897,7 @@ main(int argc, char **argv)
     }

     if (total_weight == 0 && !is_init_mode)
-    {
-        pg_log_fatal("total script weight must not be zero");
-        exit(1);
-    }
+        pg_fatal("total script weight must not be zero");

     /* show per script stats if several scripts are used */
     if (num_scripts > 1)
@@ -7012,25 +6932,19 @@ main(int argc, char **argv)

     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (is_init_mode)
     {
         if (benchmarking_option_set)
-        {
-            pg_log_fatal("some of the specified options cannot be used in initialization (-i) mode");
-            exit(1);
-        }
+            pg_fatal("some of the specified options cannot be used in initialization (-i) mode");

         if (partitions == 0 && partition_method != PART_NONE)
-        {
-            pg_log_fatal("--partition-method requires greater than zero --partitions");
-            exit(1);
-        }
+            pg_fatal("--partition-method requires greater than zero --partitions");

         /* set default method */
         if (partitions > 0 && partition_method == PART_NONE)
@@ -7066,17 +6980,11 @@ main(int argc, char **argv)
     else
     {
         if (initialization_option_set)
-        {
-            pg_log_fatal("some of the specified options cannot be used in benchmarking mode");
-            exit(1);
-        }
+            pg_fatal("some of the specified options cannot be used in benchmarking mode");
     }

     if (nxacts > 0 && duration > 0)
-    {
-        pg_log_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
-        exit(1);
-    }
+        pg_fatal("specify either a number of transactions (-t) or a duration (-T), not both");

     /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
     if (nxacts <= 0 && duration <= 0)
@@ -7084,55 +6992,31 @@ main(int argc, char **argv)

     /* --sampling-rate may be used only with -l */
     if (sample_rate > 0.0 && !use_log)
-    {
-        pg_log_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
-        exit(1);
-    }
+        pg_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");

     /* --sampling-rate may not be used with --aggregate-interval */
     if (sample_rate > 0.0 && agg_interval > 0)
-    {
-        pg_log_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same
time");
-        exit(1);
-    }
+        pg_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same
time");

     if (agg_interval > 0 && !use_log)
-    {
-        pg_log_fatal("log aggregation is allowed only when actually logging transactions");
-        exit(1);
-    }
+        pg_fatal("log aggregation is allowed only when actually logging transactions");

     if (!use_log && logfile_prefix)
-    {
-        pg_log_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
-        exit(1);
-    }
+        pg_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");

     if (duration > 0 && agg_interval > duration)
-    {
-        pg_log_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)",
agg_interval,duration); 
-        exit(1);
-    }
+        pg_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval,
duration);

     if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
-    {
-        pg_log_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
-        exit(1);
-    }
+        pg_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);

     if (progress_timestamp && progress == 0)
-    {
-        pg_log_fatal("--progress-timestamp is allowed only under --progress");
-        exit(1);
-    }
+        pg_fatal("--progress-timestamp is allowed only under --progress");

     if (!max_tries)
     {
         if (!latency_limit && duration <= 0)
-        {
-            pg_log_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration
(-T)");
-            exit(1);
-        }
+            pg_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration
(-T)");
     }

     /*
@@ -7182,10 +7066,7 @@ main(int argc, char **argv)
     /* opening connection... */
     con = doConnect();
     if (con == NULL)
-    {
-        pg_log_fatal("could not create connection for setup");
-        exit(1);
-    }
+        pg_fatal("could not create connection for setup");

     /* report pgbench and server versions */
     printVersion(con);
@@ -7293,10 +7174,7 @@ main(int argc, char **argv)

     errno = THREAD_BARRIER_INIT(&barrier, nthreads);
     if (errno != 0)
-    {
-        pg_log_fatal("could not initialize barrier: %m");
-        exit(1);
-    }
+        pg_fatal("could not initialize barrier: %m");

 #ifdef ENABLE_THREAD_SAFETY
     /* start all threads but thread 0 which is executed directly later */
@@ -7308,10 +7186,7 @@ main(int argc, char **argv)
         errno = THREAD_CREATE(&thread->thread, threadRun, thread);

         if (errno != 0)
-        {
-            pg_log_fatal("could not create thread: %m");
-            exit(1);
-        }
+            pg_fatal("could not create thread: %m");
     }
 #else
     Assert(nthreads == 1);
@@ -7379,7 +7254,7 @@ main(int argc, char **argv)
     THREAD_BARRIER_DESTROY(&barrier);

     if (exit_code != 0)
-        pg_log_fatal("Run was aborted; the above results are incomplete.");
+        pg_log_error("Run was aborted; the above results are incomplete.");

     return exit_code;
 }
@@ -7413,10 +7288,7 @@ threadRun(void *arg)
         thread->logfile = fopen(logpath, "w");

         if (thread->logfile == NULL)
-        {
-            pg_log_fatal("could not open logfile \"%s\": %m", logpath);
-            exit(1);
-        }
+            pg_fatal("could not open logfile \"%s\": %m", logpath);
     }

     /* explicitly initialize the state machines */
@@ -7441,9 +7313,8 @@ threadRun(void *arg)
             if ((state[i].con = doConnect()) == NULL)
             {
                 /* coldly abort on initial connection failure */
-                pg_log_fatal("could not create connection for client %d",
-                             state[i].id);
-                exit(1);
+                pg_fatal("could not create connection for client %d",
+                         state[i].id);
             }
         }
     }
@@ -7713,10 +7584,7 @@ setalarm(int seconds)
         !CreateTimerQueueTimer(&timer, queue,
                                win32_timer_callback, NULL, seconds * 1000, 0,
                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-    {
-        pg_log_fatal("failed to set timer");
-        exit(1);
-    }
+        pg_fatal("failed to set timer");
 }

 #endif                            /* WIN32 */
@@ -7860,8 +7728,7 @@ add_socket_to_set(socket_set *sa, int fd, int idx)
          * Doing a hard exit here is a bit grotty, but it doesn't seem worth
          * complicating the API to make it less grotty.
          */
-        pg_log_fatal("too many client connections for select()");
-        exit(1);
+        pg_fatal("too many client connections for select()");
     }
     FD_SET(fd, &sa->fds);
     if (fd > sa->maxfd)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 079f4a1a76..aacbf8349f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -232,7 +232,7 @@ HandleSlashCmds(PsqlScanState scan_state,
     {
         pg_log_error("invalid command \\%s", cmd);
         if (pset.cur_cmd_interactive)
-            pg_log_info("Try \\? for help.");
+            pg_log_error_hint("Try \\? for help.");
         status = PSQL_CMD_ERROR;
     }

diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index d65b9a124f..f9c0a2a4d2 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -302,7 +302,7 @@ CheckConnection(void)
     {
         if (!pset.cur_cmd_interactive)
         {
-            pg_log_fatal("connection to server was lost");
+            pg_log_error("connection to server was lost");
             exit(EXIT_BADCONN);
         }

diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..e24979cba4 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -58,10 +58,7 @@ usage(unsigned short int pager)
     {
         user = get_user_name(&errstr);
         if (!user)
-        {
-            pg_log_fatal("%s", errstr);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("%s", errstr);
     }

     /*
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index e5c976fc4f..b0c4177a20 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -77,10 +77,7 @@ MainLoop(FILE *source)
     if (PQExpBufferBroken(query_buf) ||
         PQExpBufferBroken(previous_buf) ||
         PQExpBufferBroken(history_buf))
-    {
-        pg_log_error("out of memory");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("out of memory");

     /* main loop to get queries and execute them */
     while (successResult == EXIT_SUCCESS)
@@ -398,10 +395,7 @@ MainLoop(FILE *source)
             prompt_status = prompt_tmp;

             if (PQExpBufferBroken(query_buf))
-            {
-                pg_log_error("out of memory");
-                exit(EXIT_FAILURE);
-            }
+                pg_fatal("out of memory");

             /*
              * Increase statement line number counter for each linebreak added
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index be9dec749d..127b578b34 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -216,10 +216,7 @@ main(int argc, char *argv[])

     /* Bail out if -1 was specified but will be ignored. */
     if (options.single_txn && options.actions.head == NULL)
-    {
-        pg_log_fatal("-1 can only be used in non-interactive mode");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("-1 can only be used in non-interactive mode");

     if (!pset.popt.topt.fieldSep.separator &&
         !pset.popt.topt.fieldSep.separator_zero)
@@ -342,11 +339,8 @@ main(int argc, char *argv[])
     {
         pset.logfile = fopen(options.logfilename, "a");
         if (!pset.logfile)
-        {
-            pg_log_fatal("could not open log file \"%s\": %m",
-                         options.logfilename);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not open log file \"%s\": %m",
+                     options.logfilename);
     }

     if (!options.no_psqlrc)
@@ -607,10 +601,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                     }

                     if (!result)
-                    {
-                        pg_log_fatal("could not set printing parameter \"%s\"", value);
-                        exit(EXIT_FAILURE);
-                    }
+                        pg_fatal("could not set printing parameter \"%s\"", value);

                     free(value);
                     break;
@@ -716,10 +707,10 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
                 break;
             default:
         unknown_option:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        pset.progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.",
+                                  pset.progname);
                 exit(EXIT_FAILURE);
-                break;
         }
     }

@@ -781,10 +772,7 @@ process_psqlrc(char *argv0)
     char       *envrc = getenv("PSQLRC");

     if (find_my_exec(argv0, my_exec_path) < 0)
-    {
-        pg_log_fatal("could not find own program executable");
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not find own program executable");

     get_etc_path(my_exec_path, etc_path);

diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 4c97bd41d7..df1766679b 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -109,7 +109,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -128,7 +129,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -144,16 +145,10 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot cluster all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot cluster all databases and a specific one at the same time");

         if (tables.head != NULL)
-        {
-            pg_log_error("cannot cluster specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot cluster specific table(s) in all databases");

         cparams.dbname = maintenance_db;

diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index 6f612abf7c..cc4f92a35b 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -130,7 +130,8 @@ main(int argc, char *argv[])
                 icu_locale = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -149,22 +150,16 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 2]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

     if (locale)
     {
         if (lc_ctype)
-        {
-            pg_log_error("only one of --locale and --lc-ctype can be specified");
-            exit(1);
-        }
+            pg_fatal("only one of --locale and --lc-ctype can be specified");
         if (lc_collate)
-        {
-            pg_log_error("only one of --locale and --lc-collate can be specified");
-            exit(1);
-        }
+            pg_fatal("only one of --locale and --lc-collate can be specified");
         lc_ctype = locale;
         lc_collate = locale;
     }
@@ -172,10 +167,7 @@ main(int argc, char *argv[])
     if (encoding)
     {
         if (pg_char_to_encoding(encoding) < 0)
-        {
-            pg_log_error("\"%s\" is not a valid encoding name", encoding);
-            exit(1);
-        }
+            pg_fatal("\"%s\" is not a valid encoding name", encoding);
     }

     if (dbname == NULL)
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index d6ce04a809..bfba0d09d1 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -166,7 +166,8 @@ main(int argc, char *argv[])
                 interactive = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -181,7 +182,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -274,11 +275,8 @@ main(int argc, char *argv[])
                                                    newuser,
                                                    NULL);
         if (!encrypted_password)
-        {
-            pg_log_error("password encryption failed: %s",
-                         PQerrorMessage(conn));
-            exit(1);
-        }
+            pg_fatal("password encryption failed: %s",
+                     PQerrorMessage(conn));
         appendStringLiteralConn(&sql, encrypted_password, conn);
         PQfreemem(encrypted_password);
     }
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index 7e321dd11b..afc00dac78 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -100,7 +100,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -109,7 +110,7 @@ main(int argc, char *argv[])
     {
         case 0:
             pg_log_error("missing required argument database name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         case 1:
             dbname = argv[optind];
@@ -117,7 +118,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index dfe4a5088c..82c1f35ab2 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -91,7 +91,8 @@ main(int argc, char *argv[])
                 /* this covers the long options */
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -106,7 +107,7 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 1]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

@@ -119,7 +120,7 @@ main(int argc, char *argv[])
         else
         {
             pg_log_error("missing required argument role name");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c
index a7653b3eaf..1aa834742d 100644
--- a/src/bin/scripts/pg_isready.c
+++ b/src/bin/scripts/pg_isready.c
@@ -93,7 +93,8 @@ main(int argc, char **argv)
                 pguser = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);

                 /*
                  * We need to make sure we don't return 1 here because someone
@@ -107,7 +108,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);

         /*
          * We need to make sure we don't return 1 here because someone
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index c292d43203..f3b03ec325 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -170,7 +170,8 @@ main(int argc, char *argv[])
                 tablespace = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -189,7 +190,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -205,30 +206,15 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot reindex all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex all databases and a specific one at the same time");
         if (syscatalog)
-        {
-            pg_log_error("cannot reindex all databases and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex all databases and system catalogs at the same time");
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific schema(s) in all databases");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific table(s) in all databases");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific index(es) in all databases");

         cparams.dbname = maintenance_db;

@@ -238,26 +224,14 @@ main(int argc, char *argv[])
     else if (syscatalog)
     {
         if (schemas.head != NULL)
-        {
-            pg_log_error("cannot reindex specific schema(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific schema(s) and system catalogs at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot reindex specific table(s) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific table(s) and system catalogs at the same time");
         if (indexes.head != NULL)
-        {
-            pg_log_error("cannot reindex specific index(es) and system catalogs at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot reindex specific index(es) and system catalogs at the same time");

         if (concurrentCons > 1)
-        {
-            pg_log_error("cannot use multiple jobs to reindex system catalogs");
-            exit(1);
-        }
+            pg_fatal("cannot use multiple jobs to reindex system catalogs");

         if (dbname == NULL)
         {
@@ -283,10 +257,7 @@ main(int argc, char *argv[])
          * depending on the same relation.
          */
         if (concurrentCons > 1 && indexes.head != NULL)
-        {
-            pg_log_error("cannot use multiple jobs to reindex indexes");
-            exit(1);
-        }
+            pg_fatal("cannot use multiple jobs to reindex indexes");

         if (dbname == NULL)
         {
@@ -349,17 +320,15 @@ reindex_one_database(ConnParams *cparams, ReindexType type,
     if (concurrently && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "concurrently", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "concurrently", "12");
     }

     if (tablespace && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "tablespace", "14");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "tablespace", "14");
     }

     if (!parallel)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 4f6917fd39..92f1ffe147 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -237,7 +237,8 @@ main(int argc, char *argv[])
                 vacopts.process_toast = false;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -256,54 +257,33 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (vacopts.analyze_only)
     {
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "full");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "full");
         if (vacopts.freeze)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "freeze");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "freeze");
         if (vacopts.disable_page_skipping)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "disable-page-skipping");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "disable-page-skipping");
         if (vacopts.no_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-index-cleanup");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-index-cleanup");
         if (vacopts.force_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "force-index-cleanup");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "force-index-cleanup");
         if (!vacopts.do_truncate)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-truncate");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-truncate");
         if (!vacopts.process_toast)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "no-process-toast");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "no-process-toast");
         /* allow 'and_analyze' with 'analyze_only' */
     }

@@ -311,26 +291,17 @@ main(int argc, char *argv[])
     if (vacopts.parallel_workers >= 0)
     {
         if (vacopts.analyze_only)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
-                         "parallel");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing only analyze",
+                     "parallel");
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing full vacuum",
-                         "parallel");
-            exit(1);
-        }
+            pg_fatal("cannot use the \"%s\" option when performing full vacuum",
+                     "parallel");
     }

     /* Prohibit --no-index-cleanup and --force-index-cleanup together */
     if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
-    {
-        pg_log_error("cannot use the \"%s\" option with the \"%s\" option",
-                     "no-index-cleanup", "force-index-cleanup");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
+                 "no-index-cleanup", "force-index-cleanup");

     /* fill cparams except for dbname, which is set below */
     cparams.pghost = host;
@@ -348,15 +319,9 @@ main(int argc, char *argv[])
     if (alldb)
     {
         if (dbname)
-        {
-            pg_log_error("cannot vacuum all databases and a specific one at the same time");
-            exit(1);
-        }
+            pg_fatal("cannot vacuum all databases and a specific one at the same time");
         if (tables.head != NULL)
-        {
-            pg_log_error("cannot vacuum specific table(s) in all databases");
-            exit(1);
-        }
+            pg_fatal("cannot vacuum specific table(s) in all databases");

         cparams.dbname = maintenance_db;

@@ -457,71 +422,56 @@ vacuum_one_database(ConnParams *cparams,
     if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "disable-page-skipping", "9.6");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "disable-page-skipping", "9.6");
     }

     if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-index-cleanup", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-index-cleanup", "12");
     }

     if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "force-index-cleanup", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "force-index-cleanup", "12");
     }

     if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-truncate", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-truncate", "12");
     }

     if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "no-process-toast", "14");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "no-process-toast", "14");
     }

     if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "skip-locked", "12");
-        exit(1);
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "skip-locked", "12");
     }

     if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--min-xid-age", "9.6");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--min-xid-age", "9.6");

     if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--min-mxid-age", "9.6");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--min-mxid-age", "9.6");

     if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-                     "--parallel", "13");
-        exit(1);
-    }
+        pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+                 "--parallel", "13");

     if (!quiet)
     {
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 348f046a44..4c0da6e124 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -70,11 +70,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                         ControlFilePath)));
 #else
     if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
-    {
-        pg_log_fatal("could not open file \"%s\" for reading: %m",
-                     ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not open file \"%s\" for reading: %m",
+                 ControlFilePath);
 #endif

     r = read(fd, ControlFile, sizeof(ControlFileData));
@@ -86,10 +83,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                     (errcode_for_file_access(),
                      errmsg("could not read file \"%s\": %m", ControlFilePath)));
 #else
-        {
-            pg_log_fatal("could not read file \"%s\": %m", ControlFilePath);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not read file \"%s\": %m", ControlFilePath);
 #endif
         else
 #ifndef FRONTEND
@@ -98,11 +92,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                      errmsg("could not read file \"%s\": read %d of %zu",
                             ControlFilePath, r, sizeof(ControlFileData))));
 #else
-        {
-            pg_log_fatal("could not read file \"%s\": read %d of %zu",
-                         ControlFilePath, r, sizeof(ControlFileData));
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not read file \"%s\": read %d of %zu",
+                     ControlFilePath, r, sizeof(ControlFileData));
 #endif
     }

@@ -114,10 +105,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
                         ControlFilePath)));
 #else
     if (close(fd) != 0)
-    {
-        pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif

     /* Check the CRC. */
@@ -203,10 +191,7 @@ update_controlfile(const char *DataDir,
 #else
     if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
                    pg_file_create_mode)) == -1)
-    {
-        pg_log_fatal("could not open file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
-    }
+        pg_fatal("could not open file \"%s\": %m", ControlFilePath);
 #endif

     errno = 0;
@@ -225,8 +210,7 @@ update_controlfile(const char *DataDir,
                  errmsg("could not write file \"%s\": %m",
                         ControlFilePath)));
 #else
-        pg_log_fatal("could not write file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
+        pg_fatal("could not write file \"%s\": %m", ControlFilePath);
 #endif
     }
 #ifndef FRONTEND
@@ -245,10 +229,7 @@ update_controlfile(const char *DataDir,
         pgstat_report_wait_end();
 #else
         if (fsync(fd) != 0)
-        {
-            pg_log_fatal("could not fsync file \"%s\": %m", ControlFilePath);
-            exit(EXIT_FAILURE);
-        }
+            pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
 #endif
     }

@@ -260,8 +241,7 @@ update_controlfile(const char *DataDir,
                  errmsg("could not close file \"%s\": %m",
                         ControlFilePath)));
 #else
-        pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-        exit(EXIT_FAILURE);
+        pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif
     }
 }
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7138068633..19d308ad1f 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -300,7 +300,7 @@ fsync_fname(const char *fname, bool isdir)
      */
     if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL)))
     {
-        pg_log_fatal("could not fsync file \"%s\": %m", fname);
+        pg_log_error("could not fsync file \"%s\": %m", fname);
         (void) close(fd);
         exit(EXIT_FAILURE);
     }
@@ -370,7 +370,7 @@ durable_rename(const char *oldfile, const char *newfile)
     {
         if (fsync(fd) != 0)
         {
-            pg_log_fatal("could not fsync file \"%s\": %m", newfile);
+            pg_log_error("could not fsync file \"%s\": %m", newfile);
             close(fd);
             exit(EXIT_FAILURE);
         }
@@ -448,7 +448,7 @@ get_dirent_type(const char *path,
         {
             result = PGFILETYPE_ERROR;
 #ifdef FRONTEND
-            pg_log_generic(elevel, "could not stat file \"%s\": %m", path);
+            pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
 #else
             ereport(elevel,
                     (errcode_for_file_access(),
diff --git a/src/common/logging.c b/src/common/logging.c
index 9a076bb812..18d6669f27 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -151,6 +151,9 @@ pg_logging_init(const char *argv0)
     }
 }

+/*
+ * Change the logging flags.
+ */
 void
 pg_logging_config(int new_flags)
 {
@@ -194,17 +197,19 @@ pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)
 }

 void
-pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
+pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+               const char *pg_restrict fmt,...)
 {
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(level, fmt, ap);
+    pg_log_generic_v(level, part, fmt, ap);
     va_end(ap);
 }

 void
-pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
+pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                 const char *pg_restrict fmt, va_list ap)
 {
     int            save_errno = errno;
     const char *filename = NULL;
@@ -232,7 +237,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     fmt = _(fmt);

-    if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+    if (part == PG_LOG_PRIMARY &&
+        (!(log_flags & PG_LOG_FLAG_TERSE) || filename))
     {
         if (sgr_locus)
             fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
@@ -251,30 +257,34 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a

     if (!(log_flags & PG_LOG_FLAG_TERSE))
     {
-        switch (level)
+        switch (part)
         {
-            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);
+            case PG_LOG_PRIMARY:
+                switch (level)
+                {
+                    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;
+                }
                 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);
+            case PG_LOG_DETAIL:
+                fprintf(stderr, _("detail: "));
                 break;
-            default:
+            case PG_LOG_HINT:
+                fprintf(stderr, _("hint: "));
                 break;
         }
     }
diff --git a/src/common/restricted_token.c b/src/common/restricted_token.c
index 48b1ce0585..82b74b565e 100644
--- a/src/common/restricted_token.c
+++ b/src/common/restricted_token.c
@@ -190,10 +190,7 @@ get_restricted_token(void)
             WaitForSingleObject(pi.hProcess, INFINITE);

             if (!GetExitCodeProcess(pi.hProcess, &x))
-            {
-                pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError());
-                exit(1);
-            }
+                pg_fatal("could not get exit code from subprocess: error code %lu", GetLastError());
             exit(x);
         }
         pg_free(cmdline);
diff --git a/src/fe_utils/archive.c b/src/fe_utils/archive.c
index 361c1c25ea..53d42c2be4 100644
--- a/src/fe_utils/archive.c
+++ b/src/fe_utils/archive.c
@@ -49,10 +49,7 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
     xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
                                          xlogfname, NULL);
     if (xlogRestoreCmd == NULL)
-    {
-        pg_log_fatal("cannot use restore_command with %%r placeholder");
-        exit(1);
-    }
+        pg_fatal("cannot use restore_command with %%r placeholder");

     /*
      * Execute restore_command, which should copy the missing file from
@@ -70,22 +67,16 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         if (stat(xlogpath, &stat_buf) == 0)
         {
             if (expectedSize > 0 && stat_buf.st_size != expectedSize)
-            {
-                pg_log_fatal("unexpected file size for \"%s\": %lld instead of %lld",
-                             xlogfname, (long long int) stat_buf.st_size,
-                             (long long int) expectedSize);
-                exit(1);
-            }
+                pg_fatal("unexpected file size for \"%s\": %lld instead of %lld",
+                         xlogfname, (long long int) stat_buf.st_size,
+                         (long long int) expectedSize);
             else
             {
                 int            xlogfd = open(xlogpath, O_RDONLY | PG_BINARY, 0);

                 if (xlogfd < 0)
-                {
-                    pg_log_fatal("could not open file \"%s\" restored from archive: %m",
-                                 xlogpath);
-                    exit(1);
-                }
+                    pg_fatal("could not open file \"%s\" restored from archive: %m",
+                             xlogpath);
                 else
                     return xlogfd;
             }
@@ -93,11 +84,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
         else
         {
             if (errno != ENOENT)
-            {
-                pg_log_fatal("could not stat file \"%s\": %m",
-                             xlogpath);
-                exit(1);
-            }
+                pg_fatal("could not stat file \"%s\": %m",
+                         xlogpath);
         }
     }

@@ -108,11 +96,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
      * fatal too.
      */
     if (wait_result_is_any_signal(rc, true))
-    {
-        pg_log_fatal("restore_command failed: %s",
-                     wait_result_to_str(rc));
-        exit(1);
-    }
+        pg_fatal("restore_command failed: %s",
+                 wait_result_to_str(rc));

     /*
      * The file is not available, so just let the caller decide what to do
diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
index a30c66f13a..f2e583f9fa 100644
--- a/src/fe_utils/connect_utils.c
+++ b/src/fe_utils/connect_utils.c
@@ -88,11 +88,8 @@ connectDatabase(const ConnParams *cparams, const char *progname,
         conn = PQconnectdbParams(keywords, values, true);

         if (!conn)
-        {
-            pg_log_error("could not connect to database %s: out of memory",
-                         cparams->dbname);
-            exit(1);
-        }
+            pg_fatal("could not connect to database %s: out of memory",
+                     cparams->dbname);

         /*
          * No luck?  Trying asking (again) for a password.
@@ -117,8 +114,7 @@ connectDatabase(const ConnParams *cparams, const char *progname,
             PQfinish(conn);
             return NULL;
         }
-        pg_log_error("%s", PQerrorMessage(conn));
-        exit(1);
+        pg_fatal("%s", PQerrorMessage(conn));
     }

     /* Start strict; callers may override this. */
diff --git a/src/fe_utils/parallel_slot.c b/src/fe_utils/parallel_slot.c
index 5896a8a6ca..684327885d 100644
--- a/src/fe_utils/parallel_slot.c
+++ b/src/fe_utils/parallel_slot.c
@@ -298,10 +298,7 @@ connect_slot(ParallelSlotArray *sa, int slotno, const char *dbname)
     sa->cparams->override_dbname = old_override;

     if (PQsocket(slot->connection) >= FD_SETSIZE)
-    {
-        pg_log_fatal("too many jobs for this platform");
-        exit(1);
-    }
+        pg_fatal("too many jobs for this platform");

     /* Setup the connection using the supplied command, if any. */
     if (sa->initcmd)
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c
index 0b31b33f17..2fc6e2405b 100644
--- a/src/fe_utils/query_utils.c
+++ b/src/fe_utils/query_utils.c
@@ -31,7 +31,7 @@ executeQuery(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_TUPLES_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
@@ -56,7 +56,7 @@ executeCommand(PGconn *conn, const char *query, bool echo)
         PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("query failed: %s", PQerrorMessage(conn));
-        pg_log_info("query was: %s", query);
+        pg_log_error_detail("Query was: %s", query);
         PQfinish(conn);
         exit(1);
     }
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
index 9407e76bba..c9a423038a 100644
--- a/src/fe_utils/recovery_gen.c
+++ b/src/fe_utils/recovery_gen.c
@@ -31,10 +31,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     contents = createPQExpBuffer();
     if (!contents)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     /*
      * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
@@ -45,10 +42,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)

     connOptions = PQconninfo(pgconn);
     if (connOptions == NULL)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     initPQExpBuffer(&conninfo_buf);
     for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
@@ -73,10 +67,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
         appendConnStrVal(&conninfo_buf, opt->val);
     }
     if (PQExpBufferDataBroken(conninfo_buf))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     /*
      * Escape the connection string, so that it can be put in the config file.
@@ -96,10 +87,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
     }

     if (PQExpBufferBroken(contents))
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");

     PQconninfoFree(connOptions);

@@ -130,16 +118,10 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)

     cf = fopen(filename, use_recovery_conf ? "w" : "a");
     if (cf == NULL)
-    {
-        pg_log_error("could not open file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not open file \"%s\": %m", filename);

     if (fwrite(contents->data, contents->len, 1, cf) != 1)
-    {
-        pg_log_error("could not write to file \"%s\": %m", filename);
-        exit(1);
-    }
+        pg_fatal("could not write to file \"%s\": %m", filename);

     fclose(cf);

@@ -148,10 +130,7 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
         snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
         cf = fopen(filename, "w");
         if (cf == NULL)
-        {
-            pg_log_error("could not create file \"%s\": %m", filename);
-            exit(1);
-        }
+            pg_fatal("could not create file \"%s\": %m", filename);

         fclose(cf);
     }
@@ -167,9 +146,6 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_fatal("out of memory");
     return result;
 }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index 43cc79afa8..e213bb70d0 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
 enum pg_log_level
 {
     /*
-     * Not initialized yet
+     * Not initialized yet (not to be used as an actual message log level).
      */
     PG_LOG_NOTSET = 0,

@@ -43,20 +43,42 @@ enum pg_log_level
     PG_LOG_ERROR,

     /*
-     * Severe errors that cause program termination.  (One-shot programs may
-     * chose to label even fatal errors as merely "errors".  The distinction
-     * is up to the program.)
-     */
-    PG_LOG_FATAL,
-
-    /*
-     * Turn all logging off.
+     * Turn all logging off (not to be used as an actual message log level).
      */
     PG_LOG_OFF,
 };

+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
 extern enum pg_log_level __pg_log_level;

+/*
+ * A log message can have several parts.  The primary message is required,
+ * others are optional.  When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+    /*
+     * The primary message.  Try to keep it to one line; follow the backend's
+     * style guideline for primary messages.
+     */
+    PG_LOG_PRIMARY,
+
+    /*
+     * Additional detail.  Follow the backend's style guideline for detail
+     * messages.
+     */
+    PG_LOG_DETAIL,
+
+    /*
+     * Hint (not guaranteed correct) about how to fix the problem.  Follow the
+     * backend's style guideline for hint messages.
+     */
+    PG_LOG_HINT,
+};
+
 /*
  * Kind of a hack to be able to produce the psql output exactly as required by
  * the regression tests.
@@ -70,27 +92,84 @@ void        pg_logging_increase_verbosity(void);
 void        pg_logging_set_pre_callback(void (*cb) (void));
 void        pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));

-void        pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void        pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2,
0);
+void        pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+                           const char *pg_restrict fmt,...)
+            pg_attribute_printf(3, 4);
+void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                             const char *pg_restrict fmt, va_list ap)
+            pg_attribute_printf(3, 0);
+
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)

-#define pg_log_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
     } while(0)

-#define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
-        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_detail(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_hint(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+    } while(0)
+
+/*
+ * A common shortcut: pg_log_error() and immediately exit(1).
+ */
+#define pg_fatal(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit(1); \
     } while(0)

 #endif                            /* COMMON_LOGGING_H */
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 8192927010..4a3d0ec2c5 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -293,8 +293,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
 #define SIMPLEHASH_H

 #ifdef FRONTEND
-#define sh_error(...) \
-    do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#define sh_error(...) pg_fatal(__VA_ARGS__)
 #define sh_log(...) pg_log_info(__VA_ARGS__)
 #else
 #define sh_error(...) elog(ERROR, __VA_ARGS__)
diff --git a/src/nls-global.mk b/src/nls-global.mk
index 53129f0a04..c1f7982300 100644
--- a/src/nls-global.mk
+++ b/src/nls-global.mk
@@ -72,10 +72,16 @@ BACKEND_COMMON_GETTEXT_FLAGS = \
 FRONTEND_COMMON_GETTEXT_FILES = $(top_srcdir)/src/common/logging.c

 FRONTEND_COMMON_GETTEXT_TRIGGERS = \
-    pg_log_fatal pg_log_error pg_log_warning pg_log_info pg_log_generic:2 pg_log_generic_v:2
+    pg_log_error pg_log_error_detail pg_log_error_hint \
+    pg_log_warning pg_log_warning_detail pg_log_warning_hint \
+    pg_log_info pg_log_info_detail pg_log_info_hint \
+    pg_fatal pg_log_generic:3 pg_log_generic_v:3

 FRONTEND_COMMON_GETTEXT_FLAGS = \
-    pg_log_fatal:1:c-format pg_log_error:1:c-format pg_log_warning:1:c-format pg_log_info:1:c-format
pg_log_generic:2:c-formatpg_log_generic_v:2:c-format 
+    pg_log_error:1:c-format pg_log_error_detail:1:c-format pg_log_error_hint:1:c-format \
+    pg_log_warning:1:c-format pg_log_warning_detail:1:c-format pg_log_warning_hint:1:c-format \
+    pg_log_info:1:c-format pg_log_info_detail:1:c-format pg_log_info_hint:1:c-format \
+    pg_fatal:1:c-format pg_log_generic:3:c-format pg_log_generic_v:3:c-format


 all-po: $(MO_FILES)

Re: Frontend error logging style

От
Daniel Gustafsson
Дата:
> On 27 Mar 2022, at 22:19, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>
> Here's a rebase up to today's HEAD.  I've fixed the merge problems,
> but there may be some stray new error calls that could be converted
> to use pg_fatal() and haven't been.  I don't want to do a full
> fresh scan of the code until we're about ready to commit this.

I focused on finding any changes in logic (exiting where it previously kept
going after error, or vice versa) and was unable to find any which inspires
confidence.  This was a large patch to read through, so I'm fairly sure I've
missed something.

Below are comments (nitpicks to a large extent) which either relates to this
patch or to the messages in general.  For the latter, feel free to deem them
out of scope and we can take those comments separately from this work on
-hackers.

As a side-note realization from reading this, there is a tremendous amount of
code paths in pg_dump which leads to a fatal error..  thats not necessarily a
bad thing, it's just striking when reading them all at once.

> @@ -508,24 +502,15 @@ writefile(char *path, char **lines)

>     if (fclose(out_file))
> -    {
> -        pg_log_error("could not write file \"%s\": %m", path);
> -        exit(1);
> -    }
> +        pg_fatal("could not write file \"%s\": %m", path);
> }

Should we update this message to differentiate it from the fwrite error case?
Something like "an error occurred during writing.."

> @@ -2057,10 +2004,7 @@ check_locale_name(int category, const char *locale, char **canonname)
>
>     save = setlocale(category, NULL);
>     if (!save)
> -    {
> -        pg_log_error("setlocale() failed");
> -        exit(1);
> -    }
> +        pg_fatal("setlocale() failed");

Should this gain a hint message for those users who have no idea what this
really means?

> @@ -2127,15 +2067,14 @@ check_locale_encoding(const char *locale, int user_enc)
>           user_enc == PG_SQL_ASCII))
>     {
>         pg_log_error("encoding mismatch");
> -        fprintf(stderr,
> -                _("The encoding you selected (%s) and the encoding that the\n"
> -                  "selected locale uses (%s) do not match.  This would lead to\n"
> -                  "misbehavior in various character string processing functions.\n"
> -                  "Rerun %s and either do not specify an encoding explicitly,\n"
> -                  "or choose a matching combination.\n"),
> -                pg_encoding_to_char(user_enc),
> -                pg_encoding_to_char(locale_enc),
> -                progname);
> +        pg_log_error_detail("The encoding you selected (%s) and the encoding that the "
> +                            "selected locale uses (%s) do not match. This would lead to "

I will be sad to see double space after period go, but I think I'm rare in
preferring that.

> @@ -2344,10 +2276,7 @@ setup_pgdata(void)
>      * have embedded spaces.
>      */
>     if (setenv("PGDATA", pg_data, 1) != 0)
> -    {
> -        pg_log_error("could not set environment");
> -        exit(1);
> -    }
> +        pg_fatal("could not set environment");

Should we be explicit about which env var?

> @@ -3089,18 +2979,14 @@ main(int argc, char *argv[])
>                 else if (strcmp(optarg, "libc") == 0)
>                     locale_provider = COLLPROVIDER_LIBC;
>                 else
> -                {
> -                    pg_log_error("unrecognized locale provider: %s", optarg);
> -                    exit(1);
> -                }
> +                    pg_fatal("unrecognized locale provider: %s", optarg);

Should this %s be within quotes to match how we usually emit user-input?

> @@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
>             pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows:
%d",
>                            rel->datinfo->datname, rel->nspname, rel->relname, ntups);
>             if (opts.verbose)
> -                pg_log_info("query was: %s", rel->sql);
> -            pg_log_warning("Are %s's and amcheck's versions compatible?",
> -                           progname);
> +                pg_log_warning_detail("Query was: %s", rel->sql);
> +            pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
> +                                progname);

Should "amcheck's" be a %s parameter to make translation reusable (which it
miht never be) and possibly avoid translation mistake?

> --- a/src/bin/pg_basebackup/receivelog.c
> +++ b/src/bin/pg_basebackup/receivelog.c
> @@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
>             /* fsync file in case of a previous crash */
>             if (stream->walmethod->sync(f) != 0)
>             {
> -                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
> +                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
>                              fn, stream->walmethod->getlasterror());
>                 stream->walmethod->close(f, CLOSE_UNLINK);
>                 exit(1);

In the case where we already have IO related errors, couldn't the close() call
cause an additional error message which potentially could be helpful for
debugging?

> @@ -597,31 +570,19 @@ main(int argc, char *argv[])

>     if (ControlFile->data_checksum_version == 0 &&
>         mode == PG_MODE_CHECK)
> -    {
> -        pg_log_error("data checksums are not enabled in cluster");
> -        exit(1);
> -    }
> +        pg_fatal("data checksums are not enabled in cluster");
>
>     if (ControlFile->data_checksum_version == 0 &&
>         mode == PG_MODE_DISABLE)
> -    {
> -        pg_log_error("data checksums are already disabled in cluster");
> -        exit(1);
> -    }
> +        pg_fatal("data checksums are already disabled in cluster");
>
>     if (ControlFile->data_checksum_version > 0 &&
>         mode == PG_MODE_ENABLE)
> -    {
> -        pg_log_error("data checksums are already enabled in cluster");
> -        exit(1);
> -    }
> +        pg_fatal("data checksums are already enabled in cluster");

Fatal seems sort of out place here, it's not really a case of "something
terrible happened" but rather "the preconditions weren't met".  Couldn't these
be a single pg_log_error erroring out with the reason in a pg_log_detail?

> @@ -1323,10 +1320,10 @@ lockTableForWorker(ArchiveHandle *AH, TocEntry *te)
>     res = PQexec(AH->connection, query->data);
>
>     if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
> -        fatal("could not obtain lock on relation \"%s\"\n"
> -              "This usually means that someone requested an ACCESS EXCLUSIVE lock "
> -              "on the table after the pg_dump parent process had gotten the "
> -              "initial ACCESS SHARE lock on the table.", qualId);
> +        pg_fatal("could not obtain lock on relation \"%s\"\n"
> +                 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
> +                 "on the table after the pg_dump parent process had gotten the "
> +                 "initial ACCESS SHARE lock on the table.", qualId);

Punctuation and formatting in a pg_fatal feels a bit wrong, and smells of need
for _detail/_hint.  I guess there isn't a way to do that here though, but still
a shame.

> @@ -50,9 +50,10 @@ _check_database_version(ArchiveHandle *AH)
>         && (remoteversion < AH->public.minRemoteVersion ||
>             remoteversion > AH->public.maxRemoteVersion))
>     {
> -        pg_log_error("server version: %s; %s version: %s",
> -                     remoteversion_str, progname, PG_VERSION);
> -        fatal("aborting because of server version mismatch");
> +        pg_log_error("aborting because of server version mismatch");
> +        pg_log_error_detail("server version: %s; %s version: %s",
> +                            remoteversion_str, progname, PG_VERSION);
> +        exit(1);

Capitalization of the _detail message?

> @@ -166,7 +167,7 @@ ConnectDatabase(Archive *AHX,
>         AH->connection = PQconnectdbParams(keywords, values, true);
>
>         if (!AH->connection)
> -            fatal("could not connect to database");
> +            pg_fatal("could not connect to database");

Shouldn't this print which database it failed to connect to?


> @@ -721,7 +721,7 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
>     dname = ctx->directory;
>
>     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)

Unrelated, but shouldn't that be >= MAXPGPATH?

> @@ -2033,8 +2027,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
>     {
>         /* copy data transfer failed */
>         pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
> -        pg_log_error("Error message from server: %s", PQerrorMessage(conn));
> -        pg_log_error("The command was: %s", q->data);
> +        pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
> +        pg_log_error_detail("Command was: %s", q->data);

Shouldn't the pg_log_error() call be with leading lowercase and no punctuation?
(same in the following hunk)


> @@ -11756,8 +11747,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
>         else if (provolatile[0] == PROVOLATILE_STABLE)
>             appendPQExpBufferStr(q, " STABLE");
>         else if (provolatile[0] != PROVOLATILE_VOLATILE)
> -            fatal("unrecognized provolatile value for function \"%s\"",
> -                  finfo->dobj.name);
> +            pg_fatal("unrecognized provolatile value for function \"%s\"",
> +                     finfo->dobj.name);
>     }
>
>     if (proisstrict[0] == 't')
> @@ -11806,8 +11797,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
>         else if (proparallel[0] == PROPARALLEL_RESTRICTED)
>             appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
>         else if (proparallel[0] != PROPARALLEL_UNSAFE)
> -            fatal("unrecognized proparallel value for function \"%s\"",
> -                  finfo->dobj.name);
> +            pg_fatal("unrecognized proparallel value for function \"%s\"",
> +                     finfo->dobj.name);

We should probably move the provolatile and propallel keywords to a %s param so
we can reuse the translation.

> @@ -14951,18 +14942,18 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)

> -        fatal("definition of view \"%s\" appears to be empty (length zero)",
> -              tbinfo->dobj.name);
> +        pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
> +                 tbinfo->dobj.name);

I'm not sure we need to provide a definition of empty here, most readers will
probably understand that already =)

> @@ -16602,13 +16593,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
>     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
>
>     if (PQntuples(res) != 1)
> -    {
> -        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
> -                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
> -                              PQntuples(res)),
> -                     tbinfo->dobj.name, PQntuples(res));
> -        exit_nicely(1);
> -    }
> +        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
> +                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
> +                          PQntuples(res)),
> +                 tbinfo->dobj.name, PQntuples(res));

The ngettext() call seems a bit out of place here since we already know that
second form will be taken given the check on PQntuples(res).

> @@ -16824,13 +16812,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
>     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
>
>     if (PQntuples(res) != 1)
> -    {
> -        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
> -                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
> -                              PQntuples(res)),
> -                     tbinfo->dobj.name, PQntuples(res));
> -        exit_nicely(1);
> -    }
> +        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
> +                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
> +                          PQntuples(res)),
> +                 tbinfo->dobj.name, PQntuples(res));

Same as above, PQntuples(res) cannot be 1 so there os just one form used no?

> --- a/src/bin/pg_dump/pg_dumpall.c
> +++ b/src/bin/pg_dump/pg_dumpall.c
> @@ -200,16 +200,15 @@ main(int argc, char *argv[])
>             strlcpy(full_path, progname, sizeof(full_path));
>
>         if (ret == -1)
> -            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
> -                         "same directory as \"%s\".\n"
> -                         "Check your installation.",
> -                         "pg_dump", progname, full_path);
> +            pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
> +                     "same directory as \"%s\".\n"
> +                     "Check your installation.",
> +                     "pg_dump", progname, full_path);
>         else
> -            pg_log_error("The program \"%s\" was found by \"%s\"\n"
> -                         "but was not the same version as %s.\n"
> -                         "Check your installation.",
> -                         "pg_dump", full_path, progname);
> -        exit_nicely(1);
> +            pg_fatal("The program \"%s\" was found by \"%s\"\n"
> +                     "but was not the same version as %s.\n"
> +                     "Check your installation.",

"Check your installation" seems superfluous here given the error.  (we do this
in a number of places)


> @@ -1611,9 +1572,9 @@ connectDatabase(const char *dbname, const char *connection_string,
>         && (server_version < 90200 ||
>             (server_version / 100) > (my_version / 100)))
>     {
> -        pg_log_error("server version: %s; %s version: %s",
> -                     remoteversion_str, progname, PG_VERSION);
>         pg_log_error("aborting because of server version mismatch");
> +        pg_log_error_detail("server version: %s; %s version: %s",
> +                            remoteversion_str, progname, PG_VERSION);

Capitalization?

> @@ -388,13 +387,13 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
>         if (errinfo.wre_errno != 0)
>         {
>             errno = errinfo.wre_errno;
> -            fatal_error("could not read from file %s, offset %d: %m",
> -                        fname, errinfo.wre_off);
> +            pg_fatal("could not read from file %s, offset %d: %m",
> +                     fname, errinfo.wre_off);
>         }
>         else
> -            fatal_error("could not read from file %s, offset %d: read %d of %d",
> -                        fname, errinfo.wre_off, errinfo.wre_read,
> -                        errinfo.wre_req);
> +            pg_fatal("could not read from file %s, offset %d: read %d of %d",
> +                     fname, errinfo.wre_off, errinfo.wre_read,
> +                     errinfo.wre_req);

Filename %s should be quoted like \"%s\".

> @@ -7379,7 +7254,7 @@ main(int argc, char **argv)
>     THREAD_BARRIER_DESTROY(&barrier);
>
>     if (exit_code != 0)
> -        pg_log_fatal("Run was aborted; the above results are incomplete.");
> +        pg_log_error("Run was aborted; the above results are incomplete.");

Capitalization and punctuation?

> @@ -781,10 +772,7 @@ process_psqlrc(char *argv0)
>     char       *envrc = getenv("PSQLRC");
>
>     if (find_my_exec(argv0, my_exec_path) < 0)
> -    {
> -        pg_log_fatal("could not find own program executable");
> -        exit(EXIT_FAILURE);
> -    }
> +        pg_fatal("could not find own program executable");

I have a feeling this will be confusing to a fair few users.  Not sure how to
improve it, but there seems to be some room for it.

> @@ -144,16 +145,10 @@ main(int argc, char *argv[])
>     if (alldb)
>     {
>         if (dbname)
> -        {
> -            pg_log_error("cannot cluster all databases and a specific one at the same time");
> -            exit(1);
> -        }
> +            pg_fatal("cannot cluster all databases and a specific one at the same time");
>
>         if (tables.head != NULL)
> -        {
> -            pg_log_error("cannot cluster specific table(s) in all databases");
> -            exit(1);
> -        }
> +            pg_fatal("cannot cluster specific table(s) in all databases");

An ngettext() candidate perhaps? There are more like this in main() hunks further down omitted for brevity here.

> diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
> index a30c66f13a..f2e583f9fa 100644
> --- a/src/fe_utils/connect_utils.c
> +++ b/src/fe_utils/connect_utils.c
> @@ -88,11 +88,8 @@ connectDatabase(const ConnParams *cparams, const char *progname,
>         conn = PQconnectdbParams(keywords, values, true);
>
>         if (!conn)
> -        {
> -            pg_log_error("could not connect to database %s: out of memory",
> -                         cparams->dbname);
> -            exit(1);
> -        }
> +            pg_fatal("could not connect to database %s: out of memory",
> +                     cparams->dbname);

Database name quoted like \"%s\" to match other messages?

--
Daniel Gustafsson        https://vmware.com/




Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 27.03.22 22:19, Tom Lane wrote:
> Here's a rebase up to today's HEAD.  I've fixed the merge problems,
> but there may be some stray new error calls that could be converted
> to use pg_fatal() and haven't been.  I don't want to do a full
> fresh scan of the code until we're about ready to commit this.

This looks like a good improvement to me.

I think I would want the program name/location also in front of the 
detail and hint lines.  I need to think about this a bit more.  This 
shouldn't hold up this patch; it would be a quick localized change. 
(I'm also thinking about providing a separate color code for the 
secondary messages.  Again, this could be a quick follow-up patch.)

The one change I didn't like was

-                       pg_log_error("The program \"%s\" is needed by %s 
but was not found in the\n"
-                                                "same directory as 
\"%s\".\n"
-                                                "Check your installation.",
+                       pg_log_error("the program \"%s\" is needed by %s 
but was not found in the same directory as \"%s\"",
                                                  "postgres", progname, 
full_path);

This appears to prioritize the guideline "don't punctuate error message 
as full sentence" over what should be the actual guideline "don't make 
the error message a full sentence".

There are other occurrences of a similar message that were not changed 
in the same way by the patch.  Maybe we should leave this one alone in 
this patch and consider rewriting the message instead.



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 29.03.22 12:13, Daniel Gustafsson wrote:

Most of these should probably be addressed separately from Tom's patch.

>> @@ -508,24 +502,15 @@ writefile(char *path, char **lines)
> 
>>     if (fclose(out_file))
>> -    {
>> -        pg_log_error("could not write file \"%s\": %m", path);
>> -        exit(1);
>> -    }
>> +        pg_fatal("could not write file \"%s\": %m", path);
>> }
> 
> Should we update this message to differentiate it from the fwrite error case?
> Something like "an error occurred during writing.."

Should be "could not close ...", no?

>> @@ -2057,10 +2004,7 @@ check_locale_name(int category, const char *locale, char **canonname)
>>
>>     save = setlocale(category, NULL);
>>     if (!save)
>> -    {
>> -        pg_log_error("setlocale() failed");
>> -        exit(1);
>> -    }
>> +        pg_fatal("setlocale() failed");
> 
> Should this gain a hint message for those users who have no idea what this
> really means?

My setlocale() man page says:

ERRORS
      No errors are defined.

So uh ... ;-)

>> @@ -3089,18 +2979,14 @@ main(int argc, char *argv[])
>>                 else if (strcmp(optarg, "libc") == 0)
>>                     locale_provider = COLLPROVIDER_LIBC;
>>                 else
>> -                {
>> -                    pg_log_error("unrecognized locale provider: %s", optarg);
>> -                    exit(1);
>> -                }
>> +                    pg_fatal("unrecognized locale provider: %s", optarg);
> 
> Should this %s be within quotes to match how we usually emit user-input?

Usually not done after colon, but could be.

>> @@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
>>             pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows:
%d",
>>                            rel->datinfo->datname, rel->nspname, rel->relname, ntups);
>>             if (opts.verbose)
>> -                pg_log_info("query was: %s", rel->sql);
>> -            pg_log_warning("Are %s's and amcheck's versions compatible?",
>> -                           progname);
>> +                pg_log_warning_detail("Query was: %s", rel->sql);
>> +            pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
>> +                                progname);
> 
> Should "amcheck's" be a %s parameter to make translation reusable (which it
> miht never be) and possibly avoid translation mistake?

We don't have translations set up for contrib modules.  Otherwise, this 
kind of thing would probably be something to look into.

>> --- a/src/bin/pg_basebackup/receivelog.c
>> +++ b/src/bin/pg_basebackup/receivelog.c
>> @@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
>>             /* fsync file in case of a previous crash */
>>             if (stream->walmethod->sync(f) != 0)
>>             {
>> -                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
>> +                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
>>                              fn, stream->walmethod->getlasterror());
>>                 stream->walmethod->close(f, CLOSE_UNLINK);
>>                 exit(1);
> 
> In the case where we already have IO related errors, couldn't the close() call
> cause an additional error message which potentially could be helpful for
> debugging?

Yeah, I think in general we have been sloppy with reporting file-closing 
errors properly.  Those presumably happen very rarely, but when they do, 
things are probably very bad.

>> @@ -597,31 +570,19 @@ main(int argc, char *argv[])
> 
>>     if (ControlFile->data_checksum_version == 0 &&
>>         mode == PG_MODE_CHECK)
>> -    {
>> -        pg_log_error("data checksums are not enabled in cluster");
>> -        exit(1);
>> -    }
>> +        pg_fatal("data checksums are not enabled in cluster");

> Fatal seems sort of out place here, it's not really a case of "something
> terrible happened" but rather "the preconditions weren't met".  Couldn't these
> be a single pg_log_error erroring out with the reason in a pg_log_detail?

"fatal" means error plus exit, so this seems ok.  There is no separate 
log level for really-bad-error.

>> @@ -721,7 +721,7 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
>>     dname = ctx->directory;
>>
>>     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
> 
> Unrelated, but shouldn't that be >= MAXPGPATH?

Seems correct to me as is.

>> @@ -14951,18 +14942,18 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
> 
>> -        fatal("definition of view \"%s\" appears to be empty (length zero)",
>> -              tbinfo->dobj.name);
>> +        pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
>> +                 tbinfo->dobj.name);
> 
> I'm not sure we need to provide a definition of empty here, most readers will
> probably understand that already =)

It could mean, contains no columns, or something similar.  If I had to 
change it, I would remove the "empty" and keep the "length zero".

>> @@ -16602,13 +16593,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
>>     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
>>
>>     if (PQntuples(res) != 1)
>> -    {
>> -        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
>> -                              "query to get data of sequence \"%s\" returned %d rows (expected 1)",
>> -                              PQntuples(res)),
>> -                     tbinfo->dobj.name, PQntuples(res));
>> -        exit_nicely(1);
>> -    }
>> +        pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
>> +                          "query to get data of sequence \"%s\" returned %d rows (expected 1)",
>> +                          PQntuples(res)),
>> +                 tbinfo->dobj.name, PQntuples(res));
> 
> The ngettext() call seems a bit out of place here since we already know that
> second form will be taken given the check on PQntuples(res).

See 
<https://www.postgresql.org/message-id/flat/CALj2ACUfJKTmK5v%3DvF%2BH2iLkqM9Yvjsp6iXaCqAks6gDpzZh6g%40mail.gmail.com> 
for a similar case that explains why this is still correct and necessary.

>> @@ -144,16 +145,10 @@ main(int argc, char *argv[])
>>     if (alldb)
>>     {
>>         if (dbname)
>> -        {
>> -            pg_log_error("cannot cluster all databases and a specific one at the same time");
>> -            exit(1);
>> -        }
>> +            pg_fatal("cannot cluster all databases and a specific one at the same time");
>>
>>         if (tables.head != NULL)
>> -        {
>> -            pg_log_error("cannot cluster specific table(s) in all databases");
>> -            exit(1);
>> -        }
>> +            pg_fatal("cannot cluster specific table(s) in all databases");
> 
> An ngettext() candidate perhaps? There are more like this in main() hunks further down omitted for brevity here.

I would just rephrase this as

     "cannot cluster specific tables in all databases"

This is still correct and sensible if the user specified just one table.



Re: Frontend error logging style

От
Daniel Gustafsson
Дата:
> On 29 Mar 2022, at 16:38, Peter Eisentraut <peter.eisentraut@enterprisedb.com> wrote:
>
> On 29.03.22 12:13, Daniel Gustafsson wrote:
>
> Most of these should probably be addressed separately from Tom's patch.

Yeah, I think so too.  I'll await input from Tom on how he wants to do, but I'm
happy to take these to a new thread.  The main point of the review was to find
logic errors in the logging changes, these came as a bonus from reading them
all in one place.

>>> @@ -508,24 +502,15 @@ writefile(char *path, char **lines)
>>>     if (fclose(out_file))
>>> -    {
>>> -        pg_log_error("could not write file \"%s\": %m", path);
>>> -        exit(1);
>>> -    }
>>> +        pg_fatal("could not write file \"%s\": %m", path);
>>> }
>> Should we update this message to differentiate it from the fwrite error case?
>> Something like "an error occurred during writing.."
>
> Should be "could not close ...", no?

Yes, it should, not sure what I was thinking.

>>> @@ -2057,10 +2004,7 @@ check_locale_name(int category, const char *locale, char **canonname)
>>>
>>>     save = setlocale(category, NULL);
>>>     if (!save)
>>> -    {
>>> -        pg_log_error("setlocale() failed");
>>> -        exit(1);
>>> -    }
>>> +        pg_fatal("setlocale() failed");
>> Should this gain a hint message for those users who have no idea what this
>> really means?
>
> My setlocale() man page says:
>
> ERRORS
>     No errors are defined.
>
> So uh ... ;-)

Even more reason to be confused then =) We have a mixed bag in the tree on how
we handle errors from setlocale, in this case we could reword it to say
something like "failed to retrieve locale for %s, category" which IMO would be
slightly more informative. Might be academic though since I guess it rarely, if
ever, happens.

>>> --- a/src/bin/pg_basebackup/receivelog.c
>>> +++ b/src/bin/pg_basebackup/receivelog.c
>>> @@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
>>>             /* fsync file in case of a previous crash */
>>>             if (stream->walmethod->sync(f) != 0)
>>>             {
>>> -                pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
>>> +                pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
>>>                              fn, stream->walmethod->getlasterror());
>>>                 stream->walmethod->close(f, CLOSE_UNLINK);
>>>                 exit(1);
>> In the case where we already have IO related errors, couldn't the close() call
>> cause an additional error message which potentially could be helpful for
>> debugging?
>
> Yeah, I think in general we have been sloppy with reporting file-closing errors properly.  Those presumably happen
veryrarely, but when they do, things are probably very bad. 

Agreed.  I'm not sure if Tom wants to add net new loggings in this patchset,
else we could do this separately.

>> The ngettext() call seems a bit out of place here since we already know that
>> second form will be taken given the check on PQntuples(res).
>
> See
<https://www.postgresql.org/message-id/flat/CALj2ACUfJKTmK5v%3DvF%2BH2iLkqM9Yvjsp6iXaCqAks6gDpzZh6g%40mail.gmail.com>
fora similar case that explains why this is still correct and necessary. 

Doh, I knew that, sorry.

>>>     }
>>> +            pg_fatal("cannot cluster specific table(s) in all databases");
>> An ngettext() candidate perhaps? There are more like this in main() hunks further down omitted for brevity here.
>
> I would just rephrase this as
>
>    "cannot cluster specific tables in all databases"
>
> This is still correct and sensible if the user specified just one table.

That's one way yes.  Mostly I'm just a bit allergic to the "foo(s)" which my
unscientifically based gut feeling am concerned about not necessarily always
working well for translations.

--
Daniel Gustafsson        https://vmware.com/




Re: Frontend error logging style

От
Tom Lane
Дата:
Daniel Gustafsson <daniel@yesql.se> writes:
> On 29 Mar 2022, at 16:38, Peter Eisentraut <peter.eisentraut@enterprisedb.com> wrote:
>> Most of these should probably be addressed separately from Tom's patch.

> Yeah, I think so too.

Agreed.  I tried to confine my patch to mechanical changes, except
for changing places where the detail/hint features could be used.
(I don't say that I yielded to temptation nowhere, because I don't
recall that for certain; but editing the message texts was not the
point of my patch.)

Feel free to work on a followup editing patch though.

Another thing that occurred to me as I looked at your list is that
I think a lot of these are nearly-can't-happen cases that we didn't
put a lot of effort into devising great error messages for.  Maybe
we should continue that approach, and in particular not put effort
into translating such messages.  That would suggest inventing
"pg_fatal_internal" and so forth, with the same functionality
except for not being gettext triggers, comparable to
errmsg_internal.  However, then somebody would have to make
judgment calls about which messages not to bother translating.
Do we want to go down that path?

            regards, tom lane



Re: Frontend error logging style

От
Greg Stark
Дата:
FYI this patch no longer applies.

Given it's a Tom patch and due to its nature it'll bitrot rapidly
anyways I'm don't see a point in updating the status though.



Re: Frontend error logging style

От
Tom Lane
Дата:
Greg Stark <stark@mit.edu> writes:
> FYI this patch no longer applies.

No surprise :-(

> Given it's a Tom patch and due to its nature it'll bitrot rapidly
> anyways I'm don't see a point in updating the status though.

We now seem to have buy-in on the concept, so my plan is to let
this sit till the end of the commitfest, then rebase and push.
That should avoid unnecessary churn for other pending patches.

            regards, tom lane



Re: Frontend error logging style

От
Daniel Gustafsson
Дата:
> On 30 Mar 2022, at 00:38, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>
> Daniel Gustafsson <daniel@yesql.se> writes:
>> On 29 Mar 2022, at 16:38, Peter Eisentraut <peter.eisentraut@enterprisedb.com> wrote:
>>> Most of these should probably be addressed separately from Tom's patch.
>
>> Yeah, I think so too.
>
> Agreed.  I tried to confine my patch to mechanical changes, except
> for changing places where the detail/hint features could be used.
> (I don't say that I yielded to temptation nowhere, because I don't
> recall that for certain; but editing the message texts was not the
> point of my patch.)
>
> Feel free to work on a followup editing patch though.

Thats my plan, once this lands I'll rebase the comments on top of your work and
we can have a separate discussion around them then.

> Another thing that occurred to me as I looked at your list is that
> I think a lot of these are nearly-can't-happen cases that we didn't
> put a lot of effort into devising great error messages for.  Maybe
> we should continue that approach, and in particular not put effort
> into translating such messages.  That would suggest inventing
> "pg_fatal_internal" and so forth, with the same functionality
> except for not being gettext triggers, comparable to
> errmsg_internal.  However, then somebody would have to make
> judgment calls about which messages not to bother translating.
> Do we want to go down that path?

I'm not sure.  If, against the odds, these cases do happen then a user of a
localized build probably really want to see the error in language they can
easily understand and take in.

--
Daniel Gustafsson        https://vmware.com/




Re: Frontend error logging style

От
Tom Lane
Дата:
Daniel Gustafsson <daniel@yesql.se> writes:
> On 30 Mar 2022, at 00:38, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Feel free to work on a followup editing patch though.

> Thats my plan, once this lands I'll rebase the comments on top of your work and
> we can have a separate discussion around them then.

The main patch is pushed now.  I addressed the complaint Peter had
about the messages with "Check your installation" pseudo-hints
by getting rid of them; I concur with your observation that those
hints were basically useless.  I also fixed the one place where the
message should clearly be "could not close" not "could not write".
Mostly didn't yield to temptation anywhere else.

One other loose end is bothering me: I stuck with logging.h's
original choice to put "if (likely())" or "if (unlikely())"
conditionals into the macros, but I rather suspect that that's
just a waste.  I think we should put a centralized level check
into logging.c, and get rid of at least the "if (likely())"
checks, because those are going to succeed approximately 100.0%
of the time.  Maybe there's an argument for keeping the unlikely()
ones.

            regards, tom lane



Re: Frontend error logging style

От
Tom Lane
Дата:
I wrote:
> One other loose end is bothering me: I stuck with logging.h's
> original choice to put "if (likely())" or "if (unlikely())"
> conditionals into the macros, but I rather suspect that that's
> just a waste.  I think we should put a centralized level check
> into logging.c, and get rid of at least the "if (likely())"
> checks, because those are going to succeed approximately 100.0%
> of the time.  Maybe there's an argument for keeping the unlikely()
> ones.

Concretely, something like the attached.  As a simple check,
I looked at the compiled size of pg_dump.  It went from

  text    data     bss     dec     hex filename
 380298    4008    1384  385690   5e29a /home/postgres/testversion/bin/pg_dump

to

   text    data     bss     dec     hex filename
 374954    4008    1384  380346   5cdba src/bin/pg_dump/pg_dump

for a savings of about 5K or 1.5%.  Not a huge amount, but
not nothing either, especially considering that the existing
coding isn't buying us anything.

            regards, tom lane

diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index 5b1c51554d..8173bb93cf 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -34,8 +34,7 @@ extern void exit_nicely(int code) pg_attribute_noreturn();
 /* In pg_dump, we modify pg_fatal to call exit_nicely instead of exit */
 #undef pg_fatal
 #define pg_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
-            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
         exit_nicely(1); \
     } while(0)

diff --git a/src/common/logging.c b/src/common/logging.c
index 18d6669f27..8a061f46b4 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -223,6 +223,10 @@ pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
     Assert(fmt);
     Assert(fmt[strlen(fmt) - 1] != '\n');

+    /* Do nothing if log level is too low. */
+    if (level < __pg_log_level)
+        return;
+
     /*
      * Flush stdout before output to stderr, to ensure sync even when stdout
      * is buffered.
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index e213bb70d0..35c7c7b976 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -104,48 +104,39 @@ void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
  * pg_log_generic[_v] directly, except perhaps in error interface code.
  */
 #define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
-            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

 #define pg_log_error_detail(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
-            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
     } while(0)

 #define pg_log_error_hint(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
-            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
-            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning_detail(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
-            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning_hint(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
-            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) \
-            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

 #define pg_log_info_detail(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) \
-            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
     } while(0)

 #define pg_log_info_hint(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) \
-            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
@@ -167,8 +158,7 @@ void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
  * A common shortcut: pg_log_error() and immediately exit(1).
  */
 #define pg_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
-            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
         exit(1); \
     } while(0)


Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 08.04.22 22:26, Tom Lane wrote:
> I wrote:
>> One other loose end is bothering me: I stuck with logging.h's
>> original choice to put "if (likely())" or "if (unlikely())"
>> conditionals into the macros, but I rather suspect that that's
>> just a waste.  I think we should put a centralized level check
>> into logging.c, and get rid of at least the "if (likely())"
>> checks, because those are going to succeed approximately 100.0%
>> of the time.  Maybe there's an argument for keeping the unlikely()
>> ones.
> 
> Concretely, something like the attached.  As a simple check,
> I looked at the compiled size of pg_dump.  It went from
> 
>    text    data     bss     dec     hex filename
>   380298    4008    1384  385690   5e29a /home/postgres/testversion/bin/pg_dump
> 
> to
> 
>     text    data     bss     dec     hex filename
>   374954    4008    1384  380346   5cdba src/bin/pg_dump/pg_dump
> 
> for a savings of about 5K or 1.5%.  Not a huge amount, but
> not nothing either, especially considering that the existing
> coding isn't buying us anything.

Yeah, that seems ok to change.  The previous coding style is more useful 
if you have a lot of debug messages in a hot code path, but that usually 
doesn't apply to where this is used.



Re: Frontend error logging style

От
Tom Lane
Дата:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
> On 08.04.22 22:26, Tom Lane wrote:
>>> I think we should put a centralized level check
>>> into logging.c, and get rid of at least the "if (likely())"
>>> checks, because those are going to succeed approximately 100.0%
>>> of the time.  Maybe there's an argument for keeping the unlikely()
>>> ones.

> Yeah, that seems ok to change.  The previous coding style is more useful 
> if you have a lot of debug messages in a hot code path, but that usually 
> doesn't apply to where this is used.

The patch I presented keeps the unlikely() checks in the debug-level
macros.  Do you think we should drop those too?  I figured that avoiding
evaluating the arguments would be worth something.

            regards, tom lane



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 11.04.22 17:22, Tom Lane wrote:
> Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
>> On 08.04.22 22:26, Tom Lane wrote:
>>>> I think we should put a centralized level check
>>>> into logging.c, and get rid of at least the "if (likely())"
>>>> checks, because those are going to succeed approximately 100.0%
>>>> of the time.  Maybe there's an argument for keeping the unlikely()
>>>> ones.
> 
>> Yeah, that seems ok to change.  The previous coding style is more useful
>> if you have a lot of debug messages in a hot code path, but that usually
>> doesn't apply to where this is used.
> 
> The patch I presented keeps the unlikely() checks in the debug-level
> macros.  Do you think we should drop those too?  I figured that avoiding
> evaluating the arguments would be worth something.

Oh, that's right, the whole thing is to not evaluate the arguments if 
the log level isn't adequate.  We should probably keep that.

Is the code size a big problem?  ereport() has a bunch of extra code 
around each call as well.  Does it have similar problems?



Re: Frontend error logging style

От
Tom Lane
Дата:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
> On 11.04.22 17:22, Tom Lane wrote:
>> The patch I presented keeps the unlikely() checks in the debug-level
>> macros.  Do you think we should drop those too?  I figured that avoiding
>> evaluating the arguments would be worth something.

> Oh, that's right, the whole thing is to not evaluate the arguments if 
> the log level isn't adequate.  We should probably keep that.

I don't think that's nearly as interesting in the frontend as in
the backend.  Error messages in the backend frequently include
catalog lookups and the like in the arguments, but I think nearly
all frontend messages are writing nothing more complicated than
variables and maybe some indirections or array fetches.  So I'm
not all that worried about runtime, and would rather save some
code space.

            regards, tom lane



Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 29.03.22 16:24, Peter Eisentraut wrote:
> I think I would want the program name/location also in front of the 
> detail and hint lines.  I need to think about this a bit more.  This 
> shouldn't hold up this patch; it would be a quick localized change.

After experiencing this for a bit now, I propose to make this change.
It lines up better and allows easier filtering of messages by program.

Example:

Before:

initdb: error: locale "zh_HK.Big5HKSCS" requires unsupported encoding "BIG5"
detail: Encoding "BIG5" is not allowed as a server-side encoding.
hint: Rerun initdb with a different locale selection.

After:

initdb: error: locale "zh_HK.Big5HKSCS" requires unsupported encoding "BIG5"
initdb: detail: Encoding "BIG5" is not allowed as a server-side encoding.
initdb: hint: Rerun initdb with a different locale selection.
Вложения

Re: Frontend error logging style

От
Peter Eisentraut
Дата:
On 23.05.22 11:53, Peter Eisentraut wrote:
> On 29.03.22 16:24, Peter Eisentraut wrote:
>> I think I would want the program name/location also in front of the 
>> detail and hint lines.  I need to think about this a bit more.  This 
>> shouldn't hold up this patch; it would be a quick localized change.
> 
> After experiencing this for a bit now, I propose to make this change.
> It lines up better and allows easier filtering of messages by program.

This has been committed.



Re: Frontend error logging style

От
Dmitry Koval
Дата:
Hi!

Commit 9a374b77fb53 (Improve frontend error logging style.) replaced a 
line of file src/include/common/logging.h
```
extern PGDLLIMPORT enum pg_log_level __pg_log_level;
```
to
```
extern enum pg_log_level __pg_log_level;
```
i.e. it is rollback of commit 8ec569479fc28 (Apply PGDLLIMPORT markings 
broadly.)

Is it correct?

-- 
With best regards,
Dmitry Koval

Postgres Professional: http://postgrespro.com



Re: Frontend error logging style

От
Tom Lane
Дата:
Dmitry Koval <d.koval@postgrespro.ru> writes:
> Hi!
> Commit 9a374b77fb53 (Improve frontend error logging style.) replaced a 
> line of file src/include/common/logging.h
> ```
> extern PGDLLIMPORT enum pg_log_level __pg_log_level;
> ```
> to
> ```
> extern enum pg_log_level __pg_log_level;
> ```
> i.e. it is rollback of commit 8ec569479fc28 (Apply PGDLLIMPORT markings 
> broadly.)

> Is it correct?

Yes, since that file is frontend-only.

            regards, tom lane