Introduce "log_connection_stages" setting.

Started by Sergey Dudoladovover 3 years ago24 messages
#1Sergey Dudoladov
sergey.dudoladov@gmail.com
1 attachment(s)

Hello,

The problem we face is excessive logging of connection information
that clutters the logs and in corner cases with many short-lived
connections leads to disk space exhaustion.

Current connection log lines share significant parts of the
information - host, port, very close timestamps etc. - in the common
case of a successful connection:

2022-07-12 12:17:39.369
UTC,,,12875,"10.2.101.63:35616",62cd6663.324b,1,"",2022-07-12 12:17:39
UTC,,0,LOG,00000,"connection received: host=10.2.101.63
port=35616",,,,,,,,,"","not initialized"
2022-07-12 12:17:39.374
UTC,"some_user","postgres",12875,"10.2.101.63:35616",62cd6663.324b,2,"authentication",2022-07-12
12:17:39 UTC,18/4571,0,LOG,00000,"connection authorized:
user=some_user database=postgres SSL enabled (protocol=, cipher=,
compression=)",,,,,,,,,"","client backend"
2022-07-12 12:17:39.430
UTC,"some_user","postgres",12875,"10.2.101.63:35616",62cd6663.324b,3,"idle",2022-07-12
12:17:39 UTC,,0,LOG,00000,"disconnection: session time: 0:00:00.060
user=some_user database=postgres host=10.2.101.63
port=35616",,,,,,,,,"","client backend"

Removing some of the lines should not harm log-based investigations in
most cases, but will shrink the logs improving readability and space
consumption.

I would like to get feedback on the following idea:

Add the `log_connection_stages` setting of type “string” with possible
values "received", "authorized", "authenticated", "disconnected", and
"all", with "all" being the default.
The setting would have effect only when `log_connections` is on.
Example: log_connection_stages=’authorized,disconnected’.
That also implies there would be no need for a separate
"log_disconnection" setting.

For the sake of completeness I have to mention omitting ‘received’
from `log_connection_stages` would lead to absence in logs info about
connections that do not complete initialization: for them only the
“connection received” line is currently logged. The attachment
contains a log excerpt to clarify the situation I am talking about.

Regards,
Sergey.

Attachments:

