Add pg_current_logfile() function.
authorRobert Haas <rhaas@postgresql.org>
Fri, 3 Mar 2017 06:02:45 +0000 (11:32 +0530)
committerRobert Haas <rhaas@postgresql.org>
Fri, 3 Mar 2017 06:13:11 +0000 (11:43 +0530)
The syslogger will write out the current stderr and csvlog names, if
it's running and there are any, to a new file in the data directory
called "current_logfiles".  We take care to remove this file when it
might no longer be valid (but not at shutdown).  The function
pg_current_logfile() can be used to read the entries in the file.

Gilles Darold, reviewed and modified by Karl O.  Pinc, Michael
Paquier, and me.  Further review by Álvaro Herrera and Christoph Berg.

12 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/func.sgml
doc/src/sgml/storage.sgml
src/backend/catalog/system_views.sql
src/backend/postmaster/postmaster.c
src/backend/postmaster/syslogger.c
src/backend/replication/basebackup.c
src/backend/utils/adt/misc.c
src/bin/pg_basebackup/t/010_pg_basebackup.pl
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/postmaster/syslogger.h

index 1b390a257ae793410f79920f07ad986b3625f17f..cd82c04b051ccb644c7c8aec53bf184783c84f26 100644 (file)
@@ -4280,6 +4280,11 @@ SELECT * FROM parent WHERE key = 2400;
       <primary>where to log</primary>
      </indexterm>
 
+     <indexterm>
+       <primary>current_logfiles</primary>
+       <secondary>and the log_destination configuration parameter</secondary>
+     </indexterm>
+
      <variablelist>
 
      <varlistentry id="guc-log-destination" xreflabel="log_destination">
@@ -4310,6 +4315,27 @@ SELECT * FROM parent WHERE key = 2400;
         <xref linkend="guc-logging-collector"> must be enabled to generate
         CSV-format log output.
        </para>
+       <para>
+        When either <systemitem>stderr</systemitem> or
+        <systemitem>csvlog</systemitem> are included, the file
+        <filename>current_logfiles</> is created to record the location
+        of the log file(s) currently in use by the logging collector and the
+        associated logging destination. This provides a convenient way to
+        find the logs currently in use by the instance. Here is an example of
+        this file's content:
+<programlisting>
+stderr pg_log/postgresql.log
+csvlog pg_log/postgresql.csv
+</programlisting>
+
+        <filename>current_logfiles</filename> is recreated when a new log file
+        is created as an effect of rotation, and
+        when <varname>log_destination</> is reloaded.  It is removed when
+        neither <systemitem>stderr</systemitem>
+        nor <systemitem>csvlog</systemitem> are included
+        in <varname>log_destination</>, and when the logging collector is
+        disabled.
+       </para>
 
        <note>
         <para>
index 71ad729ab01db495f68cf9f66fbba377b0074e53..9e084adc1aca9cf09df5586e125d34eec33b78b9 100644 (file)
@@ -15478,6 +15478,13 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
        <entry>configuration load time</entry>
       </row>
 
+      <row>
+       <entry><literal><function>pg_current_logfile(<optional><type>text</></optional>)</function></literal></entry>
+       <entry><type>text</type></entry>
+       <entry>Primary log file name, or log in the requested format,
+       currently in use by the logging collector</entry>
+      </row>
+
       <row>
        <entry><literal><function>pg_my_temp_schema()</function></literal></entry>
        <entry><type>oid</type></entry>
@@ -15696,6 +15703,45 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
     the time when the postmaster process re-read the configuration files.)
    </para>
 
+   <indexterm>
+    <primary>pg_current_logfile</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>Logging</primary>
+    <secondary>pg_current_logfile function</secondary>
+   </indexterm>
+
+   <indexterm>
+     <primary>current_logfiles</primary>
+     <secondary>and the pg_current_logfile function</secondary>
+   </indexterm>
+
+   <indexterm>
+    <primary>Logging</primary>
+    <secondary>current_logfiles file and the pg_current_logfile
+    function</secondary>
+   </indexterm>
+
+   <para>
+    <function>pg_current_logfile</function> returns, as <type>text</type>,
+    the path of the log file(s) currently in use by the logging collector.
+    The path includes the <xref linkend="guc-log-directory"> directory
+    and the log file name.  Log collection must be enabled or the return value
+    is <literal>NULL</literal>.  When multiple log files exist, each in a
+    different format, <function>pg_current_logfile</function> called
+    without arguments returns the path of the file having the first format
+    found in the ordered list: <systemitem>stderr</>, <systemitem>csvlog</>.
+    <literal>NULL</literal> is returned when no log file has any of these
+    formats.  To request a specific file format supply, as <type>text</type>,
+    either <systemitem>csvlog</> or <systemitem>stderr</> as the value of the
+    optional parameter. The return value is <literal>NULL</literal> when the
+    log format requested is not a configured
+    <xref linkend="guc-log-destination">.  The
+    <function>pg_current_logfiles</function> reflects the contents of the
+    <filename>current_logfiles</> file.
+   </para>
+
    <indexterm>
     <primary>pg_my_temp_schema</primary>
    </indexterm>
