Le 14/10/2016 à 20:45, Christoph Berg a écrit :
> Re: Gilles Darold 2016-10-14 <be9571dc-ae7c-63d4-6750-d7243cb5f...@dalibo.com>
>> Agree, the usability of the current version is really a pain. What I've
>> thought is that the function could return the csv log in all case when
>> csvlog is set in log_destination and the stderr log file when csvlog is
>> not defined. We can also have a parametrer to ask for a specific log
>> format, like pg_current_logfile('csv') or pg_current_logfile('stderr').
> Good idea to add an optional parameter.
>
> pg_current_logfile(type text DEFAULT 'auto')
>
> pg_current_logfile()
> pg_current_logfile('stderr')
> pg_current_logfile('csvlog')
> pg_current_logfile('auto')
>
> I think I'd prefer the stderr variant by default when both variants
> are configured.
>
> Christoph
>
>

Here is the v6 of the patch, here is the description of the
pg_current_logfile() function, I have tried to keep thing as simple as
possible:

pg_current_logfile( [ destination text ] )
-----------------------------------------------------

Returns the name of the current log file used by the logging collector,
as text.
Log collection must be active or the return value is undefined. When
csvlog is
used as log destination, the csv filename is returned, when it is set to
stderr,
the stderr filename is returned. When both are used, it returns the
stderr filename.

There is an optional parameter of type text to determines the log
filename to
return following the log format, values can be 'csvlog' or 'stderr'.
When the log
format asked is not used as log destination then the return value is
undefined.

Example of use when log_destination = 'csvlog,stderr'

postgres=# select pg_current_logfile();
           pg_current_logfile           
-----------------------------------------
 pg_log/postgresql-2016-10-18_121326.log
(1 row)

postgres=# select pg_current_logfile('csvlog');

           pg_current_logfile           
-----------------------------------------
 pg_log/postgresql-2016-10-18_121326.csv
(1 row)

postgres=# select pg_current_logfile('stderr');
           pg_current_logfile           
-----------------------------------------
 pg_log/postgresql-2016-10-18_121326.log
(1 row)

postgres=# select pg_current_logfile('csv');
ERROR:  log format csv not supported, possible values are stderr or csvlog

postgres=# select pg_read_file(pg_current_logfile());
                                          
pg_read_file                                           
---------------------------------------------------------------------------------------------------
 2016-10-18 14:06:30.576 CEST [8113] ERROR:  invalid input syntax for
integer: "A" at character 10+
 2016-10-18 14:06:30.576 CEST [8113] STATEMENT:  select
1='A';                                    +
 
(1 row)

I've also fixed the crash when current_logfile is removed by hand.


-- 
Gilles Darold
Consultant PostgreSQL
http://dalibo.com - http://dalibo.org

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a588350..3cd9535 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15480,6 +15480,12 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
       </row>
 
       <row>
+       <entry><literal><function>pg_current_logfile(<optional> <parameter>destination</> <type>text</></optional>)</function></literal></entry>
+       <entry><type>text</type></entry>
+       <entry>current log file used by the logging collector</entry>
+      </row>
+
+      <row>
        <entry><literal><function>session_user</function></literal></entry>
        <entry><type>name</type></entry>
        <entry>session user name</entry>
@@ -15686,6 +15692,23 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
     <primary>pg_notification_queue_usage</primary>
    </indexterm>
 
+   <indexterm>
+    <primary>pg_current_logfile</primary>
+   </indexterm>
+
+   <para>
+    <function>pg_current_logfile</function> returns the name of the
+    current log file used by the logging collector, as <type>text</type>.
+    Log collection must be active or the return value is undefined. When
+    csvlog is used as log destination, the csv filename is returned, when
+    it is set to stderr, the stderr filename is returned. When both are
+    used, it returns the stderr filename.
+    There is an optional parameter of type <type>text</type> to determines
+    the log filename to return following the log destination, values can
+    be 'csvlog' or 'stderr'. When the log format asked is not used as log
+    destination then the return value is undefined.
+   </para>
+
    <para>
     <function>pg_listening_channels</function> returns a set of names of
     asynchronous notification channels that the current session is listening
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 1b812bd..8f909a7 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -170,6 +170,13 @@ last started with</entry>
   (this file is not present after server shutdown)</entry>
 </row>
 
