diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 1e67da983c..c2b82f064d 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -350,7 +350,9 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 	{
 		const char **keywords;
 		const char **values;
+		StringInfo buf;
 		int			n;
+		int			i;
 
 		/*
 		 * Construct connection params from generic options of ForeignServer
@@ -362,6 +364,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 		n = list_length(server->options) + list_length(user->options) + 4;
 		keywords = (const char **) palloc(n * sizeof(char *));
 		values = (const char **) palloc(n * sizeof(char *));
+		buf = makeStringInfo();
 
 		n = 0;
 		n += ExtractConnectionOptions(server->options,
@@ -385,6 +388,25 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 			n++;
 		}
 
+		/*
+		 * Search application_name and replace it if found.
+		 *
+		 * We search paramters from the end because the later
+		 * one have higher priority.  See also the above comment.
+		 */
+		for (i = n - 1; i >= 0; i--)
+		{
+			if (strcmp(keywords[i], "application_name") == 0)
+			{
+				parse_pgfdw_appname(buf, values[i]);
+				/*
+				 * Note that appname may becomes an empty string
+				 * if an input string has wrong format.
+				 */
+				values[i] = buf->data;
+			}
+		}
+
 		/* Use "postgres_fdw" as fallback_application_name */
 		keywords[n] = "fallback_application_name";
 		values[n] = "postgres_fdw";
@@ -456,6 +478,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 
 		pfree(keywords);
 		pfree(values);
+		pfree(buf->data);
+		pfree(buf);
 	}
 	PG_CATCH();
 	{
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index c7b7db8065..aa9673e398 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10761,3 +10761,33 @@ ERROR:  invalid value for integer option "fetch_size": 100$%$#$#
 CREATE FOREIGN TABLE inv_bsz (c1 int )
 	SERVER loopback OPTIONS (batch_size '100$%$#$#');
 ERROR:  invalid value for integer option "batch_size": 100$%$#$#
+-- ===================================================================
+-- test postgres_fdw.application_name GUC
+-- ===================================================================
+SET debug_discard_caches TO 0;
+-- Some escapes can be used for this GUC.
+SET postgres_fdw.application_name TO '%a%u%d%p%%';
+-- All escape candidates depend on the runtime environment
+-- and it causes some fails for this tests.
+-- Hence we just count number of rows here. It returns a row if works well.
+SELECT 1 FROM postgres_fdw_disconnect_all();
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM ft6 LIMIT 1;
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name = current_setting('application_name') || current_user || current_database() || pg_backend_pid() || '%';
+ count 
+-------
+     1
+(1 row)
+
+--Clean up
+RESET postgres_fdw.application_name;
+RESET debug_discard_caches;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 5bb1af4084..b9124f23ea 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -22,7 +22,6 @@
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/varlena.h"
-
 /*
  * Describes the valid options for objects that this wrapper uses.
  */
@@ -443,6 +442,15 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
 	return extensionOids;
 }
 
+/*
+ * Wrapper function for process_format_string()
+ */
+void
+parse_pgfdw_appname(StringInfo buf, const char *name)
+{
+	process_format_string(buf, name, NULL, 0, "audp%");
+}
+
 /*
  * Module load callback
  */
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 90b72e9ec5..2f036391f0 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -158,6 +158,7 @@ extern int	ExtractConnectionOptions(List *defelems,
 									 const char **values);
 extern List *ExtractExtensionList(const char *extensionsString,
 								  bool warnOnMissing);
+extern void parse_pgfdw_appname(StringInfo buf, const char *name);
 extern char *pgfdw_application_name;
 
 /* in deparse.c */
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 38f4a7837f..c6c10996a0 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3422,3 +3422,20 @@ CREATE FOREIGN TABLE inv_fsz (c1 int )
 -- Invalid batch_size option
 CREATE FOREIGN TABLE inv_bsz (c1 int )
 	SERVER loopback OPTIONS (batch_size '100$%$#$#');