index 127b759c14f03edd0825a7ba07e89014dfa46384..e0a89861f86687e7ef333ecdca69441725a87717 100644 (file)
@@ -60,6 +60,12 @@ Item
  <entry>Subdirectory containing per-database subdirectories</entry>
 </row>
 
+<row>
+ <entry><filename>current_logfiles</></entry>
+ <entry>File recording the log file(s) currently written to by the logging
+  collector</entry>
+</row>
+
 <row>
  <entry><filename>global</></entry>
  <entry>Subdirectory containing cluster-wide tables, such as
index 38be9cf1a0cb8475f760bd3a5d7170673c6bdefe..ada542c530b2f67d64ef37bf3ad978f9551f89d1 100644 (file)
@@ -1091,6 +1091,8 @@ REVOKE EXECUTE ON FUNCTION pg_wal_replay_pause() FROM public;
 REVOKE EXECUTE ON FUNCTION pg_wal_replay_resume() FROM public;
 REVOKE EXECUTE ON FUNCTION pg_rotate_logfile() FROM public;
 REVOKE EXECUTE ON FUNCTION pg_reload_conf() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_current_logfile() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_current_logfile(text) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public;
 REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public;
index 2cf17ac42e00a8d95a7dd278a772c3e0e8d2fb80..68313424ee26e3462d060bea9afebd3e17da861d 100644 (file)
@@ -1232,6 +1232,13 @@ PostmasterMain(int argc, char *argv[])
     */
    RemovePromoteSignalFiles();
 
+   /* Remove any outdated file holding the current log filenames. */
+   if (unlink(LOG_METAINFO_DATAFILE) < 0 && errno != ENOENT)
+       ereport(LOG,
+               (errcode_for_file_access(),
+                errmsg("could not remove file \"%s\": %m",
+                   LOG_METAINFO_DATAFILE)));
+
    /*
     * If enabled, start up syslogger collection subprocess
     */
index 13a03014ebb86f5168596c51101e317fe4b2b173..aaefdaebad9f8536e1bd7ff3681f100a30fccbf2 100644 (file)
@@ -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 update_metainfo_datafile(void);
 
 
 /*
@@ -282,6 +283,7 @@ SysLoggerMain(int argc, char *argv[])
    currentLogRotationAge = Log_RotationAge;
    /* set next planned rotation time */
    set_next_rotation_time();
+   update_metainfo_datafile();
 
    /* main worker loop */
    for (;;)
@@ -348,6 +350,13 @@ SysLoggerMain(int argc, char *argv[])
                rotation_disabled = false;
                rotation_requested = true;
            }
+
+           /*
+            * Force rewriting last log filename when reloading configuration.
+            * Even if rotation_requested is false, log_destination may have
+            * been changed and we don't want to wait the next file rotation.
+            */
+           update_metainfo_datafile();
        }
 
        if (Log_RotationAge > 0 && !rotation_disabled)
@@ -1098,6 +1107,8 @@ open_csvlogfile(void)
        pfree(last_csv_file_name);
 
    last_csv_file_name = filename;