+<row>
+ <entry><filename>pg_log_file</></entry>
+ <entry>A file recording the current log file(s) used by the syslogger
+  when log collection is active (this file is not present when logging_collector is not activated)</entry>
+</row>
+
+
 </tbody>
 </tgroup>
 </table>
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index fd62d66..7b09349 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -146,6 +146,7 @@ static char *logfile_getname(pg_time_t timestamp, const char *suffix);
 static void set_next_rotation_time(void);
 static void sigHupHandler(SIGNAL_ARGS);
 static void sigUsr1Handler(SIGNAL_ARGS);
+static void logfile_writename(char *filename, char *csvfilename);
 
 
 /*
@@ -348,6 +349,13 @@ SysLoggerMain(int argc, char *argv[])
 				rotation_disabled = false;
 				rotation_requested = true;
 			}
+
+			/*
+			 * Force rewriting last log filename when reloading configuration,
+			 * log_destination and logging_collector may have been changed. Do
+			 * it right now to not wait for the next file rotation.
+			 */
+			logfile_writename(last_file_name, last_csv_file_name);
 		}
 
 		if (Log_RotationAge > 0 && !rotation_disabled)
@@ -513,8 +521,14 @@ SysLogger_Start(void)
 	pid_t		sysloggerPid;
 	char	   *filename;
 
-	if (!Logging_collector)
+	if (!Logging_collector) {
+		/* If logging collector is not enabled, remove current log
+		 * filename as we don't know where messages are logged and
+		 * information contained in this file are obsolete.
+		 */
+		unlink(CURRENT_LOG_FILENAME);
 		return 0;
+	}
 
 	/*
 	 * If first time through, create the pipe which will receive stderr
@@ -574,6 +588,8 @@ SysLogger_Start(void)
 
 	syslogFile = logfile_open(filename, "a", false);
 
+	logfile_writename(filename, NULL);
+
 	pfree(filename);
 
 #ifdef EXEC_BACKEND
@@ -988,8 +1004,10 @@ write_syslogger_file(const char *buffer, int count, int destination)
 	int			rc;
 	FILE	   *logfile;
 
-	if (destination == LOG_DESTINATION_CSVLOG && csvlogFile == NULL)
+	if (destination == LOG_DESTINATION_CSVLOG && csvlogFile == NULL) {
 		open_csvlogfile();
+		logfile_writename(last_file_name, last_csv_file_name);
+	}
 
 	logfile = destination == LOG_DESTINATION_CSVLOG ? csvlogFile : syslogFile;
 	rc = fwrite(buffer, 1, count, logfile);
@@ -1209,6 +1227,8 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
 			return;
 		}
 
+		logfile_writename(filename, csvfilename);
+
 		fclose(syslogFile);
 		syslogFile = fh;
 
@@ -1220,7 +1240,6 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
 	}
 
 	/* Same as above, but for csv file. */
-
 	if (csvlogFile != NULL &&
 		(time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG)))
 	{
@@ -1253,6 +1272,8 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
 			return;
 		}
 
+		logfile_writename(last_file_name, csvfilename);
+
 		fclose(csvlogFile);
 		csvlogFile = fh;
 
@@ -1365,3 +1386,60 @@ sigUsr1Handler(SIGNAL_ARGS)
 
 	errno = save_errno;
 }
