Hello

Patch ba79cb5 (for full discussion see [1]) introduces a feature to log bind parameter values on error, which greatly helps to reproduce errors artificially having only server log -- thanks everyone who
reviewed and improved it!

However, it cuts the values, as some of the reviewers were worried log could fill up too quickly. This applies both to the new case of logging parameter values and to the existing ones due to
log_min_duration_statement or log_statement.

This is a backwards-incompatible change, and also ruins the idea of reproducible errors -- sorry Tom
I failed to second this idea [2] in time, before the change was pushed.

I personally don't think that we necessarily need to cut the values, we can rely on the users being careful when using this feature -- in the same way we trusted them use similarly dangerous log_min_duration_statement and especially log_statement for ages. At least it's better than having no option to disable it. Alvaro's opinion was different [3]. What do you think of adding a parameter to limit max logged parameter length? See patch attached.

Best, Alex

[1] https://postgr.es/m/0146a67b-a22a-0519-9082-bc29756b9...@imap.cc
[2] https://postgr.es/m/11425.1575927321%40sss.pgh.pa.us
[3] https://postgr.es/m/20191209200531.GA19848@alvherre.pgsql
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c1128f89ec..0f40246c2d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6645,6 +6645,28 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-log-parameter-max-length" xreflabel="log_parameter_max_length">
+      <term><varname>log_parameter_max_length</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>log_parameter_max_length</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Controls whether to log parameters in full or cut them to a certain length.
+        Bind parameters can appear in the log as a result of 
+        <xref linkend="guc-log-min-duration-statement"/>,
+        <xref linkend="guc-log-statement"/> or
+        <xref linkend="guc-log-parameters-on_error"/>
+        settings.
+        Zero (the default) disables cutting.
+        If this value is specified without units, it is taken as bytes.
+        Due to multibyte character and ellipsis, truncated values may be slightly shorter.
+        Only superusers can change this setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-log-statement" xreflabel="log_statement">
       <term><varname>log_statement</varname> (<type>enum</type>)
       <indexterm>
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..acc31899d6 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1839,13 +1839,22 @@ exec_bind_message(StringInfo input_message)
 						if (knownTextValues == NULL)
 							knownTextValues =
 								palloc0(numParams * sizeof(char *));
-						/*
-						 * Note: must copy at least two more full characters
-						 * than BuildParamLogString wants to see; otherwise it
-						 * might fail to include the ellipsis.
-						 */
-						knownTextValues[paramno] =
-							pnstrdup(pstring, 64 + 2 * MAX_MULTIBYTE_CHAR_LEN);
+
+						if (log_parameter_max_length != 0)
+						{
+							/*
+							* Note: must copy at least two more full characters
+							* than BuildParamLogString wants to see;
+							* otherwise it might fail to include the ellipsis.
+							*/
+							knownTextValues[paramno] =
+								pnstrdup(pstring,
+										 log_parameter_max_length
+											+ 2 * MAX_MULTIBYTE_CHAR_LEN);
+						}
+						else
+							knownTextValues[paramno] = pstrdup(pstring);
+
 						MemoryContextSwitchTo(oldcxt);
 					}
 					if (pstring != pbuf.data)
@@ -1908,7 +1917,9 @@ exec_bind_message(StringInfo input_message)
 		 */
 		if (log_parameters_on_error)
 			params->paramValuesStr =
-				BuildParamLogString(params, knownTextValues, 64);
+				BuildParamLogString(params,
+									knownTextValues,
+									log_parameter_max_length);
 	}
 	else
 		params = NULL;
@@ -2397,7 +2408,7 @@ errdetail_params(ParamListInfo params)
 	{
 		char   *str;
 
-		str = BuildParamLogString(params, NULL, 0);
+		str = BuildParamLogString(params, NULL, log_parameter_max_length);
 		if (str && str[0] != '\0')
 			errdetail("parameters: %s", str);
 	}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8228e1f390..f27efc6c24 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -543,6 +543,7 @@ int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
+int			log_parameter_max_length = 0;
 int			log_temp_files = -1;
 double		log_statement_sample_rate = 1.0;
 double		log_xact_sample_rate = 0;
@@ -2834,6 +2835,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"log_parameter_max_length", PGC_SUSET, LOGGING_WHAT,
+			gettext_noop("When logging a parameter value, only log first N bytes."),
+			gettext_noop("Zero to print values in full."),
+			GUC_UNIT_BYTE
+		},
+		&log_parameter_max_length,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"bgwriter_delay", PGC_SIGHUP, RESOURCES_BGWRITER,
 			gettext_noop("Background writer sleep time between rounds."),
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ce93ace76c..72ab683ac6 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -235,6 +235,7 @@ typedef enum
 /* GUC vars that are actually declared in guc.c, rather than elsewhere */
 extern bool log_duration;
 extern bool log_parameters_on_error;
+extern int log_parameter_max_length;
 extern bool Debug_print_plan;
 extern bool Debug_print_parse;
 extern bool Debug_print_rewritten;

Reply via email to