Add pg_current_logfile() function.

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.
This commit is contained in:
Robert Haas 2017-03-03 11:32:45 +05:30
parent aea5d29836
commit 19dc233c32
12 changed files with 288 additions and 4 deletions

View 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>

View 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>

View 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

View 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;

View 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
*/

View 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
* --------------------------------

View 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

View 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);
}

View 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");
}

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201703011
#define CATALOG_VERSION_NO 201703031
#endif

View 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");

View 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 */