+
+/*
+ * Store the name of the files where current log messages are written when
+ * log collector is enabled. Useful to find the name of the current log file
+ * when a time-based rotation is defined. Filenames are first stored into a
+ * temporary file and renamed into the final destination.
+ */
+static void
+logfile_writename(char *filename, char *csvfilename)
+{
+	FILE	*fh;
+	char	tempfn[MAXPGPATH];
+	char	logpathfilename[MAXPGPATH];
+
+	snprintf(tempfn, sizeof(tempfn), "%s",
+						CURRENT_LOG_FILENAME);
+	strcat(tempfn, ".tmp");
+	snprintf(logpathfilename, sizeof(logpathfilename), "%s",
+						CURRENT_LOG_FILENAME);
+	if ((fh = logfile_open(tempfn, "w", true) ) == NULL)
+	{
+		return;
+	}
+	if (filename && (Log_destination & LOG_DESTINATION_STDERR)) {
+		if (fprintf(fh, "%s\n", filename) < 0)
+		{
+			ereport(LOG,
+					(errcode_for_file_access(),
+					errmsg("could not write log file \"%s\": %m",
+						tempfn)));
+			fclose(fh);
+			return;
+		}
+	}
+
+	if (csvfilename && (Log_destination & LOG_DESTINATION_CSVLOG)) {
+		if (fprintf(fh, "%s\n", csvfilename) < 0)
+		{
+			ereport(LOG,
+					(errcode_for_file_access(),
+					errmsg("could not write log file \"%s\": %m",
+						tempfn)));
+			fclose(fh);
+			return;
+		}
+	}
+	fclose(fh);
+
+	if (rename(tempfn, logpathfilename) != 0)
+	{
+		ereport(LOG,
+				(errcode_for_file_access(),
+				errmsg("could not rename file \"%s\": %m",
+						tempfn)));
+		return;
+	}
+}
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 0da051a..6763055 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <math.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #include "access/sysattr.h"
 #include "catalog/pg_authid.h"
@@ -892,3 +893,122 @@ parse_ident(PG_FUNCTION_ARGS)
 
 	PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
 }
