diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a632cf98ba..ab4a6e6c7b 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3008,6 +3008,25 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-wal-sessioninfo" xreflabel="wal_sessioninfo">
+      <term><varname>wal_sessioninfo</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>wal_sessioninfo</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        When this parameter is <literal>on</literal>, the <productname>PostgreSQL</productname>
+        server will add information about the user's session onto every
+        commit or abort record. This is intended to provide additional information
+        to trace the changes made by specific sessions, allowing more in-depth
+        investigation during security audits or impact assessment of bugs.
+        The default value is <literal>off</literal>.
+        Only superusers can change this setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-wal-init-zero" xreflabel="wal_init_zero">
       <term><varname>wal_init_zero</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
index addd95faec..77af6911e9 100644
--- a/src/backend/access/rmgrdesc/xactdesc.c
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -123,6 +123,20 @@ ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *pars
 
 		data += sizeof(xl_xact_origin);
 	}
+
+	if (parsed->xinfo & XACT_XINFO_HAS_SESSIONINFO)
+	{
+		xl_xact_sessioninfo xl_sessioninfo;
+
+		/* no alignment is guaranteed, so copy onto stack */
+		memcpy(&xl_sessioninfo, data, sizeof(xl_sessioninfo));
+
+		parsed->session_start_time = xl_sessioninfo.session_start_time;
+		parsed->session_pid = xl_sessioninfo.session_pid;
+		parsed->userid = xl_sessioninfo.userid;
+
+		data += sizeof(xl_xact_sessioninfo);
+	}
 }
 
 void
@@ -207,6 +221,20 @@ ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
 
 		data += sizeof(xl_xact_origin);
 	}
+
+	if (parsed->xinfo & XACT_XINFO_HAS_SESSIONINFO)
+	{
+		xl_xact_sessioninfo xl_sessioninfo;
+
+		/* no alignment is guaranteed, so copy onto stack */
+		memcpy(&xl_sessioninfo, data, sizeof(xl_sessioninfo));
+
+		parsed->session_start_time = xl_sessioninfo.session_start_time;
+		parsed->session_pid = xl_sessioninfo.session_pid;
+		parsed->userid = xl_sessioninfo.userid;
+
+		data += sizeof(xl_xact_sessioninfo);
+	}
 }
 
 /*
@@ -310,6 +338,14 @@ xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId
 						 (uint32) parsed.origin_lsn,
 						 timestamptz_to_str(parsed.origin_timestamp));
 	}
+
+	if (parsed.xinfo & XACT_XINFO_HAS_SESSIONINFO)
+	{
+		appendStringInfo(buf, "; session: pid %u, start at %s userid %u",
+						 parsed.session_pid,
+						 timestamptz_to_str(parsed.session_start_time),
+						 parsed.userid);
+	}
 }
 
 static void
@@ -327,6 +363,14 @@ xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
 
 	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+
+	if (parsed.xinfo & XACT_XINFO_HAS_SESSIONINFO)
+	{
+		appendStringInfo(buf, "; session: pid %u, start at %s userid %u",
+						 parsed.session_pid,
+						 timestamptz_to_str(parsed.session_start_time),
+						 parsed.userid);
+	}
 }
 
 static void
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 03c553e7ea..83022e28f8 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -82,6 +82,8 @@ bool		XactDeferrable;
 
 int			synchronous_commit = SYNCHRONOUS_COMMIT_ON;
 
+bool		wal_sessioninfo = false;
+
 /*
  * CheckXidAlive is a xid value pointing to a possibly ongoing (sub)
  * transaction.  Currently, it is used in logical decoding.  It's possible
@@ -5515,6 +5517,7 @@ XactLogCommitRecord(TimestampTz commit_time,
 	xl_xact_invals xl_invals;
 	xl_xact_twophase xl_twophase;
 	xl_xact_origin xl_origin;
+	xl_xact_sessioninfo xl_sessioninfo;
 	uint8		info;
 
 	Assert(CritSectionCount > 0);
@@ -5594,6 +5597,15 @@ XactLogCommitRecord(TimestampTz commit_time,
 		xl_origin.origin_timestamp = replorigin_session_origin_timestamp;
 	}
 
+	if (wal_sessioninfo)
+	{
+		xl_xinfo.xinfo |= XACT_XINFO_HAS_SESSIONINFO;
+
+		xl_sessioninfo.session_start_time = MyStartTimestamp;
+		xl_sessioninfo.session_pid = MyProcPid;
+		xl_sessioninfo.userid = GetUserId();
+	}
+
 	if (xl_xinfo.xinfo != 0)
 		info |= XLOG_XACT_HAS_INFO;
 
@@ -5642,6 +5654,9 @@ XactLogCommitRecord(TimestampTz commit_time,
 	if (xl_xinfo.xinfo & XACT_XINFO_HAS_ORIGIN)
 		XLogRegisterData((char *) (&xl_origin), sizeof(xl_xact_origin));
 
+	if (xl_xinfo.xinfo & XACT_XINFO_HAS_SESSIONINFO)
+		XLogRegisterData((char *) (&xl_sessioninfo), sizeof(xl_xact_sessioninfo));
+
 	/* we allow filtering by xacts */
 	XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
 
