Hi Karl, I've attached the v15 of the patch that applies your changes/fixes and remove the call to strtok().
I've not applied patch patch_pg_current_logfile-v14.diff.backoff to prevent constant call of logfile_writename() on a busy system (errno = ENFILE | EMFILE). I think this can be done quite simply by testing if log rotate is still enabled. This is possible because function logfile_rotate() is already testing if errno = ENFILE | EMFILE and in this case rotation_disabled is set to true. So the following test should do the work: if (log_metainfo_stale && !rotation_disabled) logfile_writename(); This is included in v15 patch. I've also not added patches patch_pg_current_logfile-v14.diff.conditional, patch_pg_current_logfile-v14.diff.logdest_change and patch_pg_current_logfile-v14.diff.logdest_change-part2 because the case your are trying to fix with lot of code can not appears. You said that when csv logging is turned back on, the stale csv path is written to current_logfiles. This is not the case, last_csv_file_name is immediately changed when the log file is open, see open_csvlogfile(). After running your use case I was not able to reproduce the bug you are describing. Again, patches patch_pg_current_logfile-v14.diff.doc_linux_default-v2 have not been included because I don't see any reason to talk especially about systemd. If you talk about systemd you must talk about other stderr handler by all systems. IMO saying that current_logfile is present only if logging_collector is enabled and log_destination include stderr or/and csvlog is enough, no need to talk about systemd and behavior of Linux distributions. Regards Le 23/11/2016 à 10:21, Karl O. Pinc a écrit : > Hi Gilles, > > On Sat, 19 Nov 2016 12:58:47 +0100 > Gilles Darold <gilles.dar...@dalibo.com> wrote: > >> ... attached v14 of the patch. > Attached are patches for your consideration and review. > (Including your latest v14 patch for completeness.) > > Some of the attached patches (like the GUC symbol > patch you've seen before) are marked to be submitted > as separate patches to the maintainers when we send > them code for review. These need looking over by > somebody, I hope you, before they get sent on so > please comment on these if you're not going to look > at them or if you see a problem with them. (Or if > you like them. :) Thanks. > > I also have comments at the bottom regards problems > I see but haven't patched. > > --- > > patch_pg_current_logfile-v14.diff > > Applies on top of master. > > The current patch. > > --- > > patch_pg_current_logfile-v14.diff.startup_docs > > For consideration of inclusion in "main" patch. > > Applies on top of patch_pg_current_logfile-v14.diff > > A documentation fix. > > (Part of) my previous doc patch was wrong; current_logfiles is not > unconditionally written on startup. > > --- > > patch_pg_current_logfile-v14.diff.bool_to_int > > Do not include in "main" patch, submit to maintainers separately. > > Applies on top of patch_pg_current_logfile-v14.diff > > The bool types on the stack in logfile_rotate() are > my work. Bools on the stack don't make sense as far > as hardware goes, so the compiler's optimizer should change > them to int. This patch changes the bools to ints > should that be to someone's taste. > > --- > > patch_pg_current_logfile-v14.diff.logdest_change > > For consideration of inclusion in "main" patch. > > Applies on top of patch_pg_current_logfile-v14.diff. > > Fixes a bug where, when log_destination changes and the config > file is reloaded, a no-longer-used logfile path may be written > to the current_logfiles file. The chain of events would be > as follows: > > 1) PG configured with csvlog in log_destination. Logs are written. > > This makes last_csv_file_name non-NULL. > > > 2) PG config changed to remove csvlog from log_destination > and SIGHUP sent. > > This removes the csvlog path from current_logfiles but does not > make last_csv_file_name NULL. last_csv_file_name has the old > path in it. > > > 3) PG configured to add csvlog back to log_destination and > SIGHUP sent. > > When csvlogging is turned back on, the stale csv path is written > to current_logfiles. This is overwritten as soon as some csv logs > are written because the new csv logfile must be opened, but this is > still a problem. Even if it happens to be impossible at the moment > to get past step 2 to step 3 without having some logs written it seems > a lot more robust to manually "expire" the last*_file_name variable > content when log_destination changes. > > > So what the patch does is "scrub" the "last_*file_name" variables > when the config file changes. > > FWIW, I moved the logfile_writename() call toward the top of the > if block just to keep all the code which sorta-kinda involves > the old_log_destination variable together. > > --- > > patch_pg_current_logfile-v14.diff.logdest_change-part2 > > Do not include in "main" patch, submit to maintainers separately. > > Applies on top of patch_pg_current_logfile-v14.diff.logdest_change > > Adds a PGLogDestination typedef. A separate patch since this may be > overkill and more about coding style than anything else. > > --- > > And now, a series of patches to fix the problem where, at least > potentially, logfile_writename() gets a ENFILE or EMFILE and > therefore a log file path does not ever get written to the > current_logfiles file. The point of this patch series is to retry until > the content of current_logfiles is correct. > > All of these are for consideration of inclusion in "main" patch. > > > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part1 > > Applies on top of patch_pg_current_logfile-v14.diff.logdest_change > > A documentation patch. Notes that (even with retrying) the > current_logfiles content might not be right. > > > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part2 > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part1 > > Remove arguments from logfile_writename(). Use static storage > instead. We always update last_file_name and last_csv_file_name > whenever logfile_writename() is called so there's not much of > a change here. > > > > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part2 > > Re-try the write of current_logfiles should it fail because the > system is too busy. > > --- > > patch_pg_current_logfile-v14.diff.backoff > > Do not include in "main" patch, submit to maintainers separately. > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > > Introduces a backoff when retrying to write the current_logfiles file, > because retrying when failure is due to a busy system only makes the > system more busy. This may well be over-engineering but I thought > I'd present the code and let more experienced people decide. > > I have yet to really test this patch. > > --- > > The remaining patches have to do with coding style and code clarity. > > --- > > patch_pg_current_logfile-v14.diff.deletion > > For consideration of inclusion in "main" patch, otherwise submit to > maintainers separately. > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > > I find the code to be more understandable if the deletion > of the current_logfiles file is separated from the updating > of current_logfiles. It's not that without this patch > that unnecessary code execution is inefficient, it's that > the unnecessary code execution makes it hard (for me) to think about > what's happening where and why. > > --- > > patch_pg_current_logfile-v14.diff.conditional > > For consideration of inclusion in "main" patch, otherwise submit to > maintainers separately. > > Applies on top of patch_pg_current_logfile-v14.diff.deletion > (Could be changed to apply on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3.) > > Only change the current_logfiles content on SIGHUP when the content needs > to be changed. > > As above, I understand the code more readily when statements are only > executed when they need to be executed. > > My analysis here is as follows: > > There are 9 cases, the combination of 2 sets of 3 possibilities. > > syslog is removed from log_destination > the presence or absence of syslog in log_destination is unchanged > syslog is added to log_destination > > the same 3 cases only regards csvlog > > In the no-change case we don't need to do anything > > If either syslog or csvlog are added to log_destination then > the current_logfiles file will be updated on rotation or, in > the case of adding a csvlog file, on file open. > > It is only the removal cases that require the current_logfiles > file be changed, and that's what the patch does. > > --- > > patch_pg_current_logfile-v14.diff.codecomment > > Do not include in "main" patch, submit to maintainers separately. > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > > Add a comment to the code explaining how an unusual case can come to > pass. > > --- > > patch_pg_current_logfile-v14.diff.cleanup_rotate > > Do not include in "main" patch, submit to maintainers separately. > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > > Removes a net 10 lines of code, eliminating unnecessary if statements. > (Refactoring aside, pfree() does nothing when given a NULL pointer.) > > It might be a little cleaner to move the declarations of "filename" and > "csvfilename" into the if blocks that use them, but I didn't do this. > > --- > > A series of patches which creates symbols for some GUC values which > appear in multiple places in the code. > > Do not include in "main" patch, submit to maintainers separately. > > > patch_pg_current_logfile-v14.diff.abstract_guc_part1 > > Applies on top of > patch_pg_current_logfile-v14.diff.retry_current_logfiles-part3 > (And also applies on top of master branch, or really, any of the > other patches presented in this email.) > > Changes code in master branch to create symbols for some GUC values. > > > patch_pg_current_logfile-v14.diff.abstract_guc_part2 > > Applies on top of patch_pg_current_logfile-v14.diff.abstract_guc_part1 > (and also applies on top of patch_pg_current_logfile-v14.diff, or > probably any of the other patches in this email) > > Changes code in this patch series to use symbols for some GUC values. > > --- > > I've just started to look at pg_current_logfile() in > src/backend/utils/adt/misc.c. I believe I've noticed a couple > of problems. I probably won't have time to write patches until > next week so I thought I'd simply report what I see and > let you review what I've got so far. > > It looks like under some conditions (typically errors) you > can return an uninitialized value in log_filename. The > answer here is probably to write : > > log_filename[0] = 0; > > somewhere toward the top of the function. And (as now written) > you also need to put the same statement after the error that > reports "unexpected line format in file %s" to be sure that > in that case you're not going to return a pointer to the C > NULL value. > > But also, you can't use strtok() to parse lbuffer because > the path you're returning can contain a space. (I suspect using > strstr() to parse on the the first space you find is the way > to go. You can use the same trick that strtok() uses of sticking > a 0 byte in in place of the space to get the log format string.) > > --- > > I'm going to put together a patch, to be sent separately to the > maintainers when we ask them for final review, which shows what the > docs/code looks like when current_logfiles always exists and can be an > empty file. And argue for that. But I've not gotten to this yet. > > The good news is that I don't see anything else in syslogger.c to > comment on or patch. :-) > > Regards, > > > Karl <k...@meme.com> > Free Software: "You don't pay back, you pay forward." > -- Robert A. Heinlein -- Gilles Darold Consultant PostgreSQL http://dalibo.com - http://dalibo.org
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index dcd0663..cd60268 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -4144,6 +4144,12 @@ SELECT * FROM parent WHERE key = 2400; <primary>server log</primary> </indexterm> + <para>When logs are written to the file-system their paths, names, and + types are recorded in + the <xref linkend="storage-pgdata-current-logfiles"> file. This provides + a convenient way to find and access log content without establishing a + database connection.</para> + <sect2 id="runtime-config-logging-where"> <title>Where To Log</title> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 2e64cc4..08d9f46 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -15443,6 +15443,19 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); </row> <row> + <entry><literal><function>pg_current_logfile()</function></literal></entry> + <entry><type>text</type></entry> + <entry>primary log file name in use by the logging collector</entry> + </row> + + <row> + <entry><literal><function>pg_current_logfile(<type>text</>)</function></literal></entry> + <entry><type>text</type></entry> + <entry>log file name, of log in the requested format, in use by the + logging collector</entry> + </row> + + <row> <entry><literal><function>pg_my_temp_schema()</function></literal></entry> <entry><type>oid</type></entry> <entry>OID of session's temporary schema, or 0 if none</entry> @@ -15660,6 +15673,39 @@ 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_logile</primary> + </indexterm> + + <indexterm> + <primary>Logging</primary> + <secondary>pg_current_logfile function</secondary> + </indexterm> + + <para> + <function>pg_current_logfile</function> returns, as <type>text</type>, + the path of either the csv or stderr log file currently in use by the + logging collector. This is a path including the + <xref linkend="guc-log-directory"> directory and the log file name. + Log collection must be active or the return value + is <literal>NULL</literal>. When multiple logfiles 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">. + + <function>pg_current_logfiles</function> reflects the content of the + <xref linkend="storage-pgdata-current-logfiles"> file. All caveats + regards <filename>current_logfiles</filename> content are applicable + to <function>pg_current_logfiles</function>' return value. + </para> + <indexterm> <primary>pg_my_temp_schema</primary> </indexterm> diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml index 5c52824..928133c 100644 --- a/doc/src/sgml/storage.sgml +++ b/doc/src/sgml/storage.sgml @@ -60,6 +60,79 @@ Item <entry>Subdirectory containing per-database subdirectories</entry> </row> +<row id="storage-pgdata-current-logfiles" xreflabel="current_logfiles"> + <entry> + <indexterm> + <primary><filename>current_logfiles</filename></primary> + </indexterm> + <indexterm> + <primary>Logging</primary> + <secondary><filename>current_logfiles</filename> file</secondary> + </indexterm> + <filename>current_logfiles</> + </entry> + <entry> + <para>A file recording the log file(s) currently written to by the syslogger + and the file's log formats, <systemitem>stderr</> + or <systemitem>csvlog</>. Each line of the file is a space separated list of + two elements: the log format and the full path to the log file including the + value of <xref linkend="guc-log-directory">. The log format must be present + in <xref linkend="guc-log-destination"> to be listed in + <filename>current_logfiles</filename>.</para> + + <note> + <indexterm> + <primary><application>pg_ctl</application></primary> + <secondary>and <filename>current_logfiles</filename></secondary> + </indexterm> + <indexterm> + <primary><filename>stderr</filename></primary> + <secondary>and <filename>current_logfiles</filename></secondary> + </indexterm> + <indexterm> + <primary>log_destination configuration parameter</primary> + <secondary>and <filename>current_logfiles</filename></secondary> + </indexterm> + + <para>Although logs directed to <filename>stderr</filename> may be written + to the filesystem, when the writing of <filename>stderr</filename> is + managed outside of the <productname>PostgreSQL</productname> database + server the location of such files in the filesystem is not reflected in + the content of <filename>current_logfiles</filename>. One such case is + when the <application>pg_ctl</application> command is used to start + the <command>postgres</command> database server, capture + the <filename>stderr</filename> output of the server, and direct it to a + file.</para> + + <para>There are other notable situations related + to <filename>stderr</filename> logging. + Non-<productname>PostgreSQL</productname> log sources, such as 3rd party + libraries, which deliver error messages directly + to <filename>stderr</filename> are always logged + by <productname>PostgreSQL</productname> + to <filename>stderr</filename>. <Filename>Stderr</Filename> is also the + destination for any incomplete log messages produced by + <productname>PostgreSQL</productname>. When + <systemitem>stderr</systemitem> is not in + <xref linkend="guc-log-destination">, + <filename>current_logfiles</filename> does not not contain the name of the + file where these sorts of log messages are written.</para> + </note> + + <para>The <filename>current_logfiles</filename> file + is present only when <xref linkend="guc-logging-collector"> is + activated and when at least one of <systemitem>stderr</> or + <systemitem>csvlog</> value is present in + <xref linkend="guc-log-destination">.</para> + + <para>On a busy system <filename>current_logfiles</filename> might not be + updated immediately upon logfile rotation. On an extremely busy system it + is possible, although highly unlikely, for a logfile to be "skipped" and + never appear in <filename>current_logfiles</filename>.</para> + </entry> + +</row> + <row> <entry><filename>global</></entry> <entry>Subdirectory containing cluster-wide tables, such as diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c index fd62d66..2092576 100644 --- a/src/backend/postmaster/syslogger.c +++ b/src/backend/postmaster/syslogger.c @@ -86,6 +86,7 @@ static FILE *csvlogFile = NULL; NON_EXEC_STATIC pg_time_t first_syslogger_file_time = 0; static char *last_file_name = NULL; static char *last_csv_file_name = NULL; +static bool log_metainfo_stale = false; /* * Buffers for saving partial messages from different backends. @@ -146,6 +147,8 @@ 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 bool rm_log_metainfo(void); +static void logfile_writename(void); /* @@ -298,6 +301,16 @@ SysLoggerMain(int argc, char *argv[]) /* Clear any already-pending wakeups */ ResetLatch(MyLatch); + /* On a busy system the logfile meta information may not have been + * written so retry. (The cost of this is doing extra I/O on an + * already busy system.) Write the info before SIGHUP config file + * re-read in an attempt to not ever "skip" writing a logfile + * filename. + */ + if (log_metainfo_stale && !rotation_disabled) + logfile_writename(); + + /* * Process any requests or signals received recently. */ @@ -348,6 +361,14 @@ 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. + */ + if (!rm_log_metainfo()) + logfile_writename(); } if (Log_RotationAge > 0 && !rotation_disabled) @@ -511,10 +532,14 @@ int SysLogger_Start(void) { pid_t sysloggerPid; - char *filename; - if (!Logging_collector) + if (!Logging_collector) { + /* Logging collector is not enabled. We don't know where messages are + * logged. Remove outdated file holding the current log filenames. + */ + unlink(LOG_METAINFO_DATAFILE); return 0; + } /* * If first time through, create the pipe which will receive stderr @@ -570,11 +595,13 @@ SysLogger_Start(void) * a time-based rotation. */ first_syslogger_file_time = time(NULL); - filename = logfile_getname(first_syslogger_file_time, NULL); + last_file_name = logfile_getname(first_syslogger_file_time, NULL); - syslogFile = logfile_open(filename, "a", false); + syslogFile = logfile_open(last_file_name, "a", false); - pfree(filename); + if (!rm_log_metainfo()) + logfile_writename(); + pfree(last_file_name); #ifdef EXEC_BACKEND switch ((sysloggerPid = syslogger_forkexec())) @@ -1098,6 +1125,8 @@ open_csvlogfile(void) pfree(last_csv_file_name); last_csv_file_name = filename; + + logfile_writename(); } /* @@ -1151,9 +1180,11 @@ static void logfile_rotate(bool time_based_rotation, int size_rotation_for) { char *filename; - char *csvfilename = NULL; + char *csvfilename; pg_time_t fntime; FILE *fh; + bool rotate_csvlog; + bool rotate_stderr; rotation_requested = false; @@ -1166,9 +1197,6 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) fntime = next_rotation_time; else fntime = time(NULL); - filename = logfile_getname(fntime, NULL); - if (csvlogFile != NULL) - csvfilename = logfile_getname(fntime, ".csv"); /* * Decide whether to overwrite or append. We can overwrite if (a) @@ -1178,8 +1206,12 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) * * Note: last_file_name should never be NULL here, but if it is, append. */ - if (time_based_rotation || (size_rotation_for & LOG_DESTINATION_STDERR)) + rotate_stderr = time_based_rotation || + (size_rotation_for & LOG_DESTINATION_STDERR); + if (rotate_stderr) { + filename = logfile_getname(fntime, NULL); + if (Log_truncate_on_rotation && time_based_rotation && last_file_name != NULL && strcmp(filename, last_file_name) != 0) @@ -1202,10 +1234,7 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) rotation_disabled = true; } - if (filename) - pfree(filename); - if (csvfilename) - pfree(csvfilename); + pfree(filename); return; } @@ -1216,14 +1245,16 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) if (last_file_name != NULL) pfree(last_file_name); last_file_name = filename; - filename = NULL; } /* Same as above, but for csv file. */ - - if (csvlogFile != NULL && - (time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG))) + rotate_csvlog = csvlogFile != NULL && + (time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG)); + if (rotate_csvlog) { + if (csvlogFile != NULL) + csvfilename = logfile_getname(fntime, ".csv"); + if (Log_truncate_on_rotation && time_based_rotation && last_csv_file_name != NULL && strcmp(csvfilename, last_csv_file_name) != 0) @@ -1246,10 +1277,7 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) rotation_disabled = true; } - if (filename) - pfree(filename); - if (csvfilename) - pfree(csvfilename); + pfree(csvfilename); return; } @@ -1260,13 +1288,10 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) if (last_csv_file_name != NULL) pfree(last_csv_file_name); last_csv_file_name = csvfilename; - csvfilename = NULL; } - if (filename) - pfree(filename); - if (csvfilename) - pfree(csvfilename); + if (rotate_stderr || rotate_csvlog) + logfile_writename(); set_next_rotation_time(); } @@ -1365,3 +1390,81 @@ sigUsr1Handler(SIGNAL_ARGS) errno = save_errno; } + +/* + * 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 renamed into the final destination for atomicity. + */ +static void +logfile_writename() +{ + FILE *fh; + char tempfn[MAXPGPATH]; + + snprintf(tempfn, sizeof(tempfn), "%s.tmp", LOG_METAINFO_DATAFILE); + + if ((fh = logfile_open(tempfn, "w", true) ) == NULL) + { + if (errno == ENFILE || errno == EMFILE) + /* The system is too busy to write logfile meta info. + * This is not too suprising on a busy system. + * Try again when logs are next written. */ + log_metainfo_stale = true; + 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 stderr log file path \"%s\": %m", + tempfn))); + 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 csvlog log file path \"%s\": %m", + tempfn))); + fclose(fh); + return; + } + } + fclose(fh); + + if (rename(tempfn, LOG_METAINFO_DATAFILE) != 0) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not rename file \"%s\": %m", + tempfn))); + return; + } + + log_metainfo_stale = false; +} + +/* + * Delete the LOG_METAINFO_DATAFILE if it's not going to be used. + */ +static bool +rm_log_metainfo(void) +{ + if (!(Log_destination & LOG_DESTINATION_STDERR) + && !(Log_destination & LOG_DESTINATION_CSVLOG)) + { + unlink(LOG_METAINFO_DATAFILE); + return true; + } + return false; +} + diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 0da051a..dd75acc 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,125 @@ 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 lbuffer[MAXPGPATH]; + text *fmt; + char *logfmt; + struct stat stat_buf; + char log_format[15]; + char log_filename[MAXPGPATH]; + + /* The log format parameter is optional */ + 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(LOG_METAINFO_DATAFILE, &stat_buf) != 0) + PG_RETURN_NULL(); + + 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) { + + /* Check for a read error. */ + if (ferror(fd)) { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", LOG_METAINFO_DATAFILE))); + FreeFile(fd); + break; + } + + /* remove trailing newline */ + if (strchr(lbuffer, '\n') != NULL) + *strchr(lbuffer, '\n') = '\0'; + + /* extract log format and log file path from the line */ + if (sscanf(lbuffer, "%s %s", log_format, log_filename) != 2) + { + ereport(ERROR, + (errmsg("unexpected line format in file %s", LOG_METAINFO_DATAFILE))); + break; + } + + /* + * When no log format is provided as argument always reports + * the first registered log file in LOG_METAINFO_DATAFILE. + */ + if (logfmt == NULL) + break; + + /* report the entry corresponding to the requested format */ + if (strcmp(logfmt, log_format) == 0) + break; + } + /* Close the current log filename file. */ + if (FreeFile(fd)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", LOG_METAINFO_DATAFILE))); + + + 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) + && !(Log_destination & LOG_DESTINATION_CSVLOG) ) + PG_RETURN_NULL(); + + /* Return null when stderr is requested but we have a csv log */ + if ( (logfmt != NULL) && (strcmp(logfmt, "stderr") == 0) + && !(Log_destination & LOG_DESTINATION_STDERR) ) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(cstring_to_text(log_filename)); +} + +/* + * 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); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 047a1ce..26db448 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3181,6 +3181,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"); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 78c9954..36a5b58 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -466,4 +466,13 @@ 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 holding the paths, names, and types of the files where current + * log messages are written, when the log collector is enabled. Useful + * outside of Postgres when finding the name of the current log file in the + * case of time-based log rotation. + */ +#define LOG_METAINFO_DATAFILE "current_logfiles" + #endif /* MISCADMIN_H */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 90f5132..e532022 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -522,6 +522,8 @@ 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); +extern Datum pg_current_logfile_1arg(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