+
+/*
+ * Report current log file used by log collector
+ */
+Datum
+pg_current_logfile(PG_FUNCTION_ARGS)
+{
+	FILE    *fd;
+	char    log_filename[MAXPGPATH];
+	text    *fmt;
+	char    *logfmt;
+	struct stat stat_buf;
+
+	if (PG_NARGS() == 1) {
+		fmt = PG_ARGISNULL(0) ? NULL : PG_GETARG_TEXT_PP(0);
+		if (fmt != NULL) {
+			logfmt = text_to_cstring(fmt);
+			if ( (strcmp(logfmt, "stderr") != 0) &&
+				(strcmp(logfmt, "csvlog") != 0) ) {
+				ereport(ERROR,
+		(errmsg("log format %s not supported, possible values are stderr or csvlog", logfmt)));
+				PG_RETURN_NULL();
+			}
+		}
+	} else {
+		logfmt = NULL;
+	}
+
+	if (!Logging_collector)
+		PG_RETURN_NULL();
+
+	/* Check if current log file is present */
+	if (stat(CURRENT_LOG_FILENAME, &stat_buf) != 0)
+		PG_RETURN_NULL();
+
+	fd = AllocateFile(CURRENT_LOG_FILENAME, "r");
+	if (fd == NULL)
+	{
+		if (errno != ENOENT)
+			ereport(ERROR,
+				(errcode_for_file_access(),
+				errmsg("could not read file \"%s\": %m",
+				CURRENT_LOG_FILENAME)));
+		PG_RETURN_NULL();
+	}
+
+	/*
+	* Read the file to gather current log filename(s) registered
+	* by the syslogger.
+	*/
+	while (fgets(log_filename, sizeof(log_filename), fd) != NULL) {
+
+		/* Check for a read error. */
+		if (ferror(fd)) {
+			ereport(ERROR,
+				(errcode_for_file_access(),
+				errmsg("could not read file \"%s\": %m", CURRENT_LOG_FILENAME)));
+			FreeFile(fd);
+			break;
+		}
+
+		/* remove trailing newline */
+		if (strchr(log_filename, '\n') != NULL)
+			*strchr(log_filename, '\n') = '\0';
+
+		/*
+		 * When no format is provided as argument, always reports
+		 * the first registered log file. I will be the stderr log
+		 * if csvlog is not set or if both csvlog and stderr are
+		 * set in log_destination. stderr log is always the first
+		 * entry in CURRENT_LOG_FILENAME, see logfile_writename()
+		 * in syslogger.c
+		 */
+		if (logfmt == NULL)
+			break;
+
+		/*
+		 * When the log format requested is set to stderr and
+		 * log_destination have the stderr format set, return
+		 * the current filename if this is not the csv one.
+		 */
+		if ( (strcmp(logfmt, "stderr") == 0) && 
+			(Log_destination & LOG_DESTINATION_STDERR) &&
+				(strstr(log_filename, ".csv") == NULL) )
+			break;
+
+		/*
+		 * When the log format requested is set to csvlog and
+		 * log_destination have the csvlog format set, return
+		 * the current filename if this is the csv one.
+		 */
+		if ( (strcmp(logfmt, "csvlog") == 0) && 
+			(Log_destination & LOG_DESTINATION_CSVLOG) &&
+				(strstr(log_filename, ".csv") != NULL) )
+				break;
+	}
+	/* Close the current log filename file. */
+	if (FreeFile(fd))
+	       ereport(ERROR,
+			       (errcode_for_file_access(),
+		       errmsg("could not close file \"%s\": %m", CURRENT_LOG_FILENAME)));
+
+
+	if (log_filename[0] == '\0')
+		PG_RETURN_NULL();
+
+	/* Return null when csvlog is requested but we have a stderr log */
+	if ( (logfmt != NULL) && (strcmp(logfmt, "csvlog") == 0)
+			&& (strstr(log_filename, ".csv") == NULL) )
+		PG_RETURN_NULL();
+
+	/* Return null when stderr is requested but we have a csv log */
+	if ( (logfmt != NULL) && (strcmp(logfmt, "stderr") == 0)
+			&& (strstr(log_filename, ".csv") != NULL) )
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(log_filename));
+}
+
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 17ec71d..6d9653e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3179,6 +3179,10 @@ DATA(insert OID = 2621 ( pg_reload_conf			PGNSP PGUID 12 1 0 0 0 f f f f t f v s
 DESCR("reload configuration files");
 DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
 DESCR("rotate log file");
+DATA(insert OID = 3800 ( pg_current_logfile             PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 25 "" _null_ _null_ _null_ _null_ _null_ pg_current_logfile _null_ _null_ _null_ ));
+DESCR("current logging collector file location");
+DATA(insert OID = 3801 ( pg_current_logfile             PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_current_logfile _null_ _null_ _null_ ));
+DESCR("current logging collector file location");
 
 DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ _null_ pg_stat_file_1arg _null_ _null_ _null_ ));
 DESCR("get information about file");
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 78545da..15e1214 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -466,4 +466,12 @@ extern bool has_rolreplication(Oid roleid);
 extern bool BackupInProgress(void);
 extern void CancelBackup(void);
 
+/* in backend/utils/adt/misc.c and backend/postmaster/syslogger.c */
+/*
+ * Name of file where current log messages are written when log collector is
+ * enabled. Useful to find the name of the current log file when a time-based
+ * rotation is defined.
+ */
+#define CURRENT_LOG_FILENAME  "current_logfile"
+
 #endif   /* MISCADMIN_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 90f5132..df9a1e1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -522,6 +522,7 @@ extern Datum pg_collation_for(PG_FUNCTION_ARGS);
 extern Datum pg_relation_is_updatable(PG_FUNCTION_ARGS);
 extern Datum pg_column_is_updatable(PG_FUNCTION_ARGS);
 extern Datum parse_ident(PG_FUNCTION_ARGS);
+extern Datum pg_current_logfile(PG_FUNCTION_ARGS);
 
 /* oid.c */
 extern Datum oidin(PG_FUNCTION_ARGS);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to