+
+-- ===================================================================
+-- test postgres_fdw.application_name GUC
+-- ===================================================================
+SET debug_discard_caches TO 0;
+-- Some escapes can be used for this GUC.
+SET postgres_fdw.application_name TO '%a%u%d%p%%';
+-- All escape candidates depend on the runtime environment
+-- and it causes some fails for this tests.
+-- Hence we just count number of rows here. It returns a row if works well.
+SELECT 1 FROM postgres_fdw_disconnect_all();
+SELECT 1 FROM ft6 LIMIT 1;
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name = current_setting('application_name') || current_user || current_database() || pg_backend_pid() || '%';
+
+--Clean up
+RESET postgres_fdw.application_name;
+RESET debug_discard_caches;
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 41c952fbe3..cc963b0a25 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -180,6 +180,10 @@ OPTIONS (ADD password_required 'false');
     relationship granted by authentication modes like <literal>peer</literal>
     or <literal>ident</literal> authentication.
    </para>
+   <para>
+    You can use escape sequences for <literal>application_name</literal> even if
+    it is set as a connection option. Please refer the later section.
+   </para>
   </sect3>
 
   <sect3>
@@ -920,7 +924,7 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   <title>Configuration Parameters</title>
 
   <variablelist>
-   <varlistentry>
+   <varlistentry id="guc-pgfdw-application-name" xreflabel="pgfdw_application_name">
     <term>
      <varname>postgres_fdw.application_name</varname> (<type>string</type>)
      <indexterm>
@@ -936,6 +940,53 @@ postgres=# SELECT postgres_fdw_disconnect_all();
       Note that change of this parameter doesn't affect any existing
       connections until they are re-established.
      </para>
+
+     <para>
+      Same as <xref linkend="guc-log-line-prefix"/>, this is a
+      <function>printf</function>-style string. Accepted escapes are
+      bit different from <xref linkend="guc-log-line-prefix"/>,
+      but padding can be used like as it.
+     </para>
+
+     <informaltable>
+      <tgroup cols="2">
+       <thead>
+        <row>
+         <entry>Escape</entry>
+         <entry>Effect</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+         <entry><literal>%a</literal></entry>
+         <entry>Application name</entry>
+        </row>
+        <row>
+         <entry><literal>%u</literal></entry>
+         <entry>Local user name</entry>
+        </row>
+        <row>
+         <entry><literal>%d</literal></entry>
+         <entry>Local database name</entry>
+        </row>
+        <row>
+         <entry><literal>%p</literal></entry>
+         <entry>Local backend process ID</entry>
+        </row>
+        <row>
+         <entry><literal>%%</literal></entry>
+         <entry>Liteal %</entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </informaltable>
+
+     <para>
+     Note that if embedded strings have Non-ASCII,
+     these characters will be replaced with the question marks (<literal>?</literal>).
+     This limitation is caused by <literal>application_name</literal>.
+     </para>
+
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 2af87ee3bd..615e28c16e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -152,12 +152,12 @@ static int	recursion_depth = 0;	/* to detect actual recursion */
  * Saved timeval and buffers for formatted timestamps that might be used by
  * both log_line_prefix and csv logs.
  */
-static struct timeval saved_timeval;
-static bool saved_timeval_set = false;
+struct timeval saved_timeval;
+bool saved_timeval_set = false;
 
 #define FORMATTED_TS_LEN 128
-static char formatted_start_time[FORMATTED_TS_LEN];
-static char formatted_log_time[FORMATTED_TS_LEN];
+char formatted_start_time[FORMATTED_TS_LEN];
+char formatted_log_time[FORMATTED_TS_LEN];
 
 
 /* Macro for checking errordata_stack_depth is reasonable */
@@ -175,9 +175,6 @@ static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
 static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void write_console(const char *line, int len);
-static void setup_formatted_log_time(void);
-static void setup_formatted_start_time(void);
-static const char *process_log_prefix_padding(const char *p, int *padding);
 static void log_line_prefix(StringInfo buf, ErrorData *edata);
 static void write_csvlog(ErrorData *edata);
 static void send_message_to_server_log(ErrorData *edata);
@@ -2291,7 +2288,7 @@ write_console(const char *line, int len)
 /*
  * setup formatted_log_time, for consistent times between CSV and regular logs
  */
-static void
+void
 setup_formatted_log_time(void)
 {
 	pg_time_t	stamp_time;
@@ -2323,7 +2320,7 @@ setup_formatted_log_time(void)
 /*
  * setup formatted_start_time
  */
-static void
+void
 setup_formatted_start_time(void)
 {
 	pg_time_t	stamp_time = (pg_time_t) MyStartTime;
@@ -2338,43 +2335,11 @@ setup_formatted_start_time(void)
 				pg_localtime(&stamp_time, log_timezone));
 }
 
