Le 03/10/2016 à 16:09, Christoph Berg a écrit :
> Hi Gilles,
>
> I've just tried v4 of the patch. The OID you picked for
> pg_current_logfile doesn't work anymore, but after increasing it
> randomly by 10000, it compiles. I like the added functionality,
> especially that "select pg_read_file(pg_current_logfile());" just
> works.

I've changed the OID and some other things in this v5 of the patch, see
bellow.

> What bugs me is the new file "pg_log_file" in PGDATA. It clutters the
> directory listing. I wouldn't know where else to put it, but you might
> want to cross-check that with the thread that is trying to reshuffle
> the directory layout to make it easier to exclude files from backups.
> (Should this file be part of backups?)
>
> It's probably correct to leave the file around on shutdown (given it's
> still a correct pointer). But there might be a case for removing it on
> startup if logging_collector isn't active anymore.

The file has been renamed into current_logfile and is now removed at
startup if logging_collector is not active. The file can be excluded
from a backup but otherwise if it is restored it will be removed or
overridden at startup. Perhaps the file can give a useful information in
a backup to know the last log file active at backup time, but not sure
it has any interest.

I'm not sure which thread is talking about reshuffling the directory
layout, please give me a pointer if this is not the thread talking about
renaming of pg_xlog and pg_clog. In the future if we have a directory to
store files that must be excluded from backup or status files, it will
be easy to move this file here. I will follow such a change.

> Also, pg_log_file is tab-completion-unfriendly, it conflicts with
> pg_log/. Maybe name it current_logfile?
Right, done.

> Another thing that might possibly be improved is csv logging:
>
> # select pg_read_file(pg_current_logfile());
>                          pg_read_file                          
> ───────────────────────────────────────────────────────────────
>  LOG:  ending log output to stderr                            ↵
>  HINT:  Future log output will go to log destination "csvlog".↵
>
> -rw-------  1 cbe staff 1011 Okt  3 15:06 postgresql-2016-10-03_150602.csv
> -rw-------  1 cbe staff   96 Okt  3 15:06 postgresql-2016-10-03_150602.log
>
> ... though it's unclear what to do if both stderr and csvlog are
> selected.
>
> Possibly NULL should be returned if only "syslog" is selected.
> (Maybe remove pg_log_file once 'HINT:  Future log output will go to
> log destination "syslog".' is logged?)

I've totally missed that we can have log_destination set to stderr and
csvlog at the same time, so pg_current_logfile() might return two
filenames in this case. I've changed the function to return a setof
record to report the last stderr or csv log file or both. One another
major change is that the current log filename list is also updated after
a configuration reload and not just after a startup or a log rotation.
So in the case of you are switching from stderr to  csvlog or both you
will see immediately the change in current_logfile instead of waiting
for the next log rotation.

  * log_destination set to csvlog only:

postgres=# select * from pg_current_logfile();
          pg_current_logfile          
---------------------------------------
 pg_log/postgresql-2016-10-07_1646.csv
(1 row)

* log_destination set to stderr only:

postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 row)

postgres=# select * from pg_current_logfile();
          pg_current_logfile          
---------------------------------------
 pg_log/postgresql-2016-10-07_1647.log
(1 row)

* log_destination set to both stderr,csvlog:

postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 row)

postgres=# select * from pg_current_logfile();
          pg_current_logfile          
---------------------------------------
 pg_log/postgresql-2016-10-07_1648.log
 pg_log/postgresql-2016-10-07_1648.csv
(2 rows)

When logging_collector is disabled, this function return null.

As the return type has changed to a setof, the query to read the file
need to be change too:

postgres=# SELECT pg_read_file(log) FROM pg_current_logfile() b(log);
                                                         
pg_read_file                               
--------------------------------------------------------------------------------------------------------------------------------
LOG:  duration: 0.182 ms  statement: select  pg_read_file(log) from
pg_current_logfile() file(log);+
 
(1 row)

I can change the return type to a single text[] if that's looks better.

Thanks

-- 
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..69a74af 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()</function></literal></entry>
+       <entry><type>setof record</type></entry>
+       <entry>current log file(s) 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,17 @@ 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 names of the
+    current log files used by the logging collector, as
+    <type>setof record</type>. Log collection must be active or 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..3b0f38a 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -892,3 +892,79 @@ parse_ident(PG_FUNCTION_ARGS)
 
 	PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
 }
+
+/*
+ * Report current log file used by log collector
+ */
+typedef struct
+{
+        char      *location;
+	FILE      *filedesc;
+	
+} logfilename_fctx;
+
+
+Datum
+pg_current_logfile(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	char      log_filename[MAXPGPATH];
+	logfilename_fctx *fctx;
+	MemoryContext oldcontext;
+
+	if (!Logging_collector)
+		PG_RETURN_NULL();
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		fctx = palloc(sizeof(logfilename_fctx));
+		fctx->location = CURRENT_LOG_FILENAME;
+		fctx->filedesc = AllocateFile(fctx->location, "r");
+
+		if (!fctx->filedesc)
+		{
+			if (errno != ENOENT)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+					errmsg("could not read file \"%s\": %m",
+						fctx->location)));
+		}
+
+		funcctx->user_fctx = fctx;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	fctx = (logfilename_fctx *) funcctx->user_fctx;
+
+	/*
+	* Read first line of the file to gather current log filename
+	* registered by the syslogger.
+	*/
+	while (fgets(log_filename, sizeof(log_filename), fctx->filedesc) != NULL) {
+
+		/* Check for a read error. */
+		if (ferror(fctx->filedesc)) {
+			ereport(ERROR,
+					(errcode_for_file_access(),
+				errmsg("could not read file \"%s\": %m", fctx->location)));
+			break;
+		}
+
+		/* remove trailing newline */
+		if (strchr(log_filename, '\n') != NULL)
+			*strchr(log_filename, '\n') = '\0';
+
+		if (log_filename[0] == '\0')
+			break;
+
+		 SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(log_filename));
+	}
+	FreeFile(fctx->filedesc);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..79a5a1d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3182,6 +3182,8 @@ 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 t 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 = 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 */
-- 
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