+
+   update_metainfo_datafile();
 }
 
 /*
@@ -1268,6 +1279,8 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
    if (csvfilename)
        pfree(csvfilename);
 
+   update_metainfo_datafile();
+
    set_next_rotation_time();
 }
 
@@ -1337,6 +1350,72 @@ set_next_rotation_time(void)
    next_rotation_time = now;
 }
 
+/*
+ * Store the name of the file(s) where the log collector, when enabled, writes
+ * log messages.  Useful for finding the name(s) of the current log file(s)
+ * when there is time-based logfile rotation.  Filenames are stored in a
+ * temporary file and which is renamed into the final destination for
+ * atomicity.
+ */
+static void
+update_metainfo_datafile(void)
+{
+   FILE    *fh;
+
+   if (!(Log_destination & LOG_DESTINATION_STDERR) &&
+       !(Log_destination & LOG_DESTINATION_CSVLOG))
+   {
+       if (unlink(LOG_METAINFO_DATAFILE) < 0 && errno != ENOENT)
+           ereport(LOG,
+                   (errcode_for_file_access(),
+                    errmsg("could not remove file \"%s\": %m",
+                       LOG_METAINFO_DATAFILE)));
+       return;
+   }
+
+   if ((fh = logfile_open(LOG_METAINFO_DATAFILE_TMP, "w", true)) == NULL)
+   {
+       ereport(LOG,
+               (errcode_for_file_access(),
+                errmsg("could not open file \"%s\": %m",
+                   LOG_METAINFO_DATAFILE_TMP)));
+       return;
+   }
+
+   if (last_file_name && (Log_destination & LOG_DESTINATION_STDERR))
+   {
+       if (fprintf(fh, "stderr %s\n", last_file_name) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_file_access(),
+                   errmsg("could not write file \"%s\": %m",
+                           LOG_METAINFO_DATAFILE_TMP)));
+           fclose(fh);
+           return;
+       }
+   }
+
+   if (last_csv_file_name && (Log_destination & LOG_DESTINATION_CSVLOG))
+   {
+       if (fprintf(fh, "csvlog %s\n", last_csv_file_name) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_file_access(),
+                   errmsg("could not write file \"%s\": %m",
+                           LOG_METAINFO_DATAFILE_TMP)));
+           fclose(fh);
+           return;
+       }
+   }
+   fclose(fh);
+
+   if (rename(LOG_METAINFO_DATAFILE_TMP, LOG_METAINFO_DATAFILE) != 0)
+       ereport(LOG,
+               (errcode_for_file_access(),
+               errmsg("could not rename file \"%s\" to \"%s\": %m",
+                      LOG_METAINFO_DATAFILE_TMP, LOG_METAINFO_DATAFILE)));
+}
+
 /* --------------------------------
  *     signal handler routines
  * --------------------------------
index 7414048f4eb54c80ed7075a6a57180fd31b64c85..e3a7ad5e9ab20fc622d1351c28c4524743402e13 100644 (file)
@@ -26,6 +26,7 @@
 #include "nodes/pg_list.h"
 #include "pgtar.h"
 #include "pgstat.h"
+#include "postmaster/syslogger.h"
 #include "replication/basebackup.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -147,6 +148,9 @@ static const char *excludeFiles[] =
    /* Skip auto conf temporary file. */
    PG_AUTOCONF_FILENAME ".tmp",
 