debug5_log_not_initialized_process_exit.txttext/plain; charset=US-ASCII; name=debug5_log_not_initialized_process_exit.txtDownload
#2Euler Taveira
euler@eulerto.com
In reply to: Sergey Dudoladov (#1)
Re: Introduce "log_connection_stages" setting.

On Tue, Jul 12, 2022, at 10:52 AM, Sergey Dudoladov wrote:

The problem we face is excessive logging of connection information
that clutters the logs and in corner cases with many short-lived
connections leads to disk space exhaustion.

You are proposing a fine-grained control over connection stages reported when
log_connections = on. It seems useful if you're only interested in (a)
malicious access or (b) authorized access (for audit purposes).

I would like to get feedback on the following idea:

Add the `log_connection_stages` setting of type “string” with possible
values "received", "authorized", "authenticated", "disconnected", and
"all", with "all" being the default.
The setting would have effect only when `log_connections` is on.
Example: log_connection_stages=’authorized,disconnected’.
That also implies there would be no need for a separate
"log_disconnection" setting.

Your proposal will add more confusion to the already-confused logging-related
GUCs. If you are proposing to introduce a fine-grained control, the first step
should be merge log_connections and log_disconnections into a new GUC (?) and
deprecate them. (I wouldn't introduce a new GUC that depends on the stage of
other GUC as you proposed.) There are 3 stages: connect (received), authorized
(authenticated), disconnect. You can also add 'all' that mimics log_connections
/ log_disconnections enabled. Another question is if we can reuse
log_connections for this improvement instead of a new GUC. In this case, you
would turn the boolean value into an enum value. Will it cause trouble while
upgrading to this new version? It is one of the reasons to create a new GUC. I
would suggest log_connection_messages or log_connection (with the 's' in the
end -- since it is too similar to the current GUC name, I'm afraid it is not a
good name for it).

--
Euler Taveira
EDB https://www.enterprisedb.com/

#3Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Euler Taveira (#2)
Re: Introduce "log_connection_stages" setting.

Hello,

Thank you for the constructive feedback.

Your proposal will add more confusion to the already-confused logging-related GUCs.
I wouldn't introduce a new GUC that depends on the stage of other GUC as you proposed.

Agreed, coupling a new GUC with "log_connections" is likely to lead to
extra confusion.

There are 3 stages: connect (received), authorized (authenticated), disconnect.

I've taken connection stages and terminology from the existing log messages.
The reason I have separated "authorized" and "authenticated" are [1]https://github.com/postgres/postgres/blob/3f8148c256e067dc2e8929ed174671ba7dc3339c/src/backend/utils/init/postinit.c#L257-L262
and [2]https://github.com/postgres/postgres/blob/02c408e21a6e78ff246ea7a1beb4669634fa9c4c/src/backend/libpq/auth.c#L372 usages of "log_connections";
"received" is mentioned at [3]https://github.com/postgres/postgres/blob/master/src/backend/postmaster/postmaster.c#L4393.

Example: log_connection_stages=’authorized,disconnected’.

would turn the boolean value into an enum value

I have thought about enums too, but we need to cover arbitrary
combinations of message types, for example log only "received" and
"disconnected".
Hence the proposed type "string" with individual values within the
string really drawn from an enum.

merge log_connections and log_disconnections into a new GUC (?) and deprecate them.

Are there any specific deprecation guidelines ? I have not found any
after a quick search for GUC deprecation in Google and commit history.
A deprecation scheme could look like that:
1. Mention in the docs "log_(dis)connections" are deprecated in favor
of "log_connection_stages"
2. Map "log_(dis)connections" to relevant values of
"log_connection_stages" in code if the latter is unset.
3. Complain in the logs if a conflict arises between the old params
and "log_connection_stages", with "log_connection_stages"
taking the precedence.

Regards,
Sergey

[1]: https://github.com/postgres/postgres/blob/3f8148c256e067dc2e8929ed174671ba7dc3339c/src/backend/utils/init/postinit.c#L257-L262
[2]: https://github.com/postgres/postgres/blob/02c408e21a6e78ff246ea7a1beb4669634fa9c4c/src/backend/libpq/auth.c#L372
[3]: https://github.com/postgres/postgres/blob/master/src/backend/postmaster/postmaster.c#L4393

#4Euler Taveira
euler@eulerto.com
In reply to: Sergey Dudoladov (#3)
Re: Introduce "log_connection_stages" setting.

On Thu, Jul 14, 2022, at 8:20 AM, Sergey Dudoladov wrote:

I've taken connection stages and terminology from the existing log messages.
The reason I have separated "authorized" and "authenticated" are [1]
and [2] usages of "log_connections";
"received" is mentioned at [3].

After checking the commit 9afffcb833d, I agree that you need 4 stages:
connected, authorized, authenticated, and disconnected.

I have thought about enums too, but we need to cover arbitrary
combinations of message types, for example log only "received" and
"disconnected".
Hence the proposed type "string" with individual values within the
string really drawn from an enum.

Ooops. I said enum but I meant string list.

Are there any specific deprecation guidelines ? I have not found any
after a quick search for GUC deprecation in Google and commit history.
A deprecation scheme could look like that:
1. Mention in the docs "log_(dis)connections" are deprecated in favor
of "log_connection_stages"
2. Map "log_(dis)connections" to relevant values of
"log_connection_stages" in code if the latter is unset.
3. Complain in the logs if a conflict arises between the old params
and "log_connection_stages", with "log_connection_stages"
taking the precedence.

No. AFAICS in this case, you just remove log_connections and log_disconnections
and create the new one (see for example the commit 88e98230268 that replace
checkpoint_segments with min_wal_size and max_wal_size). We don't generally
keep ConfigureNames* entries for deprecated GUCs. Unless you are renaming a GUC
-- see map_old_guc_names; that's not the case. When we remove a GUC, we are
introducing an incompatibility so the only place it will be mentioned is the
release notes (there is a section called "Migration to Version X" that lists
all incompatibilities). From the developer's point of view, you only need to
mention in the commit message that this commit is introducing an
incompatibility. Hence, when it is time to write the release notes, the
information about the removal and the new replacement will be added.

--
Euler Taveira
EDB https://www.enterprisedb.com/

#5Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Euler Taveira (#4)
1 attachment(s)
Re: Introduce "log_connection_stages" setting.

Hi hackers,

I've sketched an initial patch version; feedback is welcome.

Regards,
Sergey Dudoladov

Attachments:

0001-Introduce-log_connection_messages.patchtext/x-patch; charset=US-ASCII; name=0001-Introduce-log_connection_messages.patchDownload
From be2e6b5c2d6fff1021f52f150b4d849dfbd26ec7 Mon Sep 17 00:00:00 2001
From: Sergey Dudoladov <sergey.dudoladov@gmail.com>
Date: Tue, 8 Nov 2022 18:56:26 +0100
Subject: [PATCH] Introduce 'log_connection_messages'

This patch removes 'log_connections' and 'log_disconnections'
in favor of 'log_connection_messages', thereby introducing incompatibility

Author: Sergey Dudoladov

Reviewed-by:

Discussion:
https://www.postgresql.org/message-id/flat/CAA8Fd-qCB96uwfgMKrzfNs32mqqysi53yZFNVaRNJ6xDthZEgA%40mail.gmail.com
---
 doc/src/sgml/config.sgml                      | 80 +++++++++++------
 src/backend/commands/variable.c               | 88 +++++++++++++++++++
 src/backend/libpq/auth.c                      |  5 +-
 src/backend/postmaster/postmaster.c           |  5 +-
 src/backend/tcop/postgres.c                   | 14 +--
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_tables.c           | 29 +++---
 src/backend/utils/misc/postgresql.conf.sample |  5 +-
 src/include/postgres.h                        |  5 ++
 src/include/postmaster/postmaster.h           |  1 -
 src/include/utils/guc_hooks.h                 |  3 +
 src/test/authentication/t/001_password.pl     |  2 +-
 src/test/kerberos/t/001_auth.pl               |  2 +-
 src/test/ldap/t/001_auth.pl                   |  2 +-
 src/test/recovery/t/013_crash_restart.pl      |  2 +-
 src/test/recovery/t/022_crash_temp_files.pl   |  2 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |  2 +-
 src/test/ssl/t/SSL/Server.pm                  |  2 +-
 src/tools/ci/pg_ci_base.conf                  |  3 +-
 19 files changed, 182 insertions(+), 72 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 559eb898a9..ab9d118c12 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -140,7 +140,7 @@
      An example of what this file might look like is:
 <programlisting>
 # This is a comment
-log_connections = yes
+log_connection_messages = all
 log_destination = 'syslog'
 search_path = '"$user", public'
 shared_buffers = 128MB
@@ -335,7 +335,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
        passed to the <command>postgres</command> command via the
        <option>-c</option> command-line parameter.  For example,
 <programlisting>
-postgres -c log_connections=yes -c log_destination='syslog'
+postgres -c log_connection_messages=all -c log_destination='syslog'
 </programlisting>
        Settings provided in this way override those set via
        <filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@@ -6978,23 +6978,65 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-connections" xreflabel="log_connections">
-      <term><varname>log_connections</varname> (<type>boolean</type>)
+     <varlistentry id="guc-log-connection-messages" xreflabel="log_connection_messages">
+      <term><varname>log_connection_messages</varname> (<type>string</type>)
       <indexterm>
-       <primary><varname>log_connections</varname> configuration parameter</primary>
+       <primary><varname>log_connection_messages</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        Causes each attempted connection to the server to be logged,
-        as well as successful completion of both client authentication (if
-        necessary) and authorization.
+        Causes stages of each attempted connection to the server to be logged. Example: <literal>authorized,disconnected</literal>.
+        The default is the empty string meaning nothing is logged.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this parameter at session start,
         and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
        </para>
 
+    <table id="connection-messages">
+     <title>Connection messages</title>
+     <tgroup cols="2">
+      <colspec colname="col1" colwidth="1*"/>
+      <colspec colname="col2" colwidth="2*"/>
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal>received</literal></entry>
+        <entry>Logs reception of a connection. At this point a connection has been received, but no further work has been done:
+        Postgres is about to start interacting with a client.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity that an authentication method employs to identify a user. In most cases, the identity string equals the PostgreSQL username,
+        but some third-party authentication methods may alter the original user identifier before the server stores it. Failed authentication is always logged regardless of the value of this setting.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authorized</literal></entry>
+        <entry>Logs successful completion of authorization. At this point the connection has been fully established.</entry>
+       </row>
+
+       <row>
+        <entry><literal>disconnected</literal></entry>
+        <entry>Logs session termination. The log output provides information similar to <literal>authorized</literal>,
+        plus the duration of the session.</entry>
+       </row>
+
+       <row>
+        <entry><literal>all</literal></entry>
+        <entry>A convenience alias. When present, must be the only value in the list.</entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+
        <note>
         <para>
          Some client programs, like <application>psql</application>, attempt
@@ -7006,26 +7048,6 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-disconnections" xreflabel="log_disconnections">
-      <term><varname>log_disconnections</varname> (<type>boolean</type>)
-      <indexterm>
-       <primary><varname>log_disconnections</varname> configuration parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Causes session terminations to be logged.  The log output
-        provides information similar to <varname>log_connections</varname>,
-        plus the duration of the session.
-        Only superusers and users with the appropriate <literal>SET</literal>
-        privilege can change this parameter at session start,
-        and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-
      <varlistentry id="guc-log-duration" xreflabel="log_duration">
       <term><varname>log_duration</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 791bac6715..8b6798175b 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -993,6 +993,79 @@ show_role(void)
 }
 
 
+/* check_hook: validate new log_connection_messages value */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	char		*log_connection_messages = *newval;
+	bool		*myextra;
+
+	/*
+	 * Set up the "extra" struct actually used by assign_log_connection_messages.
+	 */
+	myextra = (bool *) guc_malloc(LOG, 4 * sizeof(bool));
+	if (!myextra)
+		return false;
+	memset(myextra, 0, 4 * sizeof(bool));
+	*extra = (void *) myextra;
+	if (log_connection_messages == NULL || log_connection_messages[0] == '\0') {
+		return true;
+	}
+
+	if (pg_strcasecmp(*newval, "all") == 0)
+	{
+		myextra[0] = true;
+		myextra[1] = true;
+		myextra[2] = true;
+		myextra[3] = true;
+		return true;
+	}
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+	/* Parse string into list of identifiers */
+	/* We rely on SplitIdentifierString to downcase and strip whitespace */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	foreach(l, namelist)
+	{
+		char		*stage = (char *) lfirst(l);
+
+		if (pg_strcasecmp(stage, "received") == 0)
+			myextra[0] = true;
+		else if (pg_strcasecmp(stage, "authenticated") == 0)
+			myextra[1] = true;
+		else if (pg_strcasecmp(stage, "authorized") == 0)
+			myextra[2] = true;
+		else if (pg_strcasecmp(stage, "disconnected") == 0)
+			myextra[3] = true;
+		else {
+			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+			GUC_check_errmsg("Invalid value '%s'", stage);
+			GUC_check_errdetail("Valid values to use in the list are 'received', 'authenticated', 'authorized', 'disconnected', and 'all'."
+			"If 'all' is present, it must be the only value in the list.");
+			pfree(rawname);
+			list_free(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	return true;
+}
+
 /*
  * PATH VARIABLES
  *
@@ -1015,6 +1088,21 @@ check_canonical_path(char **newval, void **extra, GucSource source)
 
 
 /*
+ * assign_log_connection_messages: GUC assign_hook for log_connection_messages
+ */
+void
+assign_log_connection_messages(const char *newval, void *extra)
+{
+
+	bool		*myextra = (bool *) extra;
+
+	Log_received = myextra[0];
+	Log_authenticated = myextra[1];
+	Log_authorized = myextra[2];
+	Log_disconnected = myextra[3];
+}
+
+ /*
  * MISCELLANEOUS
  */
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index e2f723d188..9a0cdc0c59 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -327,7 +327,8 @@ auth_failed(Port *port, int status, const char *logdetail)
 /*
  * Sets the authenticated identity for the current user.  The provided string
  * will be stored into MyClientConnectionInfo, alongside the current HBA
- * method in use.  The ID will be logged if log_connections is enabled.
+ * method in use.  The ID will be logged if
+ * log_connection_messages contains the 'authenticated' value.
  *
  * Auth methods should call this routine exactly once, as soon as the user is
  * successfully authenticated, even if they have reasons to know that
@@ -359,7 +360,7 @@ set_authn_id(Port *port, const char *id)
 	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 	MyClientConnectionInfo.auth_method = port->hba->auth_method;
 
-	if (Log_connections)
+	if (Log_authenticated)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 0b637ba6a2..6cc574012a 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -236,7 +236,6 @@ int			PreAuthDelay = 0;
 int			AuthenticationTimeout = 60;
 
 bool		log_hostname;		/* for ps display and logging */
-bool		Log_connections = false;
 bool		Db_user_namespace = false;
 
 bool		enable_bonjour = false;
@@ -4364,8 +4363,8 @@ BackendInitialize(Port *port)
 	port->remote_host = strdup(remote_host);
 	port->remote_port = strdup(remote_port);
 
-	/* And now we can issue the Log_connections message, if wanted */
-	if (Log_connections)
+	/* And now we can issue the Log_received message, if wanted */
+	if (Log_received)
 	{
 		if (remote_port[0])
 			ereport(LOG,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3082093d1e..81a27e4844 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -84,8 +84,11 @@ const char *debug_query_string; /* client-supplied query string */
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
 CommandDest whereToSendOutput = DestDebug;
 
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+bool		Log_disconnected = false;
+bool		Log_authenticated = false;
+bool		Log_authorized = false;
+bool		Log_received = false;
 
 int			log_statement = LOGSTMT_NONE;
 
@@ -3582,8 +3585,7 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 
 	if (debug_flag >= 1 && context == PGC_POSTMASTER)
 	{
-		SetConfigOption("log_connections", "true", context, source);
-		SetConfigOption("log_disconnections", "true", context, source);
+		SetConfigOption("log_connection_messages", "all", context, source);
 	}
 	if (debug_flag >= 2)
 		SetConfigOption("log_statement", "all", context, source);
@@ -4153,9 +4155,9 @@ PostgresMain(const char *dbname, const char *username)
 
 	/*
 	 * Also set up handler to log session end; we have to wait till now to be
-	 * sure Log_disconnections has its final value.
+	 * sure Log_disconnected has its final value.
 	 */
-	if (IsUnderPostmaster && Log_disconnections)
+	if (IsUnderPostmaster && Log_disconnected)
 		on_proc_exit(log_disconnections, 0);
 
 	pgstat_report_connect(MyDatabaseId);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 31b7e1de5d..ff2ce4bcf1 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -250,7 +250,7 @@ PerformAuthentication(Port *port)
 	 */
 	disable_timeout(STATEMENT_TIMEOUT, false);
 
-	if (Log_connections)
+	if (Log_authorized)
 	{
 		StringInfoData logmsg;
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 836b49484a..6dcf54da33 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -579,6 +579,7 @@ static char *recovery_target_string;
 static char *recovery_target_xid_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
+static char *log_connection_messages_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1161,24 +1162,6 @@ struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs each successful connection."),
-			NULL
-		},
-		&Log_connections,
-		false,
-		NULL, NULL, NULL
-	},
-	{
-		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs end of a session, including duration."),
-			NULL
-		},
-		&Log_disconnections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
 			gettext_noop("Logs each replication command."),
@@ -4097,6 +4080,16 @@ struct config_string ConfigureNamesString[] =
 		check_session_authorization, assign_session_authorization, NULL
 	},
 
+	{
+		{"log_connection_messages", PGC_SU_BACKEND, LOGGING_WHAT,
+			gettext_noop("Lists connection stages to log."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&log_connection_messages_string,
+		"",
+		check_log_connection_messages, assign_log_connection_messages, NULL
+	},
 	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 868d21c351..d34316d11c 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -21,7 +21,7 @@
 # require a server shutdown and restart to take effect.
 #
 # Any parameter can also be given as a command-line option to the server, e.g.,
-# "postgres -c log_connections=on".  Some parameters can be changed at run time
+# "postgres -c log_connection_messages=all".  Some parameters can be changed at run time
 # with the "SET" SQL command.
 #
 # Memory units:  B  = bytes            Time units:  us  = microseconds
@@ -551,8 +551,7 @@
 					# actions running at least this number
 					# of milliseconds.
 #log_checkpoints = on
-#log_connections = off
-#log_disconnections = off
+#log_connection_messages = ''
 #log_duration = off
 #log_error_verbosity = default		# terse, default, or verbose messages
 #log_hostname = off
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 54730dfb38..9ad1a781e8 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -924,4 +924,9 @@ extern Datum Float8GetDatum(float8 X);
 #define NON_EXEC_STATIC static
 #endif
 
+extern PGDLLIMPORT bool	Log_received;
+extern PGDLLIMPORT bool	Log_authenticated;
+extern PGDLLIMPORT bool	Log_authorized;
+extern PGDLLIMPORT bool	Log_disconnected;
+
 #endif							/* POSTGRES_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 90e333ccd2..3bd9a5bfcd 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -24,7 +24,6 @@ extern PGDLLIMPORT char *ListenAddresses;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
-extern PGDLLIMPORT bool Log_connections;
 extern PGDLLIMPORT bool log_hostname;
 extern PGDLLIMPORT bool enable_bonjour;
 extern PGDLLIMPORT char *bonjour_name;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index f1a9a183b4..af5bef31ea 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -67,6 +67,8 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
 extern void assign_locale_numeric(const char *newval, void *extra);
 extern bool check_locale_time(char **newval, void **extra, GucSource source);
 extern void assign_locale_time(const char *newval, void *extra);
+extern bool check_log_connection_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_connection_messages(const char *newval, void *extra);
 extern bool check_log_destination(char **newval, void **extra,
 								  GucSource source);
 extern void assign_log_destination(const char *newval, void *extra);
@@ -155,4 +157,5 @@ extern bool check_wal_consistency_checking(char **newval, void **extra,
 extern void assign_wal_consistency_checking(const char *newval, void *extra);
 extern void assign_xlog_sync_method(int new_sync_method, void *extra);
 
+
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 42d3d4c79b..be457c4d68 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -63,7 +63,7 @@ sub test_conn
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Create 3 roles with different password methods for each one. The same
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index a2bc8a5351..c9dd2d3546 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -173,7 +173,7 @@ $node->append_conf(
 	'postgresql.conf', qq{
 listen_addresses = '$hostaddr'
 krb_server_keyfile = '$keytab'
-log_connections = on
+log_connection_messages = all
 lc_messages = 'C'
 });
 $node->start;
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index fd90832b75..94ceb3c267 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -169,7 +169,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl
index c22844d39c..d005676891 100644
--- a/src/test/recovery/t/013_crash_restart.pl
+++ b/src/test/recovery/t/013_crash_restart.pl
@@ -27,7 +27,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET restart_after_crash = 1;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = all;
 				   SELECT pg_reload_conf();]);
 
 # Run psql, keeping session alive, so we have an alive backend to kill.
diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl
index 53a55c7a8a..e756a0f33c 100644
--- a/src/test/recovery/t/022_crash_temp_files.pl
+++ b/src/test/recovery/t/022_crash_temp_files.pl
@@ -26,7 +26,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET remove_temp_files_after_crash = on;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = all;
 				   ALTER SYSTEM SET work_mem = '64kB';
 				   ALTER SYSTEM SET restart_after_crash = on;
 				   SELECT pg_reload_conf();]);
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index 499ec34a7a..b0a0d19840 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -18,7 +18,7 @@ $node_primary->init(allows_streaming => 1);
 $node_primary->append_conf(
 	'postgresql.conf', q[
 allow_in_place_tablespaces = true
-log_connections=on
+log_connection_messages=all
 # to avoid "repairing" corruption
 full_page_writes=off
 log_min_messages=debug2
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index 9520578e7d..2114efb2c7 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -193,7 +193,7 @@ sub configure_test_server_for_ssl
 	# enable logging etc.
 	open my $conf, '>>', "$pgdata/postgresql.conf";
 	print $conf "fsync=off\n";
-	print $conf "log_connections=on\n";
+	print $conf "log_connection_messages=all\n";
 	print $conf "log_hostname=on\n";
 	print $conf "listen_addresses='$serverhost'\n";
 	print $conf "log_statement=all\n";
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
index d8faa9c26c..7596d515aa 100644
--- a/src/tools/ci/pg_ci_base.conf
+++ b/src/tools/ci/pg_ci_base.conf
@@ -8,7 +8,6 @@ max_prepared_transactions = 10
 # Settings that make logs more useful
 log_autovacuum_min_duration = 0
 log_checkpoints = true
-log_connections = true
-log_disconnections = true
+log_connection_messages = all
 log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
 log_lock_waits = true
-- 
2.34.1

#6Justin Pryzby
pryzby@telsasoft.com
In reply to: Sergey Dudoladov (#5)
Re: Introduce "log_connection_stages" setting.

On Tue, Nov 08, 2022 at 07:30:38PM +0100, Sergey Dudoladov wrote:

+ <entry>Logs reception of a connection. At this point a connection has been received, but no further work has been done:

receipt

+ <entry>Logs the original identity that an authentication method employs to identify a user. In most cases, the identity string equals the PostgreSQL username,

s/equals/matches

+/* check_hook: validate new log_connection_messages value */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	char		*log_connection_messages = *newval;
+	bool		*myextra;
+
+	/*
+	 * Set up the "extra" struct actually used by assign_log_connection_messages.
+	 */
+	myextra = (bool *) guc_malloc(LOG, 4 * sizeof(bool));

This function hardcodes each of the 4 connections:

+		if (pg_strcasecmp(stage, "received") == 0)
+			myextra[0] = true;

It'd be better to use #defines or enums for these.

--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -84,8 +84,11 @@ const char *debug_query_string; /* client-supplied query string */
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
CommandDest whereToSendOutput = DestDebug;
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+bool		Log_disconnected = false;
+bool		Log_authenticated = false;
+bool		Log_authorized = false;
+bool		Log_received = false;

I think this ought to be an integer with flag bits, rather than 4
booleans (I don't know, but there might be more later?). Then, the
implementation follows the user-facing GUC and also follows
log_destination.

--
Justin

#7Justin Pryzby
pryzby@telsasoft.com
In reply to: Justin Pryzby (#6)
Re: Introduce "log_connection_stages" setting.

On Thu, Nov 17, 2022 at 09:36:29AM -0600, Justin Pryzby wrote:

On Tue, Nov 08, 2022 at 07:30:38PM +0100, Sergey Dudoladov wrote:

...

Also (I didn't realize there was a CF entry), this is failing tests.
http://cfbot.cputube.org/sergey-dudoladov.html

--
Justin

#8Jacob Champion
jchampion@timescale.com
In reply to: Justin Pryzby (#6)
Re: Introduce "log_connection_stages" setting.

Hi,

+1 for the idea!

+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity that an authentication method employs to identify a user. In most cases, the identity string equals the PostgreSQL username,
+        but some third-party authentication methods may alter the original user identifier before the server stores it. Failed authentication is always logged regardless of the value of this setting.</entry>

I think the documentation needs to be rewrapped; those are very long lines.

On 11/17/22 07:36, Justin Pryzby wrote:

This function hardcodes each of the 4 connections:

+		if (pg_strcasecmp(stage, "received") == 0)
+			myextra[0] = true;

It'd be better to use #defines or enums for these.

Hardcoding seems reasonable to me, if this is the only place we're doing
string comparison.

--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -84,8 +84,11 @@ const char *debug_query_string; /* client-supplied query string */
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
CommandDest whereToSendOutput = DestDebug;
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+bool		Log_disconnected = false;
+bool		Log_authenticated = false;
+bool		Log_authorized = false;
+bool		Log_received = false;

I think this ought to be an integer with flag bits, rather than 4
booleans (I don't know, but there might be more later?). Then, the
implementation follows the user-facing GUC and also follows
log_destination.

Agreed. Or at the very least, follow what's done with
wal_consistency_checking? But I think flag bits would be better.

The tests should be expanded for cases other than 'all'.

As to the failing test cases: it looks like there's a keyword issue with
ALTER SYSTEM and 'all', but trying to fix it by quoting also fails. I
think it's because of GUC_LIST_QUOTE -- is there a reason that's used
here? I don't think we'd need any special characters in future option
names. wal_consistency_checking is very similar, and it just uses
GUC_LIST_INPUT.

Thanks,
--Jacob

#9Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Jacob Champion (#8)
1 attachment(s)
Re: Introduce "log_connection_stages" setting.

Hello, hackers

Thank you for the reviews. I've modified the patch to incorporate your
suggestions:
+ flag bits are now used to encode different connection stages
+ failing tests are now fixed. It was not a keyword issue but rather
"check_log_connection_messages" not allocating memory properly
   in the special case log_connection_messages =  'all'
+ the GUC option is now only GUC_LIST_INPUT
+ typo fixes and line rewrapping in the docs

Regards,
Sergey

Attachments:

0002-Introduce-log_connection_messages.patchtext/x-patch; charset=US-ASCII; name=0002-Introduce-log_connection_messages.patchDownload
From 4bf99bccb4b278188dbc679f00d506cd35b025f5 Mon Sep 17 00:00:00 2001
From: Sergey Dudoladov <sergey.dudoladov@gmail.com>
Date: Tue, 8 Nov 2022 18:56:26 +0100
Subject: [PATCH] Introduce 'log_connection_messages'

This patch removes 'log_connections' and 'log_disconnections'
in favor of 'log_connection_messages', thereby introducing incompatibility

Author: Sergey Dudoladov

Reviewed-by:

Discussion:
https://www.postgresql.org/message-id/flat/CAA8Fd-qCB96uwfgMKrzfNs32mqqysi53yZFNVaRNJ6xDthZEgA%40mail.gmail.com
---
 doc/src/sgml/config.sgml                      | 89 +++++++++++++------
 src/backend/commands/variable.c               | 77 ++++++++++++++++
 src/backend/libpq/auth.c                      |  5 +-
 src/backend/postmaster/postmaster.c           |  5 +-
 src/backend/tcop/postgres.c                   | 11 ++-
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_tables.c           | 29 +++---
 src/backend/utils/misc/postgresql.conf.sample |  5 +-
 src/include/postgres.h                        |  9 ++
 src/include/postmaster/postmaster.h           |  1 -
 src/include/utils/guc_hooks.h                 |  2 +
 src/test/authentication/t/001_password.pl     |  2 +-
 src/test/authentication/t/003_peer.pl         |  2 +-
 src/test/kerberos/t/001_auth.pl               |  2 +-
 src/test/ldap/t/001_auth.pl                   |  2 +-
 src/test/recovery/t/013_crash_restart.pl      |  2 +-
 src/test/recovery/t/022_crash_temp_files.pl   |  2 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |  2 +-
 src/test/ssl/t/SSL/Server.pm                  |  2 +-
 src/tools/ci/pg_ci_base.conf                  |  3 +-
 20 files changed, 181 insertions(+), 73 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 1cf53c74ea..8b60e814e9 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -140,7 +140,7 @@
      An example of what this file might look like is:
 <programlisting>
 # This is a comment
-log_connections = yes
+log_connection_messages = all
 log_destination = 'syslog'
 search_path = '"$user", public'
 shared_buffers = 128MB
@@ -335,7 +335,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
        passed to the <command>postgres</command> command via the
        <option>-c</option> command-line parameter.  For example,
 <programlisting>
-postgres -c log_connections=yes -c log_destination='syslog'
+postgres -c log_connection_messages=all -c log_destination='syslog'
 </programlisting>
        Settings provided in this way override those set via
        <filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@@ -7085,23 +7085,74 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-connections" xreflabel="log_connections">
-      <term><varname>log_connections</varname> (<type>boolean</type>)
+     <varlistentry id="guc-log-connection-messages" xreflabel="log_connection_messages">
+      <term><varname>log_connection_messages</varname> (<type>string</type>)
       <indexterm>
-       <primary><varname>log_connections</varname> configuration parameter</primary>
+       <primary><varname>log_connection_messages</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        Causes each attempted connection to the server to be logged,
-        as well as successful completion of both client authentication (if
-        necessary) and authorization.
+        Causes stages of each attempted connection to the server to be logged. Example: <literal>authorized,disconnected</literal>.
+        The default is the empty string meaning nothing is logged.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this parameter at session start,
         and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
        </para>
 
+    <table id="connection-messages">
+     <title>Connection messages</title>
+     <tgroup cols="2">
+      <colspec colname="col1" colwidth="1*"/>
+      <colspec colname="col2" colwidth="2*"/>
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal>received</literal></entry>
+        <entry>Logs receipt of a connection. At this point a connection has been
+        received, but no further work has been done: Postgres is about to start
+        interacting with a client.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity that an authentication method employs
+        to identify a user. In most cases, the identity string matches the
+        PostgreSQL username, but some third-party authentication methods may
+        alter the original user identifier before the server stores it. Failed
+        authentication is always logged regardless of the value of this setting.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>authorized</literal></entry>
+        <entry>Logs successful completion of authorization. At this point the
+        connection has been fully established.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>disconnected</literal></entry>
+        <entry>Logs session termination. The log output provides information
+        similar to <literal>authorized</literal>, plus the duration of the session.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>all</literal></entry>
+        <entry>A convenience alias. If present, it must be the only value in the
+        list.</entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+
        <note>
         <para>
          Some client programs, like <application>psql</application>, attempt
@@ -7113,26 +7164,6 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-disconnections" xreflabel="log_disconnections">
-      <term><varname>log_disconnections</varname> (<type>boolean</type>)
-      <indexterm>
-       <primary><varname>log_disconnections</varname> configuration parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Causes session terminations to be logged.  The log output
-        provides information similar to <varname>log_connections</varname>,
-        plus the duration of the session.
-        Only superusers and users with the appropriate <literal>SET</literal>
-        privilege can change this parameter at session start,
-        and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-
      <varlistentry id="guc-log-duration" xreflabel="log_duration">
       <term><varname>log_duration</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index bb0f5de4c2..8f10b2bc59 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -993,6 +993,74 @@ show_role(void)
 }
 
 
+/* check_hook: validate new log_connection_messages value
+ *
+ * The implementation follows the check_log_destination hook.
+ */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	int			newlogconnect = 0;
+	int			*myextra;
+
+	if (pg_strcasecmp(*newval, "all") == 0)
+	{
+		newlogconnect |= LOG_CONNECTION_ALL;
+		myextra = (int *) guc_malloc(ERROR, sizeof(int));
+		*myextra = newlogconnect;
+		*extra = (void *) myextra;
+		return true;
+	}
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/* Parse string into list of identifiers */
+	/* We rely on SplitIdentifierString to downcase and strip whitespace */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	foreach(l, namelist)
+	{
+		char		*stage = (char *) lfirst(l);
+		if (pg_strcasecmp(stage, "received") == 0)
+			newlogconnect |= LOG_CONNECTION_RECEIVED;
+		else if (pg_strcasecmp(stage, "authenticated") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHENTICATED;
+		else if (pg_strcasecmp(stage, "authorized") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHORIZED;
+		else if (pg_strcasecmp(stage, "disconnected") == 0)
+			newlogconnect |= LOG_CONNECTION_DISCONNECTED;
+		else {
+			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+			GUC_check_errmsg("Invalid value '%s'", stage);
+			GUC_check_errdetail("Valid values to use in the list are 'received', 'authenticated', 'authorized', 'disconnected', and 'all'."
+			"If 'all' is present, it must be the only value in the list.");
+			pfree(rawname);
+			list_free(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	myextra = (int *) guc_malloc(ERROR, sizeof(int));
+	*myextra = newlogconnect;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
 /*
  * PATH VARIABLES
  *
@@ -1015,6 +1083,15 @@ check_canonical_path(char **newval, void **extra, GucSource source)
 
 
 /*
+ * assign_log_connection_messages: GUC assign_hook for log_connection_messages
+ */
+void
+assign_log_connection_messages(const char *newval, void *extra)
+{
+	Log_connection_messages = *((int *) extra);
+}
+
+ /*
  * MISCELLANEOUS
  */
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 25b3a781cd..e0f8034fb3 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -327,7 +327,8 @@ auth_failed(Port *port, int status, const char *logdetail)
 /*
  * Sets the authenticated identity for the current user.  The provided string
  * will be stored into MyClientConnectionInfo, alongside the current HBA
- * method in use.  The ID will be logged if log_connections is enabled.
+ * method in use.  The ID will be logged if
+ * log_connection_messages contains the 'authenticated' value.
  *
  * Auth methods should call this routine exactly once, as soon as the user is
  * successfully authenticated, even if they have reasons to know that
@@ -359,7 +360,7 @@ set_authn_id(Port *port, const char *id)
 	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 	MyClientConnectionInfo.auth_method = port->hba->auth_method;
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHENTICATED)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index f92dbc2270..0710168ae3 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -235,7 +235,6 @@ int			PreAuthDelay = 0;
 int			AuthenticationTimeout = 60;
 
 bool		log_hostname;		/* for ps display and logging */
-bool		Log_connections = false;
 bool		Db_user_namespace = false;
 
 bool		enable_bonjour = false;
@@ -4343,8 +4342,8 @@ BackendInitialize(Port *port)
 	port->remote_host = strdup(remote_host);
 	port->remote_port = strdup(remote_port);
 
-	/* And now we can issue the Log_connections message, if wanted */
-	if (Log_connections)
+	/* And now we can issue the "connection received" message, if wanted */
+	if (Log_connection_messages & LOG_CONNECTION_RECEIVED)
 	{
 		if (remote_port[0])
 			ereport(LOG,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 470b734e9e..4b8a22bfdf 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -83,8 +83,8 @@ const char *debug_query_string; /* client-supplied query string */
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
 CommandDest whereToSendOutput = DestDebug;
 
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+int			Log_connection_messages = LOG_CONNECTION_ALL;
 
 int			log_statement = LOGSTMT_NONE;
 
@@ -3596,8 +3596,7 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 
 	if (debug_flag >= 1 && context == PGC_POSTMASTER)
 	{
-		SetConfigOption("log_connections", "true", context, source);
-		SetConfigOption("log_disconnections", "true", context, source);
+		SetConfigOption("log_connection_messages", "all", context, source);
 	}
 	if (debug_flag >= 2)
 		SetConfigOption("log_statement", "all", context, source);
@@ -4167,9 +4166,9 @@ PostgresMain(const char *dbname, const char *username)
 
 	/*
 	 * Also set up handler to log session end; we have to wait till now to be
-	 * sure Log_disconnections has its final value.
+	 * sure Log_connection_messages has its final value.
 	 */
-	if (IsUnderPostmaster && Log_disconnections)
+	if (IsUnderPostmaster && (Log_connection_messages & LOG_CONNECTION_DISCONNECTED))
 		on_proc_exit(log_disconnections, 0);
 
 	pgstat_report_connect(MyDatabaseId);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 2f07ca7a0e..a780c19349 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -250,7 +250,7 @@ PerformAuthentication(Port *port)
 	 */
 	disable_timeout(STATEMENT_TIMEOUT, false);
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHORIZED)
 	{
 		StringInfoData logmsg;
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index c5a95f5dcc..5d6bedaf7f 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -585,6 +585,7 @@ static char *recovery_target_string;
 static char *recovery_target_xid_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
+static char *log_connection_messages_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1182,24 +1183,6 @@ struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs each successful connection."),
-			NULL
-		},
-		&Log_connections,
-		false,
-		NULL, NULL, NULL
-	},
-	{
-		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs end of a session, including duration."),
-			NULL
-		},
-		&Log_disconnections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
 			gettext_noop("Logs each replication command."),
@@ -4163,6 +4146,16 @@ struct config_string ConfigureNamesString[] =
 		check_session_authorization, assign_session_authorization, NULL
 	},
 
+	{
+		{"log_connection_messages", PGC_SU_BACKEND, LOGGING_WHAT,
+			gettext_noop("Lists connection stages to log."),
+			NULL,
+			GUC_LIST_INPUT
+		},
+		&log_connection_messages_string,
+		"",
+		check_log_connection_messages, assign_log_connection_messages, NULL
+	},
 	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..23c6705cf2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -21,7 +21,7 @@
 # require a server shutdown and restart to take effect.
 #
 # Any parameter can also be given as a command-line option to the server, e.g.,
-# "postgres -c log_connections=on".  Some parameters can be changed at run time
+# "postgres -c log_connection_messages=all".  Some parameters can be changed at run time
 # with the "SET" SQL command.
 #
 # Memory units:  B  = bytes            Time units:  us  = microseconds
@@ -553,8 +553,7 @@
 					# actions running at least this number
 					# of milliseconds.
 #log_checkpoints = on
-#log_connections = off
-#log_disconnections = off
+#log_connection_messages = ''
 #log_duration = off
 #log_error_verbosity = default		# terse, default, or verbose messages
 #log_hostname = off
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..5c0dfa0590 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -576,4 +576,13 @@ extern Datum Float8GetDatum(float8 X);
 #define NON_EXEC_STATIC static
 #endif
 
+extern PGDLLIMPORT int	Log_connection_messages;
+
+/* Bitmap for logging connection messages */
+#define LOG_CONNECTION_RECEIVED		 1
+#define LOG_CONNECTION_AUTHENTICATED 2
+#define LOG_CONNECTION_AUTHORIZED	 4
+#define LOG_CONNECTION_DISCONNECTED  8
+#define LOG_CONNECTION_ALL			 15
+
 #endif							/* POSTGRES_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 3b3889c58c..e1ad7e1c50 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -25,7 +25,6 @@ extern PGDLLIMPORT char *ListenAddresses;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
-extern PGDLLIMPORT bool Log_connections;
 extern PGDLLIMPORT bool log_hostname;
 extern PGDLLIMPORT bool enable_bonjour;
 extern PGDLLIMPORT char *bonjour_name;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index aeb3663071..9690c13ab6 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -67,6 +67,8 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
 extern void assign_locale_numeric(const char *newval, void *extra);
 extern bool check_locale_time(char **newval, void **extra, GucSource source);
 extern void assign_locale_time(const char *newval, void *extra);
+extern bool check_log_connection_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_connection_messages(const char *newval, void *extra);
 extern bool check_log_destination(char **newval, void **extra,
 								  GucSource source);
 extern void assign_log_destination(const char *newval, void *extra);
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index a2fde1408b..30581ea5d7 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -63,7 +63,7 @@ sub test_conn
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Create 3 roles with different password methods for each one. The same
diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl
index a6be651ea7..4ad97da2f4 100644
--- a/src/test/authentication/t/003_peer.pl
+++ b/src/test/authentication/t/003_peer.pl
@@ -82,7 +82,7 @@ sub find_in_log
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Set pg_hba.conf with the peer authentication.
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index d610ce63ab..8b1b87e554 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -180,7 +180,7 @@ $node->append_conf(
 	'postgresql.conf', qq{
 listen_addresses = '$hostaddr'
 krb_server_keyfile = '$keytab'
-log_connections = on
+log_connection_messages = all
 lc_messages = 'C'
 });
 $node->start;
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index f3ed806ec2..6e6dbb830a 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -48,7 +48,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl
index 92e7b367df..ff0bb3daf0 100644
--- a/src/test/recovery/t/013_crash_restart.pl
+++ b/src/test/recovery/t/013_crash_restart.pl
@@ -27,7 +27,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET restart_after_crash = 1;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   SELECT pg_reload_conf();]);
 
 # Run psql, keeping session alive, so we have an alive backend to kill.
diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl
index 03c8efdfb5..2a466da25b 100644
--- a/src/test/recovery/t/022_crash_temp_files.pl
+++ b/src/test/recovery/t/022_crash_temp_files.pl
@@ -26,7 +26,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET remove_temp_files_after_crash = on;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   ALTER SYSTEM SET work_mem = '64kB';
 				   ALTER SYSTEM SET restart_after_crash = on;
 				   SELECT pg_reload_conf();]);
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index 92ec510037..f4ac72ef62 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -11,7 +11,7 @@ $node_primary->init(allows_streaming => 1);
 $node_primary->append_conf(
 	'postgresql.conf', q[
 allow_in_place_tablespaces = true
-log_connections=on
+log_connection_messages=all
 # to avoid "repairing" corruption
 full_page_writes=off
 log_min_messages=debug2
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index b6344b936a..6cd5a46921 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -193,7 +193,7 @@ sub configure_test_server_for_ssl
 	# enable logging etc.
 	open my $conf, '>>', "$pgdata/postgresql.conf";
 	print $conf "fsync=off\n";
-	print $conf "log_connections=on\n";
+	print $conf "log_connection_messages=all\n";
 	print $conf "log_hostname=on\n";
 	print $conf "listen_addresses='$serverhost'\n";
 	print $conf "log_statement=all\n";
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
index d8faa9c26c..7596d515aa 100644
--- a/src/tools/ci/pg_ci_base.conf
+++ b/src/tools/ci/pg_ci_base.conf
@@ -8,7 +8,6 @@ max_prepared_transactions = 10
 # Settings that make logs more useful
 log_autovacuum_min_duration = 0
 log_checkpoints = true
-log_connections = true
-log_disconnections = true
+log_connection_messages = all
 log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
 log_lock_waits = true
-- 
2.34.1

#10Justin Pryzby
pryzby@telsasoft.com
In reply to: Sergey Dudoladov (#9)
Re: Introduce "log_connection_stages" setting.

Thanks for updating the patch. It's currently failing check-world, due
to a test that was added on January 23 (a9dc7f941):

http://cfbot.cputube.org/sergey-dudoladov.html
[19:15:57.101] Summary of Failures:
[19:15:57.101] [19:15:57.101] 250/251 postgresql:ldap / ldap/002_bindpasswd ERROR 1.38s

2023-01-30 19:15:52.427 GMT [56038] LOG: unrecognized configuration parameter "log_connections" in file "/tmp/cirrus-ci-build/build/testrun/ldap/002_bindpasswd/data/t_002_bindpasswd_node_data/pgdata/postgresql.conf" line 839

+ received, but no further work has been done: Postgres is about to start

say "PostgreSQL" to match the rest of the docs.

+ GUC_check_errmsg("Invalid value '%s'", stage);

This message shouldn't be uppercased.

+			GUC_check_errdetail("Valid values to use in the list are 'received', 'authenticated', 'authorized', 'disconnected', and 'all'."
+			"If 'all' is present, it must be the only value in the list.");

Maybe "all" should be first ?

There's no spaces before "If":

| 2023-01-31 00:17:48.906 GMT [5676] DETALLE: Valid values to use in the list are 'received', 'authenticated', 'authorized', 'disconnected', and 'all'.If 'all' is present, it must be the only value in the list.

+/* flags for logging information about session state */
+int			Log_connection_messages = LOG_CONNECTION_ALL;

The initial value here is overwritten by the GUC default during startup.
For consistency, the integer should be initialized to 0.

+extern PGDLLIMPORT int	Log_connection_messages;
+
+/* Bitmap for logging connection messages */
+#define LOG_CONNECTION_RECEIVED		 1
+#define LOG_CONNECTION_AUTHENTICATED 2
+#define LOG_CONNECTION_AUTHORIZED	 4
+#define LOG_CONNECTION_DISCONNECTED  8
+#define LOG_CONNECTION_ALL			 15

Maybe the integers should be written like (1<<0)..
And maybe ALL should be 0xffff ?

More nitpicks:

+ Causes stages of each attempted connection to the server to be logged. Example: <literal>authorized,disconnected</literal>.

"Causes the specified stages of each connection attempt .."

+ The default is the empty string meaning nothing is logged.

".. string, which means that nothing is logged"

+        <entry>Logs the original identity that an authentication method employs
+        to identify a user. In most cases, the identity string matches the

".. original identity used by an authentication method ..'

--
Justin

#11Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Justin Pryzby (#10)
1 attachment(s)
Re: Introduce "log_connection_stages" setting.

Hi again,

Justin, thank you for the fast review. The new version is attached.

Regards,
Sergey Dudoladov

Attachments:

0003-Introduce-log_connection_messages.patchtext/x-patch; charset=US-ASCII; name=0003-Introduce-log_connection_messages.patchDownload
From 994a86e6ac3abb647d93bdaf0f42be76f42b83a8 Mon Sep 17 00:00:00 2001
From: Sergey Dudoladov <sergey.dudoladov@gmail.com>
Date: Tue, 8 Nov 2022 18:56:26 +0100
Subject: [PATCH] Introduce 'log_connection_messages'

This patch removes 'log_connections' and 'log_disconnections'
in favor of 'log_connection_messages', thereby introducing incompatibility

Author: Sergey Dudoladov

Reviewed-by: Justin Pryzby

Discussion:
https://www.postgresql.org/message-id/flat/CAA8Fd-qCB96uwfgMKrzfNs32mqqysi53yZFNVaRNJ6xDthZEgA%40mail.gmail.com
---
 doc/src/sgml/config.sgml                      | 89 +++++++++++++------
 src/backend/commands/variable.c               | 77 ++++++++++++++++
 src/backend/libpq/auth.c                      |  5 +-
 src/backend/postmaster/postmaster.c           |  5 +-
 src/backend/tcop/postgres.c                   | 11 ++-
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_tables.c           | 30 +++----
 src/backend/utils/misc/postgresql.conf.sample |  5 +-
 src/include/postgres.h                        |  9 ++
 src/include/postmaster/postmaster.h           |  1 -
 src/include/utils/guc_hooks.h                 |  2 +
 src/test/authentication/t/001_password.pl     |  2 +-
 src/test/authentication/t/003_peer.pl         |  2 +-
 src/test/kerberos/t/001_auth.pl               |  2 +-
 src/test/ldap/t/001_auth.pl                   |  2 +-
 src/test/ldap/t/002_bindpasswd.pl             |  2 +-
 src/test/recovery/t/013_crash_restart.pl      |  2 +-
 src/test/recovery/t/022_crash_temp_files.pl   |  2 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |  2 +-
 src/test/ssl/t/SSL/Server.pm                  |  2 +-
 src/tools/ci/pg_ci_base.conf                  |  3 +-
 21 files changed, 182 insertions(+), 75 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 1cf53c74ea..ccefe144c3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -140,7 +140,7 @@
      An example of what this file might look like is:
 <programlisting>
 # This is a comment
-log_connections = yes
+log_connection_messages = all
 log_destination = 'syslog'
 search_path = '"$user", public'
 shared_buffers = 128MB
@@ -335,7 +335,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
        passed to the <command>postgres</command> command via the
        <option>-c</option> command-line parameter.  For example,
 <programlisting>
-postgres -c log_connections=yes -c log_destination='syslog'
+postgres -c log_connection_messages=all -c log_destination='syslog'
 </programlisting>
        Settings provided in this way override those set via
        <filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@@ -7085,23 +7085,74 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-connections" xreflabel="log_connections">
-      <term><varname>log_connections</varname> (<type>boolean</type>)
+     <varlistentry id="guc-log-connection-messages" xreflabel="log_connection_messages">
+      <term><varname>log_connection_messages</varname> (<type>string</type>)
       <indexterm>
-       <primary><varname>log_connections</varname> configuration parameter</primary>
+       <primary><varname>log_connection_messages</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        Causes each attempted connection to the server to be logged,
-        as well as successful completion of both client authentication (if
-        necessary) and authorization.
+        Causes the specified stages of each connection attempt to the server to be logged. Example: <literal>authorized,disconnected</literal>.
+        The default is the empty string, which means that nothing is logged.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this parameter at session start,
         and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
        </para>
 
+    <table id="connection-messages">
+     <title>Connection messages</title>
+     <tgroup cols="2">
+      <colspec colname="col1" colwidth="1*"/>
+      <colspec colname="col2" colwidth="2*"/>
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal>received</literal></entry>
+        <entry>Logs receipt of a connection. At this point a connection has been
+        received, but no further work has been done: PostgreSQL is about to start
+        interacting with a client.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity used by an authentication method
+        to identify a user. In most cases, the identity string matches the
+        PostgreSQL username, but some third-party authentication methods may
+        alter the original user identifier before the server stores it. Failed
+        authentication is always logged regardless of the value of this setting.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>authorized</literal></entry>
+        <entry>Logs successful completion of authorization. At this point the
+        connection has been fully established.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>disconnected</literal></entry>
+        <entry>Logs session termination. The log output provides information
+        similar to <literal>authorized</literal>, plus the duration of the session.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>all</literal></entry>
+        <entry>A convenience alias. If present, it must be the only value in the
+        list.</entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+
        <note>
         <para>
          Some client programs, like <application>psql</application>, attempt
@@ -7113,26 +7164,6 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-disconnections" xreflabel="log_disconnections">
-      <term><varname>log_disconnections</varname> (<type>boolean</type>)
-      <indexterm>
-       <primary><varname>log_disconnections</varname> configuration parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Causes session terminations to be logged.  The log output
-        provides information similar to <varname>log_connections</varname>,
-        plus the duration of the session.
-        Only superusers and users with the appropriate <literal>SET</literal>
-        privilege can change this parameter at session start,
-        and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-
      <varlistentry id="guc-log-duration" xreflabel="log_duration">
       <term><varname>log_duration</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index bb0f5de4c2..b76fd6f27c 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -993,6 +993,74 @@ show_role(void)
 }
 
 
+/* check_hook: validate new log_connection_messages value
+ *
+ * The implementation follows the check_log_destination hook.
+ */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	int			newlogconnect = 0;
+	int			*myextra;
+
+	if (pg_strcasecmp(*newval, "all") == 0)
+	{
+		newlogconnect |= LOG_CONNECTION_ALL;
+		myextra = (int *) guc_malloc(ERROR, sizeof(int));
+		*myextra = newlogconnect;
+		*extra = (void *) myextra;
+		return true;
+	}
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/* Parse string into list of identifiers */
+	/* We rely on SplitIdentifierString to downcase and strip whitespace */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	foreach(l, namelist)
+	{
+		char		*stage = (char *) lfirst(l);
+		if (pg_strcasecmp(stage, "received") == 0)
+			newlogconnect |= LOG_CONNECTION_RECEIVED;
+		else if (pg_strcasecmp(stage, "authenticated") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHENTICATED;
+		else if (pg_strcasecmp(stage, "authorized") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHORIZED;
+		else if (pg_strcasecmp(stage, "disconnected") == 0)
+			newlogconnect |= LOG_CONNECTION_DISCONNECTED;
+		else {
+			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+			GUC_check_errmsg("invalid value '%s'", stage);
+			GUC_check_errdetail("Valid values to use in the list are 'all', 'received', 'authenticated', 'authorized', and 'disconnected'."
+			" If 'all' is present, it must be the only value in the list.");
+			pfree(rawname);
+			list_free(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	myextra = (int *) guc_malloc(ERROR, sizeof(int));
+	*myextra = newlogconnect;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
 /*
  * PATH VARIABLES
  *
@@ -1015,6 +1083,15 @@ check_canonical_path(char **newval, void **extra, GucSource source)
 
 
 /*
+ * assign_log_connection_messages: GUC assign_hook for log_connection_messages
+ */
+void
+assign_log_connection_messages(const char *newval, void *extra)
+{
+	Log_connection_messages = *((int *) extra);
+}
+
+ /*
  * MISCELLANEOUS
  */
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 25b3a781cd..e0f8034fb3 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -327,7 +327,8 @@ auth_failed(Port *port, int status, const char *logdetail)
 /*
  * Sets the authenticated identity for the current user.  The provided string
  * will be stored into MyClientConnectionInfo, alongside the current HBA
- * method in use.  The ID will be logged if log_connections is enabled.
+ * method in use.  The ID will be logged if
+ * log_connection_messages contains the 'authenticated' value.
  *
  * Auth methods should call this routine exactly once, as soon as the user is
  * successfully authenticated, even if they have reasons to know that
@@ -359,7 +360,7 @@ set_authn_id(Port *port, const char *id)
 	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 	MyClientConnectionInfo.auth_method = port->hba->auth_method;
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHENTICATED)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index f92dbc2270..0710168ae3 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -235,7 +235,6 @@ int			PreAuthDelay = 0;
 int			AuthenticationTimeout = 60;
 
 bool		log_hostname;		/* for ps display and logging */
-bool		Log_connections = false;
 bool		Db_user_namespace = false;
 
 bool		enable_bonjour = false;
@@ -4343,8 +4342,8 @@ BackendInitialize(Port *port)
 	port->remote_host = strdup(remote_host);
 	port->remote_port = strdup(remote_port);
 
-	/* And now we can issue the Log_connections message, if wanted */
-	if (Log_connections)
+	/* And now we can issue the "connection received" message, if wanted */
+	if (Log_connection_messages & LOG_CONNECTION_RECEIVED)
 	{
 		if (remote_port[0])
 			ereport(LOG,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 470b734e9e..0c95fbc3bd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -83,8 +83,8 @@ const char *debug_query_string; /* client-supplied query string */
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
 CommandDest whereToSendOutput = DestDebug;
 
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+int			Log_connection_messages = 0;
 
 int			log_statement = LOGSTMT_NONE;
 
@@ -3596,8 +3596,7 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 
 	if (debug_flag >= 1 && context == PGC_POSTMASTER)
 	{
-		SetConfigOption("log_connections", "true", context, source);
-		SetConfigOption("log_disconnections", "true", context, source);
+		SetConfigOption("log_connection_messages", "all", context, source);
 	}
 	if (debug_flag >= 2)
 		SetConfigOption("log_statement", "all", context, source);
@@ -4167,9 +4166,9 @@ PostgresMain(const char *dbname, const char *username)
 
 	/*
 	 * Also set up handler to log session end; we have to wait till now to be
-	 * sure Log_disconnections has its final value.
+	 * sure Log_connection_messages has its final value.
 	 */
-	if (IsUnderPostmaster && Log_disconnections)
+	if (IsUnderPostmaster && (Log_connection_messages & LOG_CONNECTION_DISCONNECTED))
 		on_proc_exit(log_disconnections, 0);
 
 	pgstat_report_connect(MyDatabaseId);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 2f07ca7a0e..a780c19349 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -250,7 +250,7 @@ PerformAuthentication(Port *port)
 	 */
 	disable_timeout(STATEMENT_TIMEOUT, false);
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHORIZED)
 	{
 		StringInfoData logmsg;
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index c5a95f5dcc..7abe27e602 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -87,7 +87,6 @@
 #endif
 
 /* XXX these should appear in other modules' header files */
-extern bool Log_disconnections;
 extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
@@ -585,6 +584,7 @@ static char *recovery_target_string;
 static char *recovery_target_xid_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
+static char *log_connection_messages_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1182,24 +1182,6 @@ struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs each successful connection."),
-			NULL
-		},
-		&Log_connections,
-		false,
-		NULL, NULL, NULL
-	},
-	{
-		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs end of a session, including duration."),
-			NULL
-		},
-		&Log_disconnections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
 			gettext_noop("Logs each replication command."),
@@ -4163,6 +4145,16 @@ struct config_string ConfigureNamesString[] =
 		check_session_authorization, assign_session_authorization, NULL
 	},
 
+	{
+		{"log_connection_messages", PGC_SU_BACKEND, LOGGING_WHAT,
+			gettext_noop("Lists connection stages to log."),
+			NULL,
+			GUC_LIST_INPUT
+		},
+		&log_connection_messages_string,
+		"",
+		check_log_connection_messages, assign_log_connection_messages, NULL
+	},
 	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..23c6705cf2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -21,7 +21,7 @@
 # require a server shutdown and restart to take effect.
 #
 # Any parameter can also be given as a command-line option to the server, e.g.,
-# "postgres -c log_connections=on".  Some parameters can be changed at run time
+# "postgres -c log_connection_messages=all".  Some parameters can be changed at run time
 # with the "SET" SQL command.
 #
 # Memory units:  B  = bytes            Time units:  us  = microseconds
@@ -553,8 +553,7 @@
 					# actions running at least this number
 					# of milliseconds.
 #log_checkpoints = on
-#log_connections = off
-#log_disconnections = off
+#log_connection_messages = ''
 #log_duration = off
 #log_error_verbosity = default		# terse, default, or verbose messages
 #log_hostname = off
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..9f888f4aef 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -576,4 +576,13 @@ extern Datum Float8GetDatum(float8 X);
 #define NON_EXEC_STATIC static
 #endif
 
+extern PGDLLIMPORT int	Log_connection_messages;
+
+/* Bitmap for logging connection messages */
+#define LOG_CONNECTION_RECEIVED		 (1 << 0)
+#define LOG_CONNECTION_AUTHENTICATED (1 << 1)
+#define LOG_CONNECTION_AUTHORIZED	 (1 << 2)
+#define LOG_CONNECTION_DISCONNECTED  (1 << 3)
+#define LOG_CONNECTION_ALL			 0xFFFF
+
 #endif							/* POSTGRES_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 3b3889c58c..e1ad7e1c50 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -25,7 +25,6 @@ extern PGDLLIMPORT char *ListenAddresses;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
-extern PGDLLIMPORT bool Log_connections;
 extern PGDLLIMPORT bool log_hostname;
 extern PGDLLIMPORT bool enable_bonjour;
 extern PGDLLIMPORT char *bonjour_name;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index aeb3663071..9690c13ab6 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -67,6 +67,8 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
 extern void assign_locale_numeric(const char *newval, void *extra);
 extern bool check_locale_time(char **newval, void **extra, GucSource source);
 extern void assign_locale_time(const char *newval, void *extra);
+extern bool check_log_connection_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_connection_messages(const char *newval, void *extra);
 extern bool check_log_destination(char **newval, void **extra,
 								  GucSource source);
 extern void assign_log_destination(const char *newval, void *extra);
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index a2fde1408b..30581ea5d7 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -63,7 +63,7 @@ sub test_conn
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Create 3 roles with different password methods for each one. The same
diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl
index a6be651ea7..4ad97da2f4 100644
--- a/src/test/authentication/t/003_peer.pl
+++ b/src/test/authentication/t/003_peer.pl
@@ -82,7 +82,7 @@ sub find_in_log
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Set pg_hba.conf with the peer authentication.
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index d610ce63ab..8b1b87e554 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -180,7 +180,7 @@ $node->append_conf(
 	'postgresql.conf', qq{
 listen_addresses = '$hostaddr'
 krb_server_keyfile = '$keytab'
-log_connections = on
+log_connection_messages = all
 lc_messages = 'C'
 });
 $node->start;
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index f3ed806ec2..6e6dbb830a 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -48,7 +48,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl
index bcd4aa2b74..e274dbf928 100644
--- a/src/test/ldap/t/002_bindpasswd.pl
+++ b/src/test/ldap/t/002_bindpasswd.pl
@@ -44,7 +44,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl
index 92e7b367df..ff0bb3daf0 100644
--- a/src/test/recovery/t/013_crash_restart.pl
+++ b/src/test/recovery/t/013_crash_restart.pl
@@ -27,7 +27,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET restart_after_crash = 1;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   SELECT pg_reload_conf();]);
 
 # Run psql, keeping session alive, so we have an alive backend to kill.
diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl
index 03c8efdfb5..2a466da25b 100644
--- a/src/test/recovery/t/022_crash_temp_files.pl
+++ b/src/test/recovery/t/022_crash_temp_files.pl
@@ -26,7 +26,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET remove_temp_files_after_crash = on;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   ALTER SYSTEM SET work_mem = '64kB';
 				   ALTER SYSTEM SET restart_after_crash = on;
 				   SELECT pg_reload_conf();]);
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index 92ec510037..f4ac72ef62 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -11,7 +11,7 @@ $node_primary->init(allows_streaming => 1);
 $node_primary->append_conf(
 	'postgresql.conf', q[
 allow_in_place_tablespaces = true
-log_connections=on
+log_connection_messages=all
 # to avoid "repairing" corruption
 full_page_writes=off
 log_min_messages=debug2
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index b6344b936a..6cd5a46921 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -193,7 +193,7 @@ sub configure_test_server_for_ssl
 	# enable logging etc.
 	open my $conf, '>>', "$pgdata/postgresql.conf";
 	print $conf "fsync=off\n";
-	print $conf "log_connections=on\n";
+	print $conf "log_connection_messages=all\n";
 	print $conf "log_hostname=on\n";
 	print $conf "listen_addresses='$serverhost'\n";
 	print $conf "log_statement=all\n";
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
index d8faa9c26c..7596d515aa 100644
--- a/src/tools/ci/pg_ci_base.conf
+++ b/src/tools/ci/pg_ci_base.conf
@@ -8,7 +8,6 @@ max_prepared_transactions = 10
 # Settings that make logs more useful
 log_autovacuum_min_duration = 0
 log_checkpoints = true
-log_connections = true
-log_disconnections = true
+log_connection_messages = all
 log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
 log_lock_waits = true
-- 
2.34.1

#12Jacob Champion
jchampion@timescale.com
In reply to: Sergey Dudoladov (#11)
Re: Introduce "log_connection_stages" setting.

On 2/1/23 11:59, Sergey Dudoladov wrote:

Justin, thank you for the fast review. The new version is attached.

This is looking very good. One bigger comment:

+	myextra = (int *) guc_malloc(ERROR, sizeof(int));
+	*myextra = newlogconnect;

If I've understood Tom correctly in [1]/messages/by-id/2012342.1658356951@sss.pgh.pa.us, both of these guc_mallocs
should be using a loglevel less than ERROR, to avoid forcing a
postmaster exit when out of memory. (I used WARNING in that thread
instead, which seemed to be acceptable.)

And a couple of nitpicks:

+ Causes the specified stages of each connection attempt to the server to be logged. Example: <literal>authorized,disconnected</literal>.

Long line; should be rewrapped.

+       else {                                                                   
+           GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);                  
+           GUC_check_errmsg("invalid value '%s'", stage);                       
+           GUC_check_errdetail("Valid values to use in the list are 'all', 'received', 'authenticate
+           " If 'all' is present, it must be the only value in the list."); 

I think the errmsg here should reuse the standard message format
invalid value for parameter "%s": "%s"
both for consistency and ease of translation.

Thanks!
--Jacob

[1]: /messages/by-id/2012342.1658356951@sss.pgh.pa.us

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Jacob Champion (#12)
Re: Introduce "log_connection_stages" setting.

Jacob Champion <jchampion@timescale.com> writes:

This is looking very good. One bigger comment:

+	myextra = (int *) guc_malloc(ERROR, sizeof(int));
+	*myextra = newlogconnect;

If I've understood Tom correctly in [1], both of these guc_mallocs
should be using a loglevel less than ERROR, to avoid forcing a
postmaster exit when out of memory. (I used WARNING in that thread
instead, which seemed to be acceptable.)

Actually, preferred practice is as seen in e.g. check_datestyle:

myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
if (!myextra)
return false;
myextra[0] = newDateStyle;
myextra[1] = newDateOrder;
*extra = (void *) myextra;

which gives the guc.c functions an opportunity to manage the
failure.

A quick grep shows that there are existing check functions that
did not get that memo, e.g. check_recovery_target_lsn.
We ought to clean them up.

This is, of course, not super important unless you're allocating
something quite large; the odds of going OOM in the postmaster
should be pretty small.

regards, tom lane

#14Jacob Champion
jchampion@timescale.com
In reply to: Tom Lane (#13)
Re: Introduce "log_connection_stages" setting.

On 3/2/23 14:56, Tom Lane wrote:

Jacob Champion <jchampion@timescale.com> writes:

If I've understood Tom correctly in [1], both of these guc_mallocs
should be using a loglevel less than ERROR, to avoid forcing a
postmaster exit when out of memory. (I used WARNING in that thread
instead, which seemed to be acceptable.)

Actually, preferred practice is as seen in e.g. check_datestyle:

myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
if (!myextra)
return false;
myextra[0] = newDateStyle;
myextra[1] = newDateOrder;
*extra = (void *) myextra;

which gives the guc.c functions an opportunity to manage the
failure.

Ah, thanks for the correction. (My guc_strdup(WARNING, ...) calls may
need to be cleaned up too, then.)

--Jacob

#15Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Jacob Champion (#14)
1 attachment(s)
Re: Introduce "log_connection_stages" setting.

Hello,

I have attached the fourth version of the patch.

Regards,
Sergey.

Attachments:

0004-Introduce-log_connection_messages.patchtext/x-patch; charset=US-ASCII; name=0004-Introduce-log_connection_messages.patchDownload
From 0303df03496ec9aafd6e69fa798177ad06a85bee Mon Sep 17 00:00:00 2001
From: Sergey Dudoladov <sergey.dudoladov@gmail.com>
Date: Tue, 8 Nov 2022 18:56:26 +0100
Subject: [PATCH] Introduce 'log_connection_messages'

This patch removes 'log_connections' and 'log_disconnections'
in favor of 'log_connection_messages', thereby introducing incompatibility

Author: Sergey Dudoladov

Reviewed-by: Justin Pryzby, Jacob Champion

Discussion:
https://www.postgresql.org/message-id/flat/CAA8Fd-qCB96uwfgMKrzfNs32mqqysi53yZFNVaRNJ6xDthZEgA%40mail.gmail.com
---
 doc/src/sgml/config.sgml                      | 90 +++++++++++++------
 src/backend/commands/variable.c               | 81 +++++++++++++++++
 src/backend/libpq/auth.c                      |  5 +-
 src/backend/postmaster/postmaster.c           |  5 +-
 src/backend/tcop/postgres.c                   | 11 ++-
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_tables.c           | 30 +++----
 src/backend/utils/misc/postgresql.conf.sample |  5 +-
 src/include/postgres.h                        |  9 ++
 src/include/postmaster/postmaster.h           |  1 -
 src/include/utils/guc_hooks.h                 |  2 +
 src/test/authentication/t/001_password.pl     |  2 +-
 src/test/authentication/t/003_peer.pl         |  2 +-
 src/test/kerberos/t/001_auth.pl               |  2 +-
 src/test/ldap/t/001_auth.pl                   |  2 +-
 src/test/ldap/t/002_bindpasswd.pl             |  2 +-
 src/test/recovery/t/013_crash_restart.pl      |  2 +-
 src/test/recovery/t/022_crash_temp_files.pl   |  2 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |  2 +-
 src/test/ssl/t/SSL/Server.pm                  |  2 +-
 src/tools/ci/pg_ci_base.conf                  |  3 +-
 21 files changed, 187 insertions(+), 75 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2073bafa1f..42c0a1f3db 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -140,7 +140,7 @@
      An example of what this file might look like is:
 <programlisting>
 # This is a comment
-log_connections = yes
+log_connection_messages = all
 log_destination = 'syslog'
 search_path = '"$user", public'
 shared_buffers = 128MB
@@ -335,7 +335,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
        passed to the <command>postgres</command> command via the
        <option>-c</option> command-line parameter.  For example,
 <programlisting>
-postgres -c log_connections=yes -c log_destination='syslog'
+postgres -c log_connection_messages=all -c log_destination='syslog'
 </programlisting>
        Settings provided in this way override those set via
        <filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@@ -7124,23 +7124,75 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-connections" xreflabel="log_connections">
-      <term><varname>log_connections</varname> (<type>boolean</type>)
+     <varlistentry id="guc-log-connection-messages" xreflabel="log_connection_messages">
+      <term><varname>log_connection_messages</varname> (<type>string</type>)
       <indexterm>
-       <primary><varname>log_connections</varname> configuration parameter</primary>
+       <primary><varname>log_connection_messages</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        Causes each attempted connection to the server to be logged,
-        as well as successful completion of both client authentication (if
-        necessary) and authorization.
+        Causes the specified stages of each connection attempt to be logged.
+        Example: <literal>authorized,disconnected</literal>.
+        The default is the empty string, which means that nothing is logged.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this parameter at session start,
         and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
        </para>
 
+    <table id="connection-messages">
+     <title>Connection messages</title>
+     <tgroup cols="2">
+      <colspec colname="col1" colwidth="1*"/>
+      <colspec colname="col2" colwidth="2*"/>
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal>received</literal></entry>
+        <entry>Logs receipt of a connection. At this point a connection has been
+        received, but no further work has been done: PostgreSQL is about to start
+        interacting with a client.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity used by an authentication method
+        to identify a user. In most cases, the identity string matches the
+        PostgreSQL username, but some third-party authentication methods may
+        alter the original user identifier before the server stores it. Failed
+        authentication is always logged regardless of the value of this setting.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>authorized</literal></entry>
+        <entry>Logs successful completion of authorization. At this point the
+        connection has been fully established.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>disconnected</literal></entry>
+        <entry>Logs session termination. The log output provides information
+        similar to <literal>authorized</literal>, plus the duration of the session.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>all</literal></entry>
+        <entry>A convenience alias. If present, it must be the only value in the
+        list.</entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+
        <note>
         <para>
          Some client programs, like <application>psql</application>, attempt
@@ -7152,26 +7204,6 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-disconnections" xreflabel="log_disconnections">
-      <term><varname>log_disconnections</varname> (<type>boolean</type>)
-      <indexterm>
-       <primary><varname>log_disconnections</varname> configuration parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Causes session terminations to be logged.  The log output
-        provides information similar to <varname>log_connections</varname>,
-        plus the duration of the session.
-        Only superusers and users with the appropriate <literal>SET</literal>
-        privilege can change this parameter at session start,
-        and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-
      <varlistentry id="guc-log-duration" xreflabel="log_duration">
       <term><varname>log_duration</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index f0f2e07655..d7d01fb495 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -993,6 +993,78 @@ show_role(void)
 }
 
 
+/* check_hook: validate new log_connection_messages value
+ *
+ * The implementation follows the check_log_destination hook.
+ */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	int			newlogconnect = 0;
+	int			*myextra;
+
+	if (pg_strcasecmp(*newval, "all") == 0)
+	{
+		newlogconnect |= LOG_CONNECTION_ALL;
+		myextra = (int *) guc_malloc(LOG, sizeof(int));
+		if (!myextra)
+			return false;
+		*myextra = newlogconnect;
+		*extra = (void *) myextra;
+		return true;
+	}
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/* Parse string into list of identifiers */
+	/* We rely on SplitIdentifierString to downcase and strip whitespace */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	foreach(l, namelist)
+	{
+		char		*stage = (char *) lfirst(l);
+		if (pg_strcasecmp(stage, "received") == 0)
+			newlogconnect |= LOG_CONNECTION_RECEIVED;
+		else if (pg_strcasecmp(stage, "authenticated") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHENTICATED;
+		else if (pg_strcasecmp(stage, "authorized") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHORIZED;
+		else if (pg_strcasecmp(stage, "disconnected") == 0)
+			newlogconnect |= LOG_CONNECTION_DISCONNECTED;
+		else {
+			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+			GUC_check_errmsg("invalid value for parameter '%s'", stage);
+			GUC_check_errdetail("Valid values to use in the list are 'all', 'received', 'authenticated', 'authorized', and 'disconnected'."
+			" If 'all' is present, it must be the only value in the list.");
+			pfree(rawname);
+			list_free(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	myextra = (int *) guc_malloc(LOG, sizeof(int));
+	if (!myextra)
+		return false;
+	*myextra = newlogconnect;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
 /*
  * PATH VARIABLES
  *
@@ -1015,6 +1087,15 @@ check_canonical_path(char **newval, void **extra, GucSource source)
 
 
 /*
+ * assign_log_connection_messages: GUC assign_hook for log_connection_messages
+ */
+void
+assign_log_connection_messages(const char *newval, void *extra)
+{
+	Log_connection_messages = *((int *) extra);
+}
+
+ /*
  * MISCELLANEOUS
  */
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index a1a826e37f..671c901814 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -332,7 +332,8 @@ auth_failed(Port *port, int status, const char *logdetail)
 /*
  * Sets the authenticated identity for the current user.  The provided string
  * will be stored into MyClientConnectionInfo, alongside the current HBA
- * method in use.  The ID will be logged if log_connections is enabled.
+ * method in use.  The ID will be logged if
+ * log_connection_messages contains the 'authenticated' value.
  *
  * Auth methods should call this routine exactly once, as soon as the user is
  * successfully authenticated, even if they have reasons to know that
@@ -364,7 +365,7 @@ set_authn_id(Port *port, const char *id)
 	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 	MyClientConnectionInfo.auth_method = port->hba->auth_method;
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHENTICATED)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 4c49393fc5..7e79736feb 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -235,7 +235,6 @@ int			PreAuthDelay = 0;
 int			AuthenticationTimeout = 60;
 
 bool		log_hostname;		/* for ps display and logging */
-bool		Log_connections = false;
 bool		Db_user_namespace = false;
 
 bool		enable_bonjour = false;
@@ -4343,8 +4342,8 @@ BackendInitialize(Port *port)
 	port->remote_host = strdup(remote_host);
 	port->remote_port = strdup(remote_port);
 
-	/* And now we can issue the Log_connections message, if wanted */
-	if (Log_connections)
+	/* And now we can issue the "connection received" message, if wanted */
+	if (Log_connection_messages & LOG_CONNECTION_RECEIVED)
 	{
 		if (remote_port[0])
 			ereport(LOG,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 01b6cc1f7d..c424e6ee7e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -87,8 +87,8 @@ const char *debug_query_string; /* client-supplied query string */
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
 CommandDest whereToSendOutput = DestDebug;
 
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+int			Log_connection_messages = 0;
 
 int			log_statement = LOGSTMT_NONE;
 
@@ -3650,8 +3650,7 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 
 	if (debug_flag >= 1 && context == PGC_POSTMASTER)
 	{
-		SetConfigOption("log_connections", "true", context, source);
-		SetConfigOption("log_disconnections", "true", context, source);
+		SetConfigOption("log_connection_messages", "all", context, source);
 	}
 	if (debug_flag >= 2)
 		SetConfigOption("log_statement", "all", context, source);
@@ -4221,9 +4220,9 @@ PostgresMain(const char *dbname, const char *username)
 
 	/*
 	 * Also set up handler to log session end; we have to wait till now to be
-	 * sure Log_disconnections has its final value.
+	 * sure Log_connection_messages has its final value.
 	 */
-	if (IsUnderPostmaster && Log_disconnections)
+	if (IsUnderPostmaster && (Log_connection_messages & LOG_CONNECTION_DISCONNECTED))
 		on_proc_exit(log_disconnections, 0);
 
 	pgstat_report_connect(MyDatabaseId);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 53420f4974..93eeb631fe 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -250,7 +250,7 @@ PerformAuthentication(Port *port)
 	 */
 	disable_timeout(STATEMENT_TIMEOUT, false);
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHORIZED)
 	{
 		StringInfoData logmsg;
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 8abf9bb644..952b1888de 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -90,7 +90,6 @@
 #endif
 
 /* XXX these should appear in other modules' header files */
-extern bool Log_disconnections;
 extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
@@ -605,6 +604,7 @@ static char *recovery_target_string;
 static char *recovery_target_xid_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
+static char *log_connection_messages_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1202,24 +1202,6 @@ struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs each successful connection."),
-			NULL
-		},
-		&Log_connections,
-		false,
-		NULL, NULL, NULL
-	},
-	{
-		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs end of a session, including duration."),
-			NULL
-		},
-		&Log_disconnections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
 			gettext_noop("Logs each replication command."),
@@ -4206,6 +4188,16 @@ struct config_string ConfigureNamesString[] =
 		check_session_authorization, assign_session_authorization, NULL
 	},
 
+	{
+		{"log_connection_messages", PGC_SU_BACKEND, LOGGING_WHAT,
+			gettext_noop("Lists connection stages to log."),
+			NULL,
+			GUC_LIST_INPUT
+		},
+		&log_connection_messages_string,
+		"",
+		check_log_connection_messages, assign_log_connection_messages, NULL
+	},
 	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 11a4cf6cfb..460d79b3a3 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -21,7 +21,7 @@
 # require a server shutdown and restart to take effect.
 #
 # Any parameter can also be given as a command-line option to the server, e.g.,
-# "postgres -c log_connections=on".  Some parameters can be changed at run time
+# "postgres -c log_connection_messages=all".  Some parameters can be changed at run time
 # with the "SET" SQL command.
 #
 # Memory units:  B  = bytes            Time units:  us  = microseconds
@@ -557,8 +557,7 @@
 					# actions running at least this number
 					# of milliseconds.
 #log_checkpoints = on
-#log_connections = off
-#log_disconnections = off
+#log_connection_messages = ''
 #log_duration = off
 #log_error_verbosity = default		# terse, default, or verbose messages
 #log_hostname = off
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..9f888f4aef 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -576,4 +576,13 @@ extern Datum Float8GetDatum(float8 X);
 #define NON_EXEC_STATIC static
 #endif
 
+extern PGDLLIMPORT int	Log_connection_messages;
+
+/* Bitmap for logging connection messages */
+#define LOG_CONNECTION_RECEIVED		 (1 << 0)
+#define LOG_CONNECTION_AUTHENTICATED (1 << 1)
+#define LOG_CONNECTION_AUTHORIZED	 (1 << 2)
+#define LOG_CONNECTION_DISCONNECTED  (1 << 3)
+#define LOG_CONNECTION_ALL			 0xFFFF
+
 #endif							/* POSTGRES_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 3b3889c58c..e1ad7e1c50 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -25,7 +25,6 @@ extern PGDLLIMPORT char *ListenAddresses;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
-extern PGDLLIMPORT bool Log_connections;
 extern PGDLLIMPORT bool log_hostname;
 extern PGDLLIMPORT bool enable_bonjour;
 extern PGDLLIMPORT char *bonjour_name;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 2ecb9fc086..ea98ac35f0 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -69,6 +69,8 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
 extern void assign_locale_numeric(const char *newval, void *extra);
 extern bool check_locale_time(char **newval, void **extra, GucSource source);
 extern void assign_locale_time(const char *newval, void *extra);
+extern bool check_log_connection_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_connection_messages(const char *newval, void *extra);
 extern bool check_log_destination(char **newval, void **extra,
 								  GucSource source);
 extern void assign_log_destination(const char *newval, void *extra);
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 0680f8b07c..7474bc8d85 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -63,7 +63,7 @@ sub test_conn
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Create 3 roles with different password methods for each one. The same
diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl
index a6be651ea7..4ad97da2f4 100644
--- a/src/test/authentication/t/003_peer.pl
+++ b/src/test/authentication/t/003_peer.pl
@@ -82,7 +82,7 @@ sub find_in_log
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Set pg_hba.conf with the peer authentication.
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index e2c928349f..8f1dc31fb3 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -216,7 +216,7 @@ $node->append_conf(
 	'postgresql.conf', qq{
 listen_addresses = '$hostaddr'
 krb_server_keyfile = '$keytab'
-log_connections = on
+log_connection_messages = all
 lc_messages = 'C'
 });
 $node->start;
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index 1e027ced01..783b05d952 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -48,7 +48,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl
index bcd4aa2b74..e274dbf928 100644
--- a/src/test/ldap/t/002_bindpasswd.pl
+++ b/src/test/ldap/t/002_bindpasswd.pl
@@ -44,7 +44,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = 'all'\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl
index 92e7b367df..ff0bb3daf0 100644
--- a/src/test/recovery/t/013_crash_restart.pl
+++ b/src/test/recovery/t/013_crash_restart.pl
@@ -27,7 +27,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET restart_after_crash = 1;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   SELECT pg_reload_conf();]);
 
 # Run psql, keeping session alive, so we have an alive backend to kill.
diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl
index 03c8efdfb5..2a466da25b 100644
--- a/src/test/recovery/t/022_crash_temp_files.pl
+++ b/src/test/recovery/t/022_crash_temp_files.pl
@@ -26,7 +26,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET remove_temp_files_after_crash = on;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   ALTER SYSTEM SET work_mem = '64kB';
 				   ALTER SYSTEM SET restart_after_crash = on;
 				   SELECT pg_reload_conf();]);
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index 92ec510037..f4ac72ef62 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -11,7 +11,7 @@ $node_primary->init(allows_streaming => 1);
 $node_primary->append_conf(
 	'postgresql.conf', q[
 allow_in_place_tablespaces = true
-log_connections=on
+log_connection_messages=all
 # to avoid "repairing" corruption
 full_page_writes=off
 log_min_messages=debug2
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index b6344b936a..6cd5a46921 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -193,7 +193,7 @@ sub configure_test_server_for_ssl
 	# enable logging etc.
 	open my $conf, '>>', "$pgdata/postgresql.conf";
 	print $conf "fsync=off\n";
-	print $conf "log_connections=on\n";
+	print $conf "log_connection_messages=all\n";
 	print $conf "log_hostname=on\n";
 	print $conf "listen_addresses='$serverhost'\n";
 	print $conf "log_statement=all\n";
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
index d8faa9c26c..7596d515aa 100644
--- a/src/tools/ci/pg_ci_base.conf
+++ b/src/tools/ci/pg_ci_base.conf
@@ -8,7 +8,6 @@ max_prepared_transactions = 10
 # Settings that make logs more useful
 log_autovacuum_min_duration = 0
 log_checkpoints = true
-log_connections = true
-log_disconnections = true
+log_connection_messages = all
 log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
 log_lock_waits = true
-- 
2.34.1

#16Daniel Gustafsson
daniel@yesql.se
In reply to: Sergey Dudoladov (#15)
Re: Introduce "log_connection_stages" setting.

On 16 May 2023, at 20:51, Sergey Dudoladov <sergey.dudoladov@gmail.com> wrote:

I have attached the fourth version of the patch.

This version fails the ldap_password test on all platforms on pg_ctl failing to start:

[14:48:10.544] --- stdout ---
[14:48:10.544] # executing test in /tmp/cirrus-ci-build/build/testrun/ldap_password_func/001_mutated_bindpasswd group ldap_password_func test 001_mutated_bindpasswd
[14:48:10.544] # waiting for slapd to accept requests...
[14:48:10.544] # setting up PostgreSQL instance
[14:48:10.544] Bail out! pg_ctl start failed
[14:48:10.544] # test failed

Updating src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl with
the new GUC might solve the problem from skimming this.

Please send a fixed version, I'm marking the patch Waiting on Author in the
meantime.

--
Daniel Gustafsson

#17Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#16)
Re: Introduce "log_connection_stages" setting.

On 3 Jul 2023, at 15:57, Daniel Gustafsson <daniel@yesql.se> wrote:

On 16 May 2023, at 20:51, Sergey Dudoladov <sergey.dudoladov@gmail.com> wrote:

I have attached the fourth version of the patch.

This version fails the ldap_password test on all platforms on pg_ctl failing to start:

[14:48:10.544] --- stdout ---
[14:48:10.544] # executing test in /tmp/cirrus-ci-build/build/testrun/ldap_password_func/001_mutated_bindpasswd group ldap_password_func test 001_mutated_bindpasswd
[14:48:10.544] # waiting for slapd to accept requests...
[14:48:10.544] # setting up PostgreSQL instance
[14:48:10.544] Bail out! pg_ctl start failed
[14:48:10.544] # test failed

Updating src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl with
the new GUC might solve the problem from skimming this.

Please send a fixed version, I'm marking the patch Waiting on Author in the
meantime.

With the patch failing tests and the thread stalled with no update I am marking
this returned with feedback. Please feel free to resubmit to a future CF.

--
Daniel Gustafsson

#18Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Daniel Gustafsson (#17)
1 attachment(s)
Re: Introduce "log_connection_stages" setting.

Hello hackers,

here is the new rebased version of the patch.

Regards,
Sergey

Attachments:

0005-Introduce-log_connection_messages.patchtext/x-patch; charset=US-ASCII; name=0005-Introduce-log_connection_messages.patchDownload
From 8a695aacfd9590737a6e10aca8ddf33181806937 Mon Sep 17 00:00:00 2001
From: Sergey Dudoladov <sergey.dudoladov@gmail.com>
Date: Fri, 28 Feb 2025 10:33:11 +0100
Subject: [PATCH] Introduce log_connection_messages

This patch removes 'log_connections' and 'log_disconnections'
in favor of 'log_connection_messages', thereby introducing
incompatibility

Author: Sergey Dudoladov

Reviewed-by: Justin Pryzby, Jacob Champion

Discussion:
https://www.postgresql.org/message-id/flat/CAA8Fd-qCB96uwfgMKrzfNs32mqqysi53yZFNVaRNJ6xDthZEgA%40mail.gmail.com
---
 doc/src/sgml/config.sgml                      | 90 +++++++++++++------
 src/backend/commands/variable.c               | 81 +++++++++++++++++
 src/backend/libpq/auth.c                      |  9 +-
 src/backend/postmaster/postmaster.c           |  1 -
 src/backend/tcop/backend_startup.c            |  4 +-
 src/backend/tcop/postgres.c                   | 11 ++-
 src/backend/utils/init/postinit.c             |  2 +-
 src/backend/utils/misc/guc_tables.c           | 29 +++---
 src/backend/utils/misc/postgresql.conf.sample |  5 +-
 src/include/postgres.h                        |  9 ++
 src/include/postmaster/postmaster.h           |  1 -
 src/include/tcop/tcopprot.h                   |  1 -
 src/include/utils/guc_hooks.h                 |  2 +
 .../libpq/t/005_negotiate_encryption.pl       |  3 +-
 src/test/authentication/t/001_password.pl     |  2 +-
 src/test/authentication/t/003_peer.pl         |  2 +-
 src/test/authentication/t/005_sspi.pl         |  2 +-
 src/test/kerberos/t/001_auth.pl               |  2 +-
 src/test/ldap/t/001_auth.pl                   |  2 +-
 src/test/ldap/t/002_bindpasswd.pl             |  2 +-
 .../t/001_mutated_bindpasswd.pl               |  2 +-
 .../modules/oauth_validator/t/001_server.pl   |  2 +-
 .../modules/oauth_validator/t/002_client.pl   |  2 +-
 .../postmaster/t/002_connection_limits.pl     |  2 +-
 src/test/postmaster/t/003_start_stop.pl       |  2 +-
 src/test/recovery/t/013_crash_restart.pl      |  2 +-
 src/test/recovery/t/022_crash_temp_files.pl   |  2 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |  2 +-
 src/test/recovery/t/037_invalid_database.pl   |  3 +-
 src/test/ssl/t/SSL/Server.pm                  |  2 +-
 src/tools/ci/pg_ci_base.conf                  |  3 +-
 31 files changed, 197 insertions(+), 87 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e55700f35b..0428733463 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -140,7 +140,7 @@
      An example of what this file might look like is:
 <programlisting>
 # This is a comment
-log_connections = yes
+log_connection_messages = all
 log_destination = 'syslog'
 search_path = '"$user", public'
 shared_buffers = 128MB
@@ -337,7 +337,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
        <option>-c name=value</option> command-line parameter, or its equivalent
        <option>--name=value</option> variation.  For example,
 <programlisting>
-postgres -c log_connections=yes --log-destination='syslog'
+postgres -c log_connection_messages=all --log-destination='syslog'
 </programlisting>
        Settings provided in this way override those set via
        <filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@@ -7314,23 +7314,75 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-connections" xreflabel="log_connections">
-      <term><varname>log_connections</varname> (<type>boolean</type>)
+     <varlistentry id="guc-log-connection-messages" xreflabel="log_connection_messages">
+      <term><varname>log_connection_messages</varname> (<type>string</type>)
       <indexterm>
-       <primary><varname>log_connections</varname> configuration parameter</primary>
+       <primary><varname>log_connection_messages</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        Causes each attempted connection to the server to be logged,
-        as well as successful completion of both client authentication (if
-        necessary) and authorization.
+        Causes the specified stages of each connection attempt to be logged.
+        Example: <literal>authorized,disconnected</literal>.
+        The default is the empty string, which means that nothing is logged.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this parameter at session start,
         and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
        </para>
 
+    <table id="connection-messages">
+     <title>Connection messages</title>
+     <tgroup cols="2">
+      <colspec colname="col1" colwidth="1*"/>
+      <colspec colname="col2" colwidth="2*"/>
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal>received</literal></entry>
+        <entry>Logs receipt of a connection. At this point a connection has been
+        received, but no further work has been done: PostgreSQL is about to start
+        interacting with a client.</entry>
+       </row>
+
+       <row>
+        <entry><literal>authenticated</literal></entry>
+        <entry>Logs the original identity used by an authentication method
+        to identify a user. In most cases, the identity string matches the
+        PostgreSQL username, but some third-party authentication methods may
+        alter the original user identifier before the server stores it. Failed
+        authentication is always logged regardless of the value of this setting.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>authorized</literal></entry>
+        <entry>Logs successful completion of authorization. At this point the
+        connection has been fully established.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>disconnected</literal></entry>
+        <entry>Logs session termination. The log output provides information
+        similar to <literal>authorized</literal>, plus the duration of the session.
+        </entry>
+       </row>
+
+       <row>
+        <entry><literal>all</literal></entry>
+        <entry>A convenience alias. If present, it must be the only value in the
+        list.</entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+
        <note>
         <para>
          Some client programs, like <application>psql</application>, attempt
@@ -7342,26 +7394,6 @@ local0.*    /var/log/postgresql
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-log-disconnections" xreflabel="log_disconnections">
-      <term><varname>log_disconnections</varname> (<type>boolean</type>)
-      <indexterm>
-       <primary><varname>log_disconnections</varname> configuration parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Causes session terminations to be logged.  The log output
-        provides information similar to <varname>log_connections</varname>,
-        plus the duration of the session.
-        Only superusers and users with the appropriate <literal>SET</literal>
-        privilege can change this parameter at session start,
-        and it cannot be changed at all within a session.
-        The default is <literal>off</literal>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-
      <varlistentry id="guc-log-duration" xreflabel="log_duration">
       <term><varname>log_duration</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 4ad6e236d6..d1612e7111 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1048,6 +1048,78 @@ show_role(void)
 }
 
 
+/* check_hook: validate new log_connection_messages value
+ *
+ * The implementation follows the check_log_destination hook.
+ */
+bool
+check_log_connection_messages(char **newval, void **extra, GucSource source)
+{
+	char		*rawname;
+	List		*namelist;
+	ListCell	*l;
+	int			newlogconnect = 0;
+	int			*myextra;
+
+	if (pg_strcasecmp(*newval, "all") == 0)
+	{
+		newlogconnect |= LOG_CONNECTION_ALL;
+		myextra = (int *) guc_malloc(LOG, sizeof(int));
+		if (!myextra)
+			return false;
+		*myextra = newlogconnect;
+		*extra = (void *) myextra;
+		return true;
+	}
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/* Parse string into list of identifiers */
+	/* We rely on SplitIdentifierString to downcase and strip whitespace */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	foreach(l, namelist)
+	{
+		char		*stage = (char *) lfirst(l);
+		if (pg_strcasecmp(stage, "received") == 0)
+			newlogconnect |= LOG_CONNECTION_RECEIVED;
+		else if (pg_strcasecmp(stage, "authenticated") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHENTICATED;
+		else if (pg_strcasecmp(stage, "authorized") == 0)
+			newlogconnect |= LOG_CONNECTION_AUTHORIZED;
+		else if (pg_strcasecmp(stage, "disconnected") == 0)
+			newlogconnect |= LOG_CONNECTION_DISCONNECTED;
+		else {
+			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+			GUC_check_errmsg("invalid value for parameter '%s'", stage);
+			GUC_check_errdetail("Valid values to use in the list are 'all', 'received', 'authenticated', 'authorized', and 'disconnected'."
+			" If 'all' is present, it must be the only value in the list.");
+			pfree(rawname);
+			list_free(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	myextra = (int *) guc_malloc(LOG, sizeof(int));
+	if (!myextra)
+		return false;
+	*myextra = newlogconnect;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
 /*
  * PATH VARIABLES
  *
@@ -1070,6 +1142,15 @@ check_canonical_path(char **newval, void **extra, GucSource source)
 
 
 /*
+ * assign_log_connection_messages: GUC assign_hook for log_connection_messages
+ */
+void
+assign_log_connection_messages(const char *newval, void *extra)
+{
+	Log_connection_messages = *((int *) extra);
+}
+
+ /*
  * MISCELLANEOUS
  */
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 81e2f8184e..afe64d20a0 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -317,7 +317,8 @@ auth_failed(Port *port, int status, const char *logdetail)
 /*
  * Sets the authenticated identity for the current user.  The provided string
  * will be stored into MyClientConnectionInfo, alongside the current HBA
- * method in use.  The ID will be logged if log_connections is enabled.
+ * method in use.  The ID will be logged if
+ * log_connection_messages contains the 'authenticated' value.
  *
  * Auth methods should call this routine exactly once, as soon as the user is
  * successfully authenticated, even if they have reasons to know that
@@ -349,7 +350,7 @@ set_authn_id(Port *port, const char *id)
 	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 	MyClientConnectionInfo.auth_method = port->hba->auth_method;
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHENTICATED)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
@@ -633,11 +634,11 @@ ClientAuthentication(Port *port)
 #endif
 	}
 
-	if (Log_connections && status == STATUS_OK &&
+	if (Log_connection_messages && LOG_CONNECTION_AUTHENTICATED && status == STATUS_OK &&
 		!MyClientConnectionInfo.authn_id)
 	{
 		/*
-		 * Normally, if log_connections is set, the call to set_authn_id()
+		 * Normally, if log_connection_messages is set, the call to set_authn_id()
 		 * will log the connection.  However, if that function is never
 		 * called, perhaps because the trust method is in use, then we handle
 		 * the logging here instead.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 5dd3b6a4fd..616cd8be77 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -237,7 +237,6 @@ int			PreAuthDelay = 0;
 int			AuthenticationTimeout = 60;
 
 bool		log_hostname;		/* for ps display and logging */
-bool		Log_connections = false;
 
 bool		enable_bonjour = false;
 char	   *bonjour_name;
diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c
index c70746fa56..10b20cdce3 100644
--- a/src/backend/tcop/backend_startup.c
+++ b/src/backend/tcop/backend_startup.c
@@ -201,8 +201,8 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
 	port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host);
 	port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port);
 
-	/* And now we can issue the Log_connections message, if wanted */
-	if (Log_connections)
+	/* And now we can issue the "connection received" message, if wanted */
+	if (Log_connection_messages & LOG_CONNECTION_RECEIVED)
 	{
 		if (remote_port[0])
 			ereport(LOG,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f2f75aa0f8..7886599904 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -89,8 +89,8 @@ const char *debug_query_string; /* client-supplied query string */
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
 CommandDest whereToSendOutput = DestDebug;
 
-/* flag for logging end of session */
-bool		Log_disconnections = false;
+/* flags for logging information about session state */
+int			Log_connection_messages = 0;
 
 int			log_statement = LOGSTMT_NONE;
 
@@ -3654,8 +3654,7 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 
 	if (debug_flag >= 1 && context == PGC_POSTMASTER)
 	{
-		SetConfigOption("log_connections", "true", context, source);
-		SetConfigOption("log_disconnections", "true", context, source);
+		SetConfigOption("log_connection_messages", "all", context, source);
 	}
 	if (debug_flag >= 2)
 		SetConfigOption("log_statement", "all", context, source);
@@ -4271,9 +4270,9 @@ PostgresMain(const char *dbname, const char *username)
 
 	/*
 	 * Also set up handler to log session end; we have to wait till now to be
-	 * sure Log_disconnections has its final value.
+	 * sure Log_connection_messages has its final value.
 	 */
-	if (IsUnderPostmaster && Log_disconnections)
+	if (IsUnderPostmaster && (Log_connection_messages & LOG_CONNECTION_DISCONNECTED))
 		on_proc_exit(log_disconnections, 0);
 
 	pgstat_report_connect(MyDatabaseId);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 318600d6d0..14637b6911 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -251,7 +251,7 @@ PerformAuthentication(Port *port)
 	 */
 	disable_timeout(STATEMENT_TIMEOUT, false);
 
-	if (Log_connections)
+	if (Log_connection_messages & LOG_CONNECTION_AUTHORIZED)
 	{
 		StringInfoData logmsg;
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index ad25cbb39c..fef1f06eb2 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -616,6 +616,7 @@ static char *recovery_target_string;
 static char *recovery_target_xid_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
+static char *log_connection_messages_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1219,15 +1220,6 @@ struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs each successful connection."),
-			NULL
-		},
-		&Log_connections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"trace_connection_negotiation", PGC_POSTMASTER, DEVELOPER_OPTIONS,
 			gettext_noop("Logs details of pre-authentication connection handshake."),
@@ -1238,15 +1230,6 @@ struct config_bool ConfigureNamesBool[] =
 		false,
 		NULL, NULL, NULL
 	},
-	{
-		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
-			gettext_noop("Logs end of a session, including duration."),
-			NULL
-		},
-		&Log_disconnections,
-		false,
-		NULL, NULL, NULL
-	},
 	{
 		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
 			gettext_noop("Logs each replication command."),
@@ -4467,6 +4450,16 @@ struct config_string ConfigureNamesString[] =
 		check_session_authorization, assign_session_authorization, NULL
 	},
 
+	{
+		{"log_connection_messages", PGC_SU_BACKEND, LOGGING_WHAT,
+			gettext_noop("Lists connection stages to log."),
+			NULL,
+			GUC_LIST_INPUT
+		},
+		&log_connection_messages_string,
+		"",
+		check_log_connection_messages, assign_log_connection_messages, NULL
+	},
 	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 5362ff8051..2ee7a6fd19 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -21,7 +21,7 @@
 # require a server shutdown and restart to take effect.
 #
 # Any parameter can also be given as a command-line option to the server, e.g.,
-# "postgres -c log_connections=on".  Some parameters can be changed at run time
+# "postgres -c log_connection_messages=all".  Some parameters can be changed at run time
 # with the "SET" SQL command.
 #
 # Memory units:  B  = bytes            Time units:  us  = microseconds
@@ -578,8 +578,7 @@
 					# actions running at least this number
 					# of milliseconds.
 #log_checkpoints = on
-#log_connections = off
-#log_disconnections = off
+#log_connection_messages = ''
 #log_duration = off
 #log_error_verbosity = default		# terse, default, or verbose messages
 #log_hostname = off
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a41a66868..3c8eb4b3d5 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -581,4 +581,13 @@ extern Datum Float8GetDatum(float8 X);
 #define NON_EXEC_STATIC static
 #endif
 
+extern PGDLLIMPORT int	Log_connection_messages;
+
+/* Bitmap for logging connection messages */
+#define LOG_CONNECTION_RECEIVED		 (1 << 0)
+#define LOG_CONNECTION_AUTHENTICATED (1 << 1)
+#define LOG_CONNECTION_AUTHORIZED	 (1 << 2)
+#define LOG_CONNECTION_DISCONNECTED  (1 << 3)
+#define LOG_CONNECTION_ALL			 0xFFFF
+
 #endif							/* POSTGRES_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b6a3f275a1..aa786bfacf 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -63,7 +63,6 @@ extern PGDLLIMPORT char *ListenAddresses;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
-extern PGDLLIMPORT bool Log_connections;
 extern PGDLLIMPORT bool log_hostname;
 extern PGDLLIMPORT bool enable_bonjour;
 extern PGDLLIMPORT char *bonjour_name;
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index a62367f779..dde9b7e9b5 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -36,7 +36,6 @@ typedef enum
 	LOGSTMT_ALL,				/* log all statements */
 } LogStmtLevel;
 
-extern PGDLLIMPORT bool Log_disconnections;
 extern PGDLLIMPORT int log_statement;
 
 /* Flags for restrict_nonsystem_relation_kind value */
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 951451a976..2c1c76b9dc 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -71,6 +71,8 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
 extern void assign_locale_numeric(const char *newval, void *extra);
 extern bool check_locale_time(char **newval, void **extra, GucSource source);
 extern void assign_locale_time(const char *newval, void *extra);
+extern bool check_log_connection_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_connection_messages(const char *newval, void *extra);
 extern bool check_log_destination(char **newval, void **extra,
 								  GucSource source);
 extern void assign_log_destination(const char *newval, void *extra);
diff --git a/src/interfaces/libpq/t/005_negotiate_encryption.pl b/src/interfaces/libpq/t/005_negotiate_encryption.pl
index c834fa5149..319541c21d 100644
--- a/src/interfaces/libpq/t/005_negotiate_encryption.pl
+++ b/src/interfaces/libpq/t/005_negotiate_encryption.pl
@@ -107,8 +107,7 @@ $node->append_conf(
 listen_addresses = '$hostaddr'
 
 # Capturing the EVENTS that occur during tests requires these settings
-log_connections = on
-log_disconnections = on
+log_connection_messages = all
 trace_connection_negotiation = on
 lc_messages = 'C'
 });
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 4ce22ccbdf..34ede07889 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -63,7 +63,7 @@ sub test_conn
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # could fail in FIPS mode
diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl
index 69ba73bd2b..030bf0b2b8 100644
--- a/src/test/authentication/t/003_peer.pl
+++ b/src/test/authentication/t/003_peer.pl
@@ -71,7 +71,7 @@ sub test_role
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 # Set pg_hba.conf with the peer authentication.
diff --git a/src/test/authentication/t/005_sspi.pl b/src/test/authentication/t/005_sspi.pl
index b480b70259..fed1054fdf 100644
--- a/src/test/authentication/t/005_sspi.pl
+++ b/src/test/authentication/t/005_sspi.pl
@@ -18,7 +18,7 @@ if (!$windows_os || $use_unix_sockets)
 # Initialize primary node
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 my $huge_pages_status =
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 6748b109de..d355cc1f11 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -65,7 +65,7 @@ $node->append_conf(
 	'postgresql.conf', qq{
 listen_addresses = '$hostaddr'
 krb_server_keyfile = '$krb->{keytab}'
-log_connections = on
+log_connection_messages = all
 lc_messages = 'C'
 });
 $node->start;
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index 352b0fc1fa..601edc74e7 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -47,7 +47,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl
index f8beba2b27..67564b32dd 100644
--- a/src/test/ldap/t/002_bindpasswd.pl
+++ b/src/test/ldap/t/002_bindpasswd.pl
@@ -43,7 +43,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = 'all'\n");
 $node->start;
 
 $node->safe_psql('postgres', 'CREATE USER test0;');
diff --git a/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl b/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
index 9b062e1c80..d42c7a2a67 100644
--- a/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
+++ b/src/test/modules/ldap_password_func/t/001_mutated_bindpasswd.pl
@@ -42,7 +42,7 @@ note "setting up PostgreSQL instance";
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->append_conf('postgresql.conf',
 	"shared_preload_libraries = 'ldap_password_func'");
 $node->start;
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index 6fa59fbeb2..8b4224f0e9 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -43,7 +43,7 @@ if ($ENV{with_python} ne 'yes')
 
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->append_conf('postgresql.conf',
 	"oauth_validator_libraries = 'validator'\n");
 $node->start;
diff --git a/src/test/modules/oauth_validator/t/002_client.pl b/src/test/modules/oauth_validator/t/002_client.pl
index ab83258d73..2be1adeaf9 100644
--- a/src/test/modules/oauth_validator/t/002_client.pl
+++ b/src/test/modules/oauth_validator/t/002_client.pl
@@ -26,7 +26,7 @@ if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\boauth\b/)
 
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
-$node->append_conf('postgresql.conf', "log_connections = on\n");
+$node->append_conf('postgresql.conf', "log_connection_messages = all\n");
 $node->append_conf('postgresql.conf',
 	"oauth_validator_libraries = 'validator'\n");
 $node->start;
diff --git a/src/test/postmaster/t/002_connection_limits.pl b/src/test/postmaster/t/002_connection_limits.pl
index 8cfa6e0ced..d22efe3825 100644
--- a/src/test/postmaster/t/002_connection_limits.pl
+++ b/src/test/postmaster/t/002_connection_limits.pl
@@ -19,7 +19,7 @@ $node->init(
 $node->append_conf('postgresql.conf', "max_connections = 6");
 $node->append_conf('postgresql.conf', "reserved_connections = 2");
 $node->append_conf('postgresql.conf', "superuser_reserved_connections = 1");
-$node->append_conf('postgresql.conf', "log_connections = on");
+$node->append_conf('postgresql.conf', "log_connection_messages = all");
 $node->start;
 
 $node->safe_psql(
diff --git a/src/test/postmaster/t/003_start_stop.pl b/src/test/postmaster/t/003_start_stop.pl
index 036b296f72..b9af84ee12 100644
--- a/src/test/postmaster/t/003_start_stop.pl
+++ b/src/test/postmaster/t/003_start_stop.pl
@@ -29,7 +29,7 @@ $node->append_conf('postgresql.conf', "max_connections = 5");
 $node->append_conf('postgresql.conf', "max_wal_senders = 0");
 $node->append_conf('postgresql.conf', "autovacuum_max_workers = 1");
 $node->append_conf('postgresql.conf', "max_worker_processes = 1");
-$node->append_conf('postgresql.conf', "log_connections = on");
+$node->append_conf('postgresql.conf', "log_connection_messages = all");
 $node->append_conf('postgresql.conf', "log_min_messages = debug2");
 $node->append_conf('postgresql.conf',
 	"authentication_timeout = '$authentication_timeout s'");
diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl
index cd848918d0..edd5275fc3 100644
--- a/src/test/recovery/t/013_crash_restart.pl
+++ b/src/test/recovery/t/013_crash_restart.pl
@@ -27,7 +27,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET restart_after_crash = 1;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   SELECT pg_reload_conf();]);
 
 # Run psql, keeping session alive, so we have an alive backend to kill.
diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl
index 483a416723..bdd1435905 100644
--- a/src/test/recovery/t/022_crash_temp_files.pl
+++ b/src/test/recovery/t/022_crash_temp_files.pl
@@ -26,7 +26,7 @@ $node->start();
 $node->safe_psql(
 	'postgres',
 	q[ALTER SYSTEM SET remove_temp_files_after_crash = on;
-				   ALTER SYSTEM SET log_connections = 1;
+				   ALTER SYSTEM SET log_connection_messages = 'all';
 				   ALTER SYSTEM SET work_mem = '64kB';
 				   ALTER SYSTEM SET restart_after_crash = on;
 				   SELECT pg_reload_conf();]);
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index ddb7223b33..b18846a798 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -14,7 +14,7 @@ $node_primary->init(allows_streaming => 1);
 $node_primary->append_conf(
 	'postgresql.conf', q[
 allow_in_place_tablespaces = true
-log_connections=on
+log_connection_messages=all
 # to avoid "repairing" corruption
 full_page_writes=off
 log_min_messages=debug2
diff --git a/src/test/recovery/t/037_invalid_database.pl b/src/test/recovery/t/037_invalid_database.pl
index bdf3939739..a0572054c8 100644
--- a/src/test/recovery/t/037_invalid_database.pl
+++ b/src/test/recovery/t/037_invalid_database.pl
@@ -15,8 +15,7 @@ $node->append_conf(
 autovacuum = off
 max_prepared_transactions=5
 log_min_duration_statement=0
-log_connections=on
-log_disconnections=on
+log_connection_messages=all
 ));
 
 $node->start;
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index 447469d893..8c10449948 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -200,7 +200,7 @@ sub configure_test_server_for_ssl
 	$node->append_conf(
 		'postgresql.conf', <<EOF
 fsync=off
-log_connections=on
+log_connection_messages=all
 log_hostname=on
 listen_addresses='$serverhost'
 log_statement=all
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
index d8faa9c26c..7596d515aa 100644
--- a/src/tools/ci/pg_ci_base.conf
+++ b/src/tools/ci/pg_ci_base.conf
@@ -8,7 +8,6 @@ max_prepared_transactions = 10
 # Settings that make logs more useful
 log_autovacuum_min_duration = 0
 log_checkpoints = true
-log_connections = true
-log_disconnections = true
+log_connection_messages = all
 log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
 log_lock_waits = true
-- 
2.34.1

#19Bertrand Drouvot
bertranddrouvot.pg@gmail.com
In reply to: Sergey Dudoladov (#18)
Re: Introduce "log_connection_stages" setting.

Hi,

On Fri, Feb 28, 2025 at 10:40:37AM +0100, Sergey Dudoladov wrote:

Hello hackers,

here is the new rebased version of the patch.

Thanks for the patch!

Did not realized that before but FWIW, it looks "very close" to what Melanie is
doing in [1]/messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com (0001 patch). Idea looks similar excepts, among other things, that
log_connections is kept.

[1]: /messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#20Melanie Plageman
melanieplageman@gmail.com
In reply to: Bertrand Drouvot (#19)
Re: Introduce "log_connection_stages" setting.

On Fri, Feb 28, 2025 at 5:04 AM Bertrand Drouvot
<bertranddrouvot.pg@gmail.com> wrote:

Did not realized that before but FWIW, it looks "very close" to what Melanie is
doing in [1] (0001 patch). Idea looks similar excepts, among other things, that
log_connections is kept.

Oh wow, yes, I never saw this thread. I prefer keeping the original
guc name log_connections. I think it makes sense to also include
disconnected as an option in the list and remove that separate guc
log_disconnections. I was thinking, it should be okay to include "all"
in the list with other values and it just sets the value to all.

- Melanie

#21Sergey Dudoladov
sergey.dudoladov@gmail.com
In reply to: Melanie Plageman (#20)
Re: Introduce "log_connection_stages" setting.

Hi all,

I must admit I haven't seen the [1]/messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com either. Thank you for pointing that
out, Bertrand.
I agree the patch from Melanie is very close to this patch.

Melanie, do you think it is realistic to merge
the v8-0001-Modularize-log_connections-output.patch from [1]/messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com during the
current commitfest ?
I need the patch in v18 to remove some hacks I currently use to reduce the
amount of logging.

Regards,
Sergey

[1]: /messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com
/messages/by-id/CAAKRu_asMtUpxDjts1J60batVqLsSGRodMOq4E9ryg1XdO8EZw@mail.gmail.com

#22Melanie Plageman
melanieplageman@gmail.com
In reply to: Sergey Dudoladov (#21)
Re: Introduce "log_connection_stages" setting.

On Mon, Mar 3, 2025 at 5:43 AM Sergey Dudoladov
<sergey.dudoladov@gmail.com> wrote:

I must admit I haven't seen the [1] either. Thank you for pointing that out, Bertrand.
I agree the patch from Melanie is very close to this patch.

Melanie, do you think it is realistic to merge the v8-0001-Modularize-log_connections-output.patch from [1] during the current commitfest ?

I'd certainly like to merge it in 18. The patch itself needs more
work, which I'm planning on doing this week. I think we have a
reasonable amount of consensus on using GUC_LIST_INPUT.

I am less sure about merging log_disconnections into log_connections.
I think it makes sense in principle. But, in 18, when people satisfied
with existing behavior change log_connections from 'on' to 'all' to
comply with the new format, they will get the disconnections messages.

I am still somewhat nervous about merging the patch in general and
people being surprised because they didn't pay attention to the
discussion and now log_connections is different and they would have
-1'd if they'd been paying attention.

- Melanie

#23Melanie Plageman
melanieplageman@gmail.com
In reply to: Melanie Plageman (#22)
Re: Introduce "log_connection_stages" setting.

On Mon, Mar 3, 2025 at 9:40 AM Melanie Plageman
<melanieplageman@gmail.com> wrote:

I'd certainly like to merge it in 18. The patch itself needs more
work, which I'm planning on doing this week. I think we have a
reasonable amount of consensus on using GUC_LIST_INPUT.

I am less sure about merging log_disconnections into log_connections.
I think it makes sense in principle. But, in 18, when people satisfied
with existing behavior change log_connections from 'on' to 'all' to
comply with the new format, they will get the disconnections messages.

I am still somewhat nervous about merging the patch in general and
people being surprised because they didn't pay attention to the
discussion and now log_connections is different and they would have
-1'd if they'd been paying attention.

Okay, I got cold feet today working on this. I actually think we
probably want some kind of guc set (set like union/intersection/etc
not set like assign a value) infrastructure for gucs that can be equal
to any combination of predetermined values. See my musings on this
here [1]/messages/by-id/CAAKRu_a5-7sUP+Q6YD5emQYS1w7ffBDUNf-NMbcxR-dpOdGehw@mail.gmail.com.

It is too late in the cycle for me to get something like that done,
but I am interested in working on it next release. IIRC you did not
deprecate log_connections in your patch, but adding another guc that
is a staged version of it seems not as good as turning log_connections
into a more modular GUC. But I just don't think we want to move
log_connections/disconnections toward a string/list.

- Melanie

[1]: /messages/by-id/CAAKRu_a5-7sUP+Q6YD5emQYS1w7ffBDUNf-NMbcxR-dpOdGehw@mail.gmail.com

#24Melanie Plageman
melanieplageman@gmail.com
In reply to: Melanie Plageman (#23)
Re: Introduce "log_connection_stages" setting.

On Mon, Mar 3, 2025 at 6:43 PM Melanie Plageman
<melanieplageman@gmail.com> wrote:

Okay, I got cold feet today working on this. I actually think we
probably want some kind of guc set (set like union/intersection/etc
not set like assign a value) infrastructure for gucs that can be equal
to any combination of predetermined values.

I ended up with a patch set that I was happy with which used the list
of string GUCs. I committed it in 9219093cab26. It changes
log_connections to a list of strings for the different aspects of
connection setup and supports the most common inputs for the boolean
version of log_connections for backwards compatibility. I did not end
up deprecating log_disconnections since it is rather late in the cycle
and that change could use more visibility. I suggest proposing that at
the beginning of the next release.

Thanks for your interest in this topic.

- Melanie