diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 6bac4ad23e..7d6a49be74 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -348,6 +348,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 	{
 		const char **keywords;
 		const char **values;
+		char *data = NULL;
 		int			n;
 
 		/*
@@ -383,6 +384,36 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 			n++;
 		}
 
+		/*
+		 * Search the parameter arrays to find application_name setting,
+		 * and replace escape sequences in it with status information if found.
+		 * The arrays are searched backwards because the last value
+		 * is used if application_name is repeatedly set.
+		 */
+		for (int i = n - 1; i >= 0; i--)
+		{
+			if (strcmp(keywords[i], "application_name") == 0)
+			{
+				data = process_pgfdw_appname(values[i]);
+
+				/* Use the string and break if it is valid */
+				if (data[0] != '\0')
+				{
+					values[i] = data;
+					break;
+				}
+
+				/*
+				 * The parsing result became an empty string,
+				 * and that means other "application_name" will be used
+				 * for connecting to the remote server.
+				 * So we must set values[i] to an empty string
+				 * and search the array again.
+				 */
+				values[i] = "";
+			}
+		}
+
 		/* Use "postgres_fdw" as fallback_application_name */
 		keywords[n] = "fallback_application_name";
 		values[n] = "postgres_fdw";
@@ -452,6 +483,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 		/* Prepare new session for use */
 		configure_remote_session(conn);
 
+		if (data != NULL)
+			pfree(data);
 		pfree(keywords);
 		pfree(values);
 	}
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 5196e4797a..55441f8b40 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10822,3 +10822,33 @@ ERROR:  invalid value for integer option "batch_size": 100$%$#$#
 ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
 ERROR:  invalid option "nonexistent"
 HINT:  There are no valid options in this context.
+-- ===================================================================
+-- 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 48c7417e6e..a57de6903d 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -18,6 +18,9 @@
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "commands/extension.h"
+#include "common/string.h"
+#include "lib/stringinfo.h"
+#include "libpq/libpq-be.h"
 #include "postgres_fdw.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
@@ -445,6 +448,64 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
 	return extensionOids;
 }
 
+/*
+ * Replace escape sequences beginning with % character in the given
+ * application_name with status information, and return it.
+ */
+char *
+process_pgfdw_appname(const char *name)
+{
+	const char *p;
+	StringInfoData buf;
+
+	Assert(MyProcPort != NULL);
+	initStringInfo(&buf);
+
+
+	for(p = name; *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 the option */
+		switch (*p)
+		{
+			case 'a':
+				appendStringInfoString(&buf, application_name);
+				break;
+			case 'u':
+				appendStringInfoString(&buf, MyProcPort->user_name);
+				break;
+			case 'd':
+				appendStringInfoString(&buf, MyProcPort->database_name);
+				break;
+			case 'p':
+				appendStringInfo(&buf, "%d", MyProcPid);
+				break;
+			default:
+				/* format error - ignore it */
+				break;
+		}
+	}
+
+	return buf.data;
+}
+
 /*
  * Module load callback
  */
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 90b72e9ec5..584b87d96d 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 char *process_pgfdw_appname(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 666d21962a..9ca5651483 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3449,3 +3449,20 @@ CREATE FOREIGN TABLE inv_bsz (c1 int )
 
 -- No option is allowed to be specified at foreign data wrapper level
 ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
+
+-- ===================================================================
+-- 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 e71d2b4eea..7e39c93d02 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>
@@ -946,6 +950,58 @@ 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>
+      <literal>%</literal> characters begin <quote>escape sequences</quote>
+      that are replaced with status information as outlined below.
+      Unrecognized escapes are ignored. Other characters are copied straight
+      to the application name. Note that it's not allowed to specify a
+      plus/minus sign or a numeric literal after the <literal>%</literal>
+      and before the option, for alignment and padding.
+     </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>
+      In the table above, the term "local" means information about a server
+      that uses <filename>postgres_fdw</filename>. For example, suppose you
+      connect to database template1 as user postgres and set
+      <varname>postgres_fdw.application_name</varname>
+      to "db=%d, user=%u". If you connect to a remote server with this
+      configuration, the <varname>application_name</varname> used is
+      "db=template1, user=postgres".
+     </para>
     </listitem>
    </varlistentry>
   </variablelist>