-/*
- * process_log_prefix_padding --- helper function for processing the format
- * string in log_line_prefix
- *
- * Note: This function returns NULL if it finds something which
- * it deems invalid in the format string.
- */
-static const char *
-process_log_prefix_padding(const char *p, int *ppadding)
-{
-	int			paddingsign = 1;
-	int			padding = 0;
-
-	if (*p == '-')
-	{
-		p++;
-
-		if (*p == '\0')			/* Did the buf end in %- ? */
-			return NULL;
-		paddingsign = -1;
-	}
-
-	/* generate an int version of the numerical string */
-	while (*p >= '0' && *p <= '9')
-		padding = padding * 10 + (*p++ - '0');
-
-	/* format is invalid if it ends with the padding number */
-	if (*p == '\0')
-		return NULL;
-
-	padding *= paddingsign;
-	*ppadding = padding;
-	return p;
-}
-
 /*
  * Format tag info for log lines; append to the provided buffer.
+ *
+ * This is also the wrapper function for process_format_string(),
+ * but this counts log_line_number.
  */
 static void
 log_line_prefix(StringInfo buf, ErrorData *edata)
@@ -2384,8 +2349,6 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
 
 	/* has counter been reset in current process? */
 	static int	log_my_pid = 0;
-	int			padding;
-	const char *p;
 
 	/*
 	 * This is one of the few places where we'd rather not inherit a static
@@ -2404,329 +2367,7 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
 	if (Log_line_prefix == NULL)
 		return;					/* in case guc hasn't run yet */
 
-	for (p = Log_line_prefix; *p != '\0'; p++)
-	{
-		if (*p != '%')
-		{
-			/* literal char, just copy */
-			appendStringInfoChar(buf, *p);
-			continue;
-		}
-
-		/* must be a '%', so skip to the next char */
-		p++;
-		if (*p == '\0')
-			break;				/* format error - ignore it */
-		else if (*p == '%')
-		{
-			/* string contains %% */
-			appendStringInfoChar(buf, '%');
-			continue;
-		}
-
-
-		/*
-		 * Process any formatting which may exist after the '%'.  Note that
-		 * process_log_prefix_padding moves p past the padding number if it
-		 * exists.
-		 *
-		 * Note: Since only '-', '0' to '9' are valid formatting characters we
-		 * can do a quick check here to pre-check for formatting. If the char
-		 * is not formatting then we can skip a useless function call.
-		 *
-		 * Further note: At least on some platforms, passing %*s rather than
-		 * %s to appendStringInfo() is substantially slower, so many of the
-		 * cases below avoid doing that unless non-zero padding is in fact
-		 * specified.
-		 */
-		if (*p > '9')
-			padding = 0;
-		else if ((p = process_log_prefix_padding(p, &padding)) == NULL)
-			break;
-
-		/* process the option */
-		switch (*p)
-		{
-			case 'a':
-				if (MyProcPort)
-				{
-					const char *appname = application_name;
-
-					if (appname == NULL || *appname == '\0')
-						appname = _("[unknown]");
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, appname);
-					else
-						appendStringInfoString(buf, appname);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-
-				break;
-			case 'b':
-				{
-					const char *backend_type_str;
-
-					if (MyProcPid == PostmasterPid)
-						backend_type_str = "postmaster";
-					else if (MyBackendType == B_BG_WORKER)
-						backend_type_str = MyBgworkerEntry->bgw_type;
-					else
-						backend_type_str = GetBackendTypeDesc(MyBackendType);
-
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, backend_type_str);
-					else
-						appendStringInfoString(buf, backend_type_str);
-					break;
-				}
-			case 'u':
-				if (MyProcPort)
-				{
-					const char *username = MyProcPort->user_name;
-
-					if (username == NULL || *username == '\0')
-						username = _("[unknown]");
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, username);
-					else
-						appendStringInfoString(buf, username);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'd':
-				if (MyProcPort)
-				{
-					const char *dbname = MyProcPort->database_name;
-
-					if (dbname == NULL || *dbname == '\0')
-						dbname = _("[unknown]");
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, dbname);
-					else
-						appendStringInfoString(buf, dbname);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'c':
-				if (padding != 0)
-				{
-					char		strfbuf[128];
-
-					snprintf(strfbuf, sizeof(strfbuf) - 1, "%lx.%x",
-							 (long) (MyStartTime), MyProcPid);
-					appendStringInfo(buf, "%*s", padding, strfbuf);
-				}
-				else
-					appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid);
-				break;
-			case 'p':
-				if (padding != 0)
-					appendStringInfo(buf, "%*d", padding, MyProcPid);
-				else
-					appendStringInfo(buf, "%d", MyProcPid);
-				break;
-
-			case 'P':
-				if (MyProc)
-				{
-					PGPROC	   *leader = MyProc->lockGroupLeader;
-
-					/*
-					 * Show the leader only for active parallel workers. This
-					 * leaves out the leader of a parallel group.
-					 */
-					if (leader == NULL || leader->pid == MyProcPid)
-						appendStringInfoSpaces(buf,
-											   padding > 0 ? padding : -padding);
-					else if (padding != 0)
-						appendStringInfo(buf, "%*d", padding, leader->pid);
-					else
-						appendStringInfo(buf, "%d", leader->pid);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-
-			case 'l':
-				if (padding != 0)
-					appendStringInfo(buf, "%*ld", padding, log_line_number);
-				else
-					appendStringInfo(buf, "%ld", log_line_number);
-				break;
-			case 'm':
-				setup_formatted_log_time();
-				if (padding != 0)
-					appendStringInfo(buf, "%*s", padding, formatted_log_time);
-				else
-					appendStringInfoString(buf, formatted_log_time);
-				break;
-			case 't':
-				{
-					pg_time_t	stamp_time = (pg_time_t) time(NULL);
-					char		strfbuf[128];
-
-					pg_strftime(strfbuf, sizeof(strfbuf),
-								"%Y-%m-%d %H:%M:%S %Z",
-								pg_localtime(&stamp_time, log_timezone));
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, strfbuf);
-					else
-						appendStringInfoString(buf, strfbuf);
-				}
-				break;
-			case 'n':
-				{
-					char		strfbuf[128];
-
-					if (!saved_timeval_set)
-					{
-						gettimeofday(&saved_timeval, NULL);
-						saved_timeval_set = true;
-					}
-
-					snprintf(strfbuf, sizeof(strfbuf), "%ld.%03d",
-							 (long) saved_timeval.tv_sec,
-							 (int) (saved_timeval.tv_usec / 1000));
-
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, strfbuf);
-					else
-						appendStringInfoString(buf, strfbuf);
-				}
-				break;
-			case 's':
-				if (formatted_start_time[0] == '\0')
-					setup_formatted_start_time();
-				if (padding != 0)
-					appendStringInfo(buf, "%*s", padding, formatted_start_time);
-				else
-					appendStringInfoString(buf, formatted_start_time);
-				break;
-			case 'i':
-				if (MyProcPort)
-				{
-					const char *psdisp;
-					int			displen;
-
-					psdisp = get_ps_display(&displen);
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, psdisp);
-					else
-						appendBinaryStringInfo(buf, psdisp, displen);
-
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'r':
-				if (MyProcPort && MyProcPort->remote_host)
-				{
-					if (padding != 0)
-					{
-						if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
-						{
-							/*
-							 * This option is slightly special as the port
-							 * number may be appended onto the end. Here we
-							 * need to build 1 string which contains the
-							 * remote_host and optionally the remote_port (if
-							 * set) so we can properly align the string.
-							 */
-
-							char	   *hostport;
-
-							hostport = psprintf("%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port);
-							appendStringInfo(buf, "%*s", padding, hostport);
-							pfree(hostport);
-						}
-						else
-							appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
-					}
-					else
-					{
-						/* padding is 0, so we don't need a temp buffer */
-						appendStringInfoString(buf, MyProcPort->remote_host);
-						if (MyProcPort->remote_port &&
-							MyProcPort->remote_port[0] != '\0')
-							appendStringInfo(buf, "(%s)",
-											 MyProcPort->remote_port);
-					}
-
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'h':
-				if (MyProcPort && MyProcPort->remote_host)
-				{
-					if (padding != 0)
-						appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
-					else
-						appendStringInfoString(buf, MyProcPort->remote_host);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'q':
-				/* in postmaster and friends, stop if %q is seen */
-				/* in a backend, just ignore */
-				if (MyProcPort == NULL)
-					return;
-				break;
-			case 'v':
-				/* keep VXID format in sync with lockfuncs.c */
-				if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
-				{
-					if (padding != 0)
-					{
-						char		strfbuf[128];
-
-						snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
-								 MyProc->backendId, MyProc->lxid);
-						appendStringInfo(buf, "%*s", padding, strfbuf);
-					}
-					else
-						appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
-				}
-				else if (padding != 0)
-					appendStringInfoSpaces(buf,
-										   padding > 0 ? padding : -padding);
-				break;
-			case 'x':
-				if (padding != 0)
-					appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
-				else
-					appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
-				break;
-			case 'e':
-				if (padding != 0)
-					appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode));
-				else
-					appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
-				break;
-			case 'Q':
-				if (padding != 0)
-					appendStringInfo(buf, "%*lld", padding,
-									 (long long) pgstat_get_my_query_id());
-				else
-					appendStringInfo(buf, "%lld",
-									 (long long) pgstat_get_my_query_id());
-				break;
-			default:
-				/* format error - ignore it */
-				break;
-		}
-	}
+	process_format_string(buf, Log_line_prefix, edata, log_line_number, NULL);
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index d2ce4a8450..f54aae3ba3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -93,6 +93,7 @@
 #include "utils/backend_status.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
+#include "utils/elog.h"
 #include "utils/float.h"
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
@@ -154,6 +155,8 @@ char	   *GUC_check_errhint_string;
 
 static void do_serialize(char **destptr, Size *maxbytes, const char *fmt,...) pg_attribute_printf(3, 4);
 
+static int parse_padding(const char *nptr, char **endptr);
+
 static void set_config_sourcefile(const char *name, char *sourcefile,
 								  int sourceline);
 static bool call_bool_check_hook(struct config_bool *conf, bool *newval,
@@ -11390,6 +11393,406 @@ validate_option_array_item(const char *name, const char *value,
 	return true;
 }
 
+/*
+ * process_format_string - parse string and translate escapes
+ * 
+ * This function parses an input string
+ * and replaces escape sequences if possible.
+ * escape_chars specifies the allowed escapes, and accept all if NULL is set.
+ *
+ * Note that argument buf must be initialized.
+ */
+void
+process_format_string(StringInfo buf, const char *input, ErrorData *edata, long log_line_number, const char *escape_chars)
+{
+	int padding;
+	const char *p;
+
+	if (!input)
+		return;
+
+	for (p = input; *p != '\0'; p++)
+	{
+		if (*p != '%')
+		{
+			/* literal char, just copy */
+			appendStringInfoChar(buf, *p);
+			continue;
+		}
+
+		/* must be a '%', so skip to the next char */
+		p++;
+		if (*p == '\0')
+			break;				/* format error - ignore it */
+		else if (*p == '%')
+		{
+			/* string contains %% */
+			appendStringInfoChar(buf, '%');
+			continue;
+		}
+
+
+		/*
+		 * Process any formatting which may exist after the '%'.
+		 *
+		 * Note: Since only '-', '0' to '9' are valid formatting characters we
+		 * can do a quick check here to pre-check for formatting. If the char
+		 * is not formatting then we can skip a useless function call.
+		 *
+		 * Further note: At least on some platforms, passing %*s rather than
+		 * %s to appendStringInfo() is substantially slower, so many of the
+		 * cases below avoid doing that unless non-zero padding is in fact
+		 * specified.
+		 */
+		if (*p > '9')
+			padding = 0;
+		else
+		{
+			char *endptr = NULL;
+			padding = parse_padding(p, &endptr);
+			if (!endptr|| *endptr == '\0')
+				break;
+			p = endptr;
+		}
+
+		if (escape_chars && !strchr(escape_chars, *p))
+			continue;
+
+		/* process the option */
+		switch (*p)
+		{
+			case 'a':
+				if (MyProcPort)
+				{
+					const char *appname = application_name;
+
+					if (appname == NULL || *appname == '\0')
+						appname = _("[unknown]");
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, appname);
+					else
+						appendStringInfoString(buf, appname);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+
+				break;
+			case 'b':
+				{
+					const char *backend_type_str;
+
+					if (MyProcPid == PostmasterPid)
+						backend_type_str = "postmaster";
+					else if (MyBackendType == B_BG_WORKER)
+						backend_type_str = MyBgworkerEntry->bgw_type;
+					else
+						backend_type_str = GetBackendTypeDesc(MyBackendType);
+
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, backend_type_str);
+					else
+						appendStringInfoString(buf, backend_type_str);
+					break;
+				}
+			case 'u':
+				if (MyProcPort)
+				{
+					const char *username = MyProcPort->user_name;
+
+					if (username == NULL || *username == '\0')
+						username = _("[unknown]");
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, username);
+					else
+						appendStringInfoString(buf, username);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'd':
+				if (MyProcPort)
+				{
+					const char *dbname = MyProcPort->database_name;
+
+					if (dbname == NULL || *dbname == '\0')
+						dbname = _("[unknown]");
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, dbname);
+					else
+						appendStringInfoString(buf, dbname);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'c':
+				if (padding != 0)
+				{
+					char		strfbuf[128];
+
+					snprintf(strfbuf, sizeof(strfbuf) - 1, "%lx.%x",
+							 (long) (MyStartTime), MyProcPid);
+					appendStringInfo(buf, "%*s", padding, strfbuf);
+				}
+				else
+					appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid);
+				break;
+			case 'p':
+				if (padding != 0)
+					appendStringInfo(buf, "%*d", padding, MyProcPid);
+				else
+					appendStringInfo(buf, "%d", MyProcPid);
+				break;
+
+			case 'P':
+				if (MyProc)
+				{
+					PGPROC	   *leader = MyProc->lockGroupLeader;
+
+					/*
+					 * Show the leader only for active parallel workers. This
+					 * leaves out the leader of a parallel group.
+					 */
+					if (leader == NULL || leader->pid == MyProcPid)
+						appendStringInfoSpaces(buf,
+											   padding > 0 ? padding : -padding);
+					else if (padding != 0)
+						appendStringInfo(buf, "%*d", padding, leader->pid);
+					else
+						appendStringInfo(buf, "%d", leader->pid);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+
+			case 'l':
+				if (padding != 0)
+					appendStringInfo(buf, "%*ld", padding, log_line_number);
+				else
+					appendStringInfo(buf, "%ld", log_line_number);
+				break;
+			case 'm':
+				setup_formatted_log_time();
+				if (padding != 0)
+					appendStringInfo(buf, "%*s", padding, formatted_log_time);
+				else
+					appendStringInfoString(buf, formatted_log_time);
+				break;
+			case 't':
+				{
+					pg_time_t	stamp_time = (pg_time_t) time(NULL);
+					char		strfbuf[128];
+
+					pg_strftime(strfbuf, sizeof(strfbuf),
+								"%Y-%m-%d %H:%M:%S %Z",
+								pg_localtime(&stamp_time, log_timezone));
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, strfbuf);
+					else
+						appendStringInfoString(buf, strfbuf);
+				}
+				break;
+			case 'n':
+				{
+					char		strfbuf[128];
+
+					if (!saved_timeval_set)
+					{
+						gettimeofday(&saved_timeval, NULL);
+						saved_timeval_set = true;
+					}
+
+					snprintf(strfbuf, sizeof(strfbuf), "%ld.%03d",
+							 (long) saved_timeval.tv_sec,
+							 (int) (saved_timeval.tv_usec / 1000));
+
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, strfbuf);
+					else
+						appendStringInfoString(buf, strfbuf);
+				}
+				break;
+			case 's':
+				if (formatted_start_time[0] == '\0')
+					setup_formatted_start_time();
+				if (padding != 0)
+					appendStringInfo(buf, "%*s", padding, formatted_start_time);
+				else
+					appendStringInfoString(buf, formatted_start_time);
+				break;
+			case 'i':
+				if (MyProcPort)
+				{
+					const char *psdisp;
+					int			displen;
+
+					psdisp = get_ps_display(&displen);
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, psdisp);
+					else
+						appendBinaryStringInfo(buf, psdisp, displen);
+
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'r':
+				if (MyProcPort && MyProcPort->remote_host)
+				{
+					if (padding != 0)
+					{
+						if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
+						{
+							/*
+							 * This option is slightly special as the port
+							 * number may be appended onto the end. Here we
+							 * need to build 1 string which contains the
+							 * remote_host and optionally the remote_port (if
+							 * set) so we can properly align the string.
+							 */
+
+							char	   *hostport;
+
+							hostport = psprintf("%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port);
+							appendStringInfo(buf, "%*s", padding, hostport);
+							pfree(hostport);
+						}
+						else
+							appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
+					}
+					else
+					{
+						/* padding is 0, so we don't need a temp buffer */
+						appendStringInfoString(buf, MyProcPort->remote_host);
+						if (MyProcPort->remote_port &&
+							MyProcPort->remote_port[0] != '\0')
+							appendStringInfo(buf, "(%s)",
+											 MyProcPort->remote_port);
+					}
+
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'h':
+				if (MyProcPort && MyProcPort->remote_host)
+				{
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
+					else
+						appendStringInfoString(buf, MyProcPort->remote_host);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'q':
+				/* in postmaster and friends, stop if %q is seen */
+				/* in a backend, just ignore */
+				if (MyProcPort == NULL)
+					return;
+				break;
+			case 'v':
+				/* keep VXID format in sync with lockfuncs.c */
+				if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
+				{
+					if (padding != 0)
+					{
+						char		strfbuf[128];
+
+						snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
+								 MyProc->backendId, MyProc->lxid);
+						appendStringInfo(buf, "%*s", padding, strfbuf);
+					}
+					else
+						appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
+				}
+				else if (padding != 0)
+					appendStringInfoSpaces(buf,
+										   padding > 0 ? padding : -padding);
+				break;
+			case 'x':
+				if (padding != 0)
+					appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
+				else
+					appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
+				break;
+			case 'e':
+				Assert(edata);
+				if (padding != 0)
+					appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode));
+				else
+					appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
+				break;
+			case 'Q':
+				if (padding != 0)
+					appendStringInfo(buf, "%*lld", padding,
+									 (long long) pgstat_get_my_query_id());
+				else
+					appendStringInfo(buf, "%lld",
+									 (long long) pgstat_get_my_query_id());
+				break;
+			default:
+				/* format error - ignore it */
+				break;
+		}
+	}
+}
+
+/*
+ * parse_padding --- helper function for process_format_string()
+ *
+ * This converts initial part of the string and returns
+ * the result of the convertion.
+ *
+ * This function is very similar to strtoint(),
+ * but this follows the printf-format more precisely than that.
+ */
+static int
+parse_padding(const char *nptr, char **endptr)
+{
+	int			paddingsign = 1;
+	int			padding = 0;
+	const char*	p = nptr;
+
+	if (*p == '-')
+	{
+		p++;
+
+		if (*p == '\0')			/* Did the buf end in %- ? */
+		{
+			if (endptr)
+				*endptr = (char *)p;
+			return 0;
+		}
+		paddingsign = -1;
+	}
+
+	/* generate an int version of the numerical string */
+	while (*p >= '0' && *p <= '9')
+		padding = padding * 10 + (*p++ - '0');
+
+	/* format is invalid if it ends with the padding number */
+	if (*p == '\0')
+	{
+		if (endptr)
+			*endptr = (char *)p;
+		return 0;
+	}
+
+	padding *= paddingsign;
+
+	if (endptr)
+		*endptr = (char *)p;
+	return padding;
+}
+
+
 
 /*
  * Called by check_hooks that want to override the normal
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f53607e12e..ed1e460886 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -453,4 +453,18 @@ extern void set_syslog_parameters(const char *ident, int facility);
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 
+/*
+ * Time-related variables and functions.
+ * They are used in elog.c and process_format_string().
+ */
+extern struct timeval saved_timeval;
+extern bool saved_timeval_set;
+
+#define FORMATTED_TS_LEN 128
+extern char formatted_start_time[FORMATTED_TS_LEN];
+extern char formatted_log_time[FORMATTED_TS_LEN];
+
+void setup_formatted_log_time(void);
+void setup_formatted_start_time(void);
+
 #endif							/* ELOG_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index aa18d304ac..f8240ef997 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -13,6 +13,7 @@
 #ifndef GUC_H
 #define GUC_H
 
+#include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "tcop/dest.h"
 #include "utils/array.h"
@@ -397,6 +398,8 @@ extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *va
 extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
 extern ArrayType *GUCArrayReset(ArrayType *array);
 
+extern void process_format_string(StringInfo buf, const char *input, ErrorData *edata, long log_line_number, const char *escape_chars);
+
 #ifdef EXEC_BACKEND
 extern void write_nondefault_variables(GucContext context);
 extern void read_nondefault_variables(void);