@@ -5668,6 +5683,7 @@ XactLogAbortRecord(TimestampTz abort_time,
 	xl_xact_twophase xl_twophase;
 	xl_xact_dbinfo xl_dbinfo;
 	xl_xact_origin xl_origin;
+	xl_xact_sessioninfo xl_sessioninfo;
 
 	uint8		info;
 
@@ -5730,6 +5746,15 @@ XactLogAbortRecord(TimestampTz abort_time,
 		xl_origin.origin_timestamp = replorigin_session_origin_timestamp;
 	}
 
+	if (wal_sessioninfo)
+	{
+		xl_xinfo.xinfo |= XACT_XINFO_HAS_SESSIONINFO;
+
+		xl_sessioninfo.session_start_time = MyStartTimestamp;
+		xl_sessioninfo.session_pid = MyProcPid;
+		xl_sessioninfo.userid = GetUserId();
+	}
+
 	if (xl_xinfo.xinfo != 0)
 		info |= XLOG_XACT_HAS_INFO;
 
@@ -5771,6 +5796,9 @@ XactLogAbortRecord(TimestampTz abort_time,
 	if (xl_xinfo.xinfo & XACT_XINFO_HAS_ORIGIN)
 		XLogRegisterData((char *) (&xl_origin), sizeof(xl_xact_origin));
 
+	if (xl_xinfo.xinfo & XACT_XINFO_HAS_SESSIONINFO)
+		XLogRegisterData((char *) (&xl_sessioninfo), sizeof(xl_xact_sessioninfo));
+
 	if (TransactionIdIsValid(twophase_xid))
 		XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
 
@@ -5870,6 +5898,8 @@ xact_redo_commit(xl_xact_parsed_commit *parsed,
 						   false /* backward */ , false /* WAL */ );
 	}
 
+	/* No action if XACT_INFO_HAS_SESSIONINFO */
+
 	/* Make sure files supposed to be dropped are dropped */
 	if (parsed->nrels > 0)
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..c69754531d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -130,6 +130,7 @@ extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool ignore_invalid_pages;
 extern bool synchronize_seqscans;
+extern bool wal_sessioninfo;
 
 #ifdef TRACE_SYNCSCAN
 extern bool trace_syncscan;
@@ -1269,6 +1270,16 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"wal_sessioninfo", PGC_SUSET, WAL_SETTINGS,
+			gettext_noop("Includes sessionfo onto the WAL records for transaction completion."),
+			NULL
+		},
+		&wal_sessioninfo,
+		true,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"wal_init_zero", PGC_SUSET, WAL_SETTINGS,
 			gettext_noop("Writes zeroes to new WAL files before first use."),
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 7320de345c..533be43950 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -172,6 +172,7 @@ typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
 #define XACT_XINFO_HAS_ORIGIN			(1U << 5)
 #define XACT_XINFO_HAS_AE_LOCKS			(1U << 6)
 #define XACT_XINFO_HAS_GID				(1U << 7)
+#define XACT_XINFO_HAS_SESSIONINFO		(1U << 8)
 
 /*
  * Also stored in xinfo, these indicating a variety of additional actions that
@@ -267,6 +268,13 @@ typedef struct xl_xact_origin
 	TimestampTz origin_timestamp;
 } xl_xact_origin;
 
+typedef struct xl_xact_sessioninfo
+{
+	TimestampTz	session_start_time;
+	int			session_pid;
+	Oid			userid;
+} xl_xact_sessioninfo;
+
 typedef struct xl_xact_commit
 {
 	TimestampTz xact_time;		/* time of commit */
@@ -279,6 +287,7 @@ typedef struct xl_xact_commit
 	/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
 	/* twophase_gid follows if XINFO_HAS_GID. As a null-terminated string. */
 	/* xl_xact_origin follows if XINFO_HAS_ORIGIN, stored unaligned! */
+	/* xl_xact_sessioninfo follows if XINFO_HAS_SESSIONINFO */
 } xl_xact_commit;
 #define MinSizeOfXactCommit (offsetof(xl_xact_commit, xact_time) + sizeof(TimestampTz))
 
@@ -294,6 +303,7 @@ typedef struct xl_xact_abort
 	/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
 	/* twophase_gid follows if XINFO_HAS_GID. As a null-terminated string. */
 	/* xl_xact_origin follows if XINFO_HAS_ORIGIN, stored unaligned! */
+	/* xl_xact_sessioninfo follows if XINFO_HAS_SESSIONINFO */
 } xl_xact_abort;
 #define MinSizeOfXactAbort sizeof(xl_xact_abort)
 
@@ -344,6 +354,10 @@ typedef struct xl_xact_parsed_commit
 
 	XLogRecPtr	origin_lsn;
 	TimestampTz origin_timestamp;
+
+	TimestampTz	session_start_time;
+	int			session_pid;
+	Oid			userid;
 } xl_xact_parsed_commit;
 
 typedef xl_xact_parsed_commit xl_xact_parsed_prepare;
@@ -367,6 +381,10 @@ typedef struct xl_xact_parsed_abort
 
 	XLogRecPtr	origin_lsn;
 	TimestampTz origin_timestamp;
+
+	TimestampTz	session_start_time;
+	int			session_pid;
+	Oid			userid;
 } xl_xact_parsed_abort;
 
 