+   /* Skip current log file temporary file */
+   LOG_METAINFO_DATAFILE_TMP,
+
    /*
     * If there's a backup_label or tablespace_map file, it belongs to a
     * backup started by the user with pg_start_backup().  It is *not* correct
index 8f7c1f81fd44cdd1b1c64ce31c320ba7d497cedc..ff6a25d2b60233a356afb60616443bd67fd8f8f2 100644 (file)
@@ -885,3 +885,106 @@ parse_ident(PG_FUNCTION_ARGS)
 
    PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
 }
+
+/*
+ * pg_current_logfile
+ *
+ * Report current log file used by log collector by scanning current_logfiles.
+ */
+Datum
+pg_current_logfile(PG_FUNCTION_ARGS)
+{
+   FILE       *fd;
+   char        lbuffer[MAXPGPATH];
+   char       *logfmt;
+   char       *log_filepath;
+   char       *log_format = lbuffer;
+   char       *nlpos;
+
+   /* The log format parameter is optional */
+   if (PG_NARGS() == 0 || PG_ARGISNULL(0))
+       logfmt = NULL;
+   else
+   {
+       logfmt = text_to_cstring(PG_GETARG_TEXT_PP(0));
+
+       if (strcmp(logfmt, "stderr") != 0 && strcmp(logfmt, "csvlog") != 0)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("log format \"%s\" is not supported", logfmt),
+                    errhint("The supported log formats are \"stderr\" and \"csvlog\".")));
+   }
+
+   fd = AllocateFile(LOG_METAINFO_DATAFILE, "r");
+   if (fd == NULL)
+   {
+       if (errno != ENOENT)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not read file \"%s\": %m",
+                           LOG_METAINFO_DATAFILE)));
+       PG_RETURN_NULL();
+   }
+
+   /*
+    * Read the file to gather current log filename(s) registered by the
+    * syslogger.
+    */
+   while (fgets(lbuffer, sizeof(lbuffer), fd) != NULL)
+   {
+       /*
+        * Extract log format and log file path from the line; lbuffer ==
+        * log_format, they share storage.
+        */
+       log_filepath = strchr(lbuffer, ' ');
+       if (log_filepath == NULL)
+       {
+           /*
+            * No space found, file content is corrupted.  Return NULL to the
+            * caller and inform him on the situation.
+            */
+           elog(ERROR,
+                "missing space character in \"%s\"", LOG_METAINFO_DATAFILE);
+           break;
+       }
+
+       *log_filepath = '\0';
+       log_filepath++;
+       nlpos = strchr(log_filepath, '\n');
+       if (nlpos == NULL)
+       {
+           /*
+            * No newlinei found, file content is corrupted.  Return NULL to
+            * the caller and inform him on the situation.
+            */
+           elog(ERROR,
+              "missing newline character in \"%s\"", LOG_METAINFO_DATAFILE);
+           break;
+       }
+       *nlpos = '\0';
+
+       if (logfmt == NULL || strcmp(logfmt, log_format) == 0)
+       {
+           FreeFile(fd);
+           PG_RETURN_TEXT_P(cstring_to_text(log_filepath));
+       }
+   }
+
+   /* Close the current log filename file. */
+   FreeFile(fd);
+
+   PG_RETURN_NULL();
+}
+
+/*
+ * Report current log file used by log collector (1 argument version)
+ *
+ * note: this wrapper is necessary to pass the sanity check in opr_sanity,
+ * which checks that all built-in functions that share the implementing C
+ * function take the same number of arguments
+ */
+Datum
+pg_current_logfile_1arg(PG_FUNCTION_ARGS)
+{
+   return pg_current_logfile(fcinfo);
+}
index 29f519d8c99bd002ce8eee6c41407e3a7f03aae6..aafb138fd53a3804e0d4ab54238c8a322300e213 100644 (file)
@@ -4,7 +4,7 @@ use Cwd;
 use Config;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 72;
+use Test::More tests => 73;
 
 program_help_ok('pg_basebackup');
 program_version_ok('pg_basebackup');
@@ -56,7 +56,7 @@ close CONF;
 $node->restart;
 
 # Write some files to test that they are not copied.
-foreach my $filename (qw(backup_label tablespace_map postgresql.auto.conf.tmp))
+foreach my $filename (qw(backup_label tablespace_map postgresql.auto.conf.tmp current_logfiles.tmp))
 {
    open FILE, ">>$pgdata/$filename";
    print FILE "DONOTCOPY";
@@ -83,7 +83,7 @@ foreach my $dirname (qw(pg_dynshmem pg_notify pg_replslot pg_serial pg_snapshots
 }
 
 # These files should not be copied.
-foreach my $filename (qw(postgresql.auto.conf.tmp postmaster.opts postmaster.pid tablespace_map))
+foreach my $filename (qw(postgresql.auto.conf.tmp postmaster.opts postmaster.pid tablespace_map current_logfiles.tmp))
 {
    ok(! -f "$tempdir/backup/$filename", "$filename not copied");
 }
index 90456fa668f4454306d643263724097cca2ce075..57fbc9509ed745a8569c967756defc90bda9cb3c 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201703011
+#define CATALOG_VERSION_NO 201703031
 
 #endif
index 4b9c6e75b0529d9145764b372e507c748cc30bb2..0c8b5c630d84d135a8d9e8cae8e990e11003718e 100644 (file)
@@ -3191,6 +3191,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 f 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 f f v s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_current_logfile_1arg _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");
index c187a5f23ed25cdd512d18e62ba4eaea9e476a50..94d7eac347f54e655fe637bddb4fe9fbd3798762 100644 (file)
@@ -87,4 +87,11 @@ extern void write_syslogger_file(const char *buffer, int count, int dest);
 extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
 #endif
 
+/*
+ * Name of files saving meta-data information about the log
+ * files currently in use by the syslogger
+ */
+#define LOG_METAINFO_DATAFILE  "current_logfiles"
+#define LOG_METAINFO_DATAFILE_TMP  LOG_METAINFO_DATAFILE ".tmp"
+
 #endif   /* _SYSLOGGER_H */