diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4b5ee81..313403e 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -15027,6 +15027,12 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); + pg_current_logfile() + text + current log file used by the logging collector + + + pg_notification_queue_usage() double fraction of the asynchronous notification queue currently occupied (0-1) @@ -15264,6 +15270,16 @@ SET search_path TO schema , schema, .. + pg_current_logfile + + + + pg_current_logfile returns the name of the current log + file used by the logging collector, as a text. Log collection + must be active. + + + pg_postmaster_start_time diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml index 9b2e09e..41aaf5d 100644 --- a/doc/src/sgml/storage.sgml +++ b/doc/src/sgml/storage.sgml @@ -166,7 +166,8 @@ last started with Unix-domain socket directory path (empty on Windows), first valid listen_address (IP address or *, or empty if not listening on TCP), - and shared memory segment ID + the shared memory segment ID, + and the path to the current log file used by the syslogger (this file is not present after server shutdown) diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c index e7e488a..40fec7a 100644 --- a/src/backend/postmaster/syslogger.c +++ b/src/backend/postmaster/syslogger.c @@ -54,7 +54,6 @@ */ #define READ_BUF_SIZE (2 * PIPE_CHUNK_SIZE) - /* * GUC parameters. Logging_collector cannot be changed after postmaster * start, but the rest can change at SIGHUP. @@ -571,6 +570,8 @@ SysLogger_Start(void) syslogFile = logfile_open(filename, "a", false); + AddToDataDirLockFile(LOCK_FILE_LINE_LOG_FILENAME, filename); + pfree(filename); #ifdef EXEC_BACKEND @@ -1209,6 +1210,9 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) fclose(syslogFile); syslogFile = fh; + /* Store current log filename */ + AddToDataDirLockFile(LOCK_FILE_LINE_LOG_FILENAME, filename); + /* instead of pfree'ing filename, remember it for next time */ if (last_file_name != NULL) pfree(last_file_name); @@ -1253,6 +1257,9 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) fclose(csvlogFile); csvlogFile = fh; + /* Store current log filename */ + AddToDataDirLockFile(LOCK_FILE_LINE_LOG_FILENAME, csvfilename); + /* instead of pfree'ing filename, remember it for next time */ if (last_csv_file_name != NULL) pfree(last_csv_file_name); @@ -1262,6 +1269,7 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for) if (filename) pfree(filename); + if (csvfilename) pfree(csvfilename); @@ -1362,3 +1370,4 @@ sigUsr1Handler(SIGNAL_ARGS) errno = save_errno; } + diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 43f36db..2bc2283 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "access/sysattr.h" #include "catalog/catalog.h" @@ -43,7 +44,6 @@ #define atooid(x) ((Oid) strtoul((x), NULL, 10)) - /* * Common subroutine for num_nulls() and num_nonnulls(). * Returns TRUE if successful, FALSE if function should return NULL. @@ -719,3 +719,22 @@ pg_column_is_updatable(PG_FUNCTION_ARGS) PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS); } + + +/* + * Report current log file used by log collector + */ +Datum +pg_current_logfile(PG_FUNCTION_ARGS) +{ + + if (!Logging_collector) + { + ereport(WARNING, + (errmsg("current log can not be reported because log collection is not active"))); + PG_RETURN_NULL(); + } + + PG_RETURN_TEXT_P(cstring_to_text(get_current_log_filename())); +} + diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 18f5e6f..f38c3fc 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -49,8 +49,6 @@ #include "utils/syscache.h" -#define DIRECTORY_LOCK_FILE "postmaster.pid" - ProcessingMode Mode = InitProcessing; /* List of lock files to be removed at proc exit */ @@ -1490,3 +1488,188 @@ pg_bindtextdomain(const char *domain) } #endif } + +/* + * get the lines from postmaster.pid file - return NULL if file can't be opened + */ +char ** +read_pidfile(const char *path) +{ + int fd; + int nlines; + char **result; + char *buffer; + char *linebegin; + int i; + int n; + int len; + struct stat statbuf; + + /* + * Slurp the file into memory. + * + * The file can change concurrently, so we read the whole file into memory + * with a single read() call. That's not guaranteed to get an atomic + * snapshot, but in practice, for a small file, it's close enough for the + * current use. + */ + fd = open(path, O_RDONLY | PG_BINARY, 0); + if (fd < 0) + return NULL; + if (fstat(fd, &statbuf) < 0) + { + close(fd); + return NULL; + } + if (statbuf.st_size == 0) + { + /* empty file */ + close(fd); + result = (char **) malloc(sizeof(char *)); + *result = NULL; + return result; + } + buffer = malloc(statbuf.st_size + 1); + if (buffer == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + return NULL; + } + len = read(fd, buffer, statbuf.st_size + 1); + close(fd); + if (len != statbuf.st_size) + { + /* oops, the file size changed between fstat and read */ + free(buffer); + return NULL; + } + + /* + * Count newlines. We expect there to be a newline after each full line, + * including one at the end of file. If there isn't a newline at the end, + * any characters after the last newline will be ignored. + */ + nlines = 0; + for (i = 0; i < len; i++) + { + if (buffer[i] == '\n') + nlines++; + } + + /* set up the result buffer */ + result = (char **) malloc((nlines + 1) * sizeof(char *)); + if (result == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + return NULL; + } + + /* now split the buffer into lines */ + linebegin = buffer; + n = 0; + for (i = 0; i < len; i++) + { + if (buffer[i] == '\n') + { + int slen = &buffer[i] - linebegin + 1; + char *linebuf = malloc(slen + 1); + if (linebuf == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + return NULL; + } + + memcpy(linebuf, linebegin, slen); + linebuf[slen] = '\0'; + result[n++] = linebuf; + linebegin = &buffer[i + 1]; + } + } + result[n] = NULL; + + free(buffer); + + return result; +} + +/* + * Free memory allocated for optlines through read_pidfile() + */ +void +free_read_pidfile(char **optlines) +{ + char *curr_line = NULL; + int i = 0; + + if (!optlines) + return; + + while ((curr_line = optlines[i++])) + free(curr_line); + + free(optlines); + + return; +} + +/* + * Find the current log file or NULL when not available. + */ +char * +get_current_log_filename(void) +{ + int i; + char **optlines; + char *current_log; + + /* Try to read the postmaster.pid file */ + if ((optlines = read_pidfile(DIRECTORY_LOCK_FILE)) != NULL && + optlines[0] != NULL && + optlines[1] != NULL && + optlines[2] != NULL) + { + /* Only 9.6+ server register the current log file in pid file + * and only with stderr redirection and a time-based rotation. + * Return null otherwise. + */ + if (optlines[7] == NULL) + { + return NULL; + } + else + { + /* + * Extract current log filename + */ + current_log = palloc(MAXPGPATH); + snprintf(current_log, MAXPGPATH, "%s", optlines[LOCK_FILE_LINE_LOG_FILENAME - 1]); + + /* remove trailing newline */ + if (strchr(current_log, '\n') != NULL) + *strchr(current_log, '\n') = '\0'; + + /* Fail if couldn't get current log */ + if (current_log[0] == '\0') + { + return NULL; + } + } + } + + /* + * Free the results of readfile. + * + * This is safe to call even if optlines is NULL. + */ + free_read_pidfile(optlines); + + /* return current log filename */ + return current_log; +} + diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index cbbb883..5aa95b0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3120,7 +3120,8 @@ DESCR("true if xlog replay is paused"); DATA(insert OID = 2621 ( pg_reload_conf PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_reload_conf _null_ _null_ _null_ )); 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 = 3794 ( pg_current_logfile PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _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 cc7833e..23ec7f3 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -447,6 +447,9 @@ extern char *local_preload_libraries_string; #define LOCK_FILE_LINE_SOCKET_DIR 5 #define LOCK_FILE_LINE_LISTEN_ADDR 6 #define LOCK_FILE_LINE_SHMEM_KEY 7 +#define LOCK_FILE_LINE_LOG_FILENAME 8 + +#define DIRECTORY_LOCK_FILE "postmaster.pid" extern void CreateDataDirLockFile(bool amPostmaster); extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster, @@ -460,6 +463,10 @@ extern void process_session_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); extern bool has_rolreplication(Oid roleid); +extern char **read_pidfile(const char *path); +extern void free_read_pidfile(char **optlines); +extern char *get_current_log_filename(void); + /* in access/transam/xlog.c */ extern bool BackupInProgress(void); extern void CancelBackup(void); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 115f8af..fd2c4ea 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -509,6 +509,7 @@ extern Datum pg_typeof(PG_FUNCTION_ARGS); 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 pg_current_logfile(PG_FUNCTION_ARGS); /* oid.c */ extern Datum oidin(PG_FUNCTION_ARGS);