From 17b11e5d51ecaeb6a94adb45768da74a52a46df8 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 09:44:55 -0500 Subject: [PATCH 01/11] Return value coherent to logging type macros. The as_logging_type() function must return the default logging type PGAGROAL_LOGGING_TYPE_CONSOLE that has the value 0. This does not change the behaviour of the application but makes the code coherent with other configuration parsing functions. --- src/libpgagroal/configuration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 5e68ebac..e1998110 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -2252,7 +2252,7 @@ as_logging_type(char* str) if (!strcasecmp(str, "syslog")) return PGAGROAL_LOGGING_TYPE_SYSLOG; - return 0; + return PGAGROAL_LOGGING_TYPE_CONSOLE; } static int From b54e9b8992aeb3abad7cf4b9fb4e600aef0b6f85 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Wed, 9 Feb 2022 05:41:51 -0500 Subject: [PATCH 02/11] [#45] Log rotation support. This is a possible implementation of log rotation. The idea is to have the following configuration option: - log_rotation_size expresses the bytes of max size for a file before its rotation; - log_rotation_age expresses the seconds since the last rotation. In order to allow log rotation, log_path must allow for strftime(3) format strings. Moreover, there is the need for a set of utility functions to quickly open and rotate the log file. The truncation is performed via the already existing log_mode configuration option. The parsing of log_roation_size accepts a number (bytes) and a few suffixes, like 'M' for megabytes. Similarly, log_rotation_age accepts a number (seconds) and a few suffixes like 'd' for days. Only one suffix is supported at a time. If the logging is done to console or in general not to a file, the rotation is disabled. There is a global variable within logginc.g that keeps track about the current filename as "expanded" from strftime, so that when a rotation is required, the file can be flushed and closed and a new one can be opened. Close #45 --- doc/CONFIGURATION.md | 4 +- src/include/logging.h | 19 +++ src/include/pgagroal.h | 3 + src/libpgagroal/configuration.c | 201 +++++++++++++++++++++++++++ src/libpgagroal/logging.c | 238 ++++++++++++++++++++++++++++---- 5 files changed, 434 insertions(+), 31 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 39ad7350..dc17f891 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -30,7 +30,9 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | | log_path | pgagroal.log | String | No | The log file location | -| log_mode | append | String | No | Append to or create the log file (append, create) | +| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as minutes. Supports suffixes: 'H' (hours), 'D' (days), 'W' (weeks), 'M' (months) and 'Y' (years) | +| log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | +| log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | | log_connections | `off` | Bool | No | Log connects | | log_disconnections | `off` | Bool | No | Log disconnects | | blocking_timeout | 30 | Int | No | The number of seconds the process will be blocking for a connection (disable = 0) | diff --git a/src/include/logging.h b/src/include/logging.h index ffd1bbf3..626076e8 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -52,6 +52,9 @@ extern "C" { #define PGAGROAL_LOGGING_MODE_CREATE 0 #define PGAGROAL_LOGGING_MODE_APPEND 1 +#define PGAGROAL_LOGGING_ROTATION_DISABLED -1 + + #define pgagroal_log_trace(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG5, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_debug(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG1, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_info(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) @@ -86,6 +89,22 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...); void pgagroal_log_mem(void* data, size_t size); + +bool +log_rotation_enabled(void); + +void +log_rotation_disable(void); + +bool +log_rotation_required(void); + +bool +log_rotation_set_next_rotation_age(void); + +int +log_file_open(void); + #ifdef __cplusplus } #endif diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index c51cf8a4..25803631 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -304,6 +304,9 @@ struct configuration bool log_disconnections; /**< Log disconnects */ int log_mode; /**< The logging mode */ atomic_schar log_lock; /**< The logging lock */ + int log_rotation_size; /**< bytes to force log rotation */ + int log_rotation_age; /**< minutes for log rotation */ + bool log_truncate; /**< Truncate an existing log on rotation */ bool authquery; /**< Is authentication query enabled */ diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 5e68ebac..b48ed1e8 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -49,6 +49,7 @@ #ifdef HAVE_LINUX #include #endif +#include #define LINE_LENGTH 512 @@ -58,6 +59,8 @@ static int as_bool(char* str, bool* b); static int as_logging_type(char* str); static int as_logging_level(char* str); static int as_logging_mode(char* str); +static int as_logging_rotation_size(char* str, int* size); +static int as_logging_rotation_age(char* str, int* age); static int as_validation(char* str); static int as_pipeline(char* str); static int as_hugepage(char* str); @@ -594,6 +597,36 @@ pgagroal_read_configuration(void* shm, char* filename) unknown = true; } } + else if (!strcmp(key, "log_rotation_size")) + { + if (!strcmp(section, "pgagroal")) + { + if (as_logging_rotation_size(value, &config->log_rotation_size)) + { + unknown = true; + } + } + else + { + unknown = true; + } + + } + else if (!strcmp(key, "log_rotation_age")) + { + if (!strcmp(section, "pgagroal")) + { + if (as_logging_rotation_age(value, &config->log_rotation_age)) + { + unknown = true; + } + } + else + { + unknown = true; + } + + } else if (!strcmp(key, "log_connections")) { if (!strcmp(section, "pgagroal")) @@ -2749,3 +2782,171 @@ is_empty_string(char* s) return true; } + + +/** + * Parses a string to see if it contains + * a valid value for log rotation size. + * Returns 0 if parsing ok, 1 otherwise. + * + * Valid strings have one of the suffixes: + * - k for kilobytes + * - m for megabytes + * - g for gigabytes + * + * The default is expressed always as kilobytes. The functions sets the + * rotation size in kilobytes. + */ +static int +as_logging_rotation_size(char* str, int* size) +{ + int multiplier = 1; + int index; + char value[MISC_LENGTH]; + bool multiplier_set = false; + + if (is_empty_string(str)) + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 0; + } + + index = 0; + for (int i = 0; i < strlen(str); i++) + { + if (isdigit(str[i])) + { + value[index++] = str[i]; + } + else if (isalpha(str[i]) && multiplier_set) + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + else if (isalpha(str[i]) && ! multiplier_set) + { + + if (str[i] == 'M' || str[i] == 'm') + { + multiplier = 1024 * 1024; + multiplier_set = true; + } + else if(str[i] == 'G' || str[i] == 'g') + { + multiplier = 1024 * 1024 * 1024; + multiplier_set = true; + } + else if(str[i] == 'K' || str[i] == 'k') + { + multiplier = 1024; + multiplier_set = true; + } + else if(str[i] == 'B' || str[i] == 'b') + { + multiplier = 1; + multiplier_set = true; + } + } + else + // ignore alien chars + continue; + } + + value[index] = '\0'; + if (!as_int(value, size)) + { + *size = *size * multiplier; + return 0; + } + else + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + +} + + +/** + * Parses the log_rotation_age string. + * The string accepts + * - h for hours + * - d for days + * - w for weeks + * - m for months + * - y for years + * + * The default is expressed in minutes. + * The function sets the number of rotationg age as minutes. + * Returns 1 for errors, 0 for correct parsing. + */ +static int +as_logging_rotation_age(char* str, int* age) +{ + int multiplier = 1; + int index; + char value[MISC_LENGTH]; + bool multiplier_set = false; + + if (is_empty_string(str)) + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 0; + } + + index = 0; + for (int i = 0; i < strlen(str); i++) + { + if (isdigit(str[i])) + { + value[index++] = str[i]; + } + else if (isalpha(str[i]) && multiplier_set) + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + else if (isalpha(str[i]) && ! multiplier_set) + { + if (str[i] == 'h' || str[i] == 'H') + { + multiplier = 60; + multiplier_set = true; + } + else if (str[i] == 'd' || str[i] == 'D') + { + multiplier = 24 * 60; + multiplier_set = true; + } + else if (str[i] == 'w' || str[i] == 'W') + { + multiplier = 24 * 60 * 7; + multiplier_set = true; + } + else if (str[i] == 'm' || str[i] == 'M') + { + multiplier = 24 * 60 * 7 * 31; + multiplier_set = true; + } + else if (str[i] == 'y' || str[i] == 'Y') + { + multiplier = 24 * 60 * 365; + multiplier_set = true; + } + } + else + continue; + } + + value[index] = '\0'; + if (!as_int(value, age)) + { + *age = *age * multiplier; + return 0; + } + else + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } +} diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index 97d08a5d..0ec21c70 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -36,11 +36,20 @@ #include #include #include +#include +#include +#include +#include +#include #define LINE_LENGTH 32 FILE *log_file; +time_t next_log_rotation_age; /* number of seconds at which the next location will happen */ + +char current_log_path[MAX_PATH]; /* the current log file */ + static const char* levels[] = { "TRACE", @@ -61,6 +70,120 @@ static const char* colors[] = "\x1b[35m" }; + +/** + * Utility function to understand if log rotation + * is enabled or not. + * Returns `true` if the rotation is enabled. + */ +bool +log_rotation_enabled(void) +{ + struct configuration* config; + config = (struct configuration*)shmem; + + // disable log rotation in the case + // logging is not to a file + if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) + { + return false; + } + + // log rotation is enabled if either log_rotation_age or + // log_rotation_size is enabled + return config->log_rotation_age != PGAGROAL_LOGGING_ROTATION_DISABLED + || config->log_rotation_size != PGAGROAL_LOGGING_ROTATION_DISABLED; +} + +/** + * Forces a disabling of the log rotation. + * Useful when the system cannot determine how to rotate logs. + */ +void +log_rotation_disable(void) +{ + struct configuration* config; + config = (struct configuration*)shmem; + + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + config->log_rotation_size = PGAGROAL_LOGGING_ROTATION_DISABLED; + next_log_rotation_age = 0; +} + +/** + * Checks if there are the requirements to perform a log rotation. + * It returns true in either the case of the size exceeded or + * the age exceeded. The age is contained into a global + * variable 'next_log_rotation_age' that express the number + * of seconds at which the next rotation will be performed. + */ +bool +log_rotation_required(void) +{ + struct stat log_stat; + struct configuration* config; + + config = (struct configuration*)shmem; + + if (!log_rotation_enabled()) + { + return false; + } + + if (stat(current_log_path, &log_stat)) + { + return false; + } + + if (config->log_rotation_size > 0 && log_stat.st_size >= config->log_rotation_size) + { + return true; + } + + if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age >= log_stat.st_ctime) + { + return true; + } + + return false; +} + +/** + * Function to compute the next instant at which a log rotation + * will happen. It computes only if the logging is to a file + * and only if the configuration tells to compute the rotation + * age. + * Returns true on success. + */ +bool +log_rotation_set_next_rotation_age(void) +{ + struct configuration* config; + time_t now; + + config = (struct configuration*)shmem; + + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && config->log_rotation_age > 0) + { + now = time( NULL ); + if (!now) + { + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; + } + + next_log_rotation_age = now + config->log_rotation_age * 60; + return true; + } + else + { + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; + } + +} + + /** * */ @@ -73,35 +196,19 @@ pgagroal_init_logging(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - if (strlen(config->log_path) > 0) - { - if (config->log_mode == PGAGROAL_LOGGING_MODE_APPEND) - { - log_file = fopen(config->log_path, "a"); - } - else - { - log_file = fopen(config->log_path, "w"); - } - } - else - { - if (config->log_mode == PGAGROAL_LOGGING_MODE_APPEND) - { - log_file = fopen("pgagroal.log", "a"); - } - else - { - log_file = fopen("pgagroal.log", "w"); - } - } + log_file_open(); if (!log_file) { printf("Failed to open log file %s due to %s\n", strlen(config->log_path) > 0 ? config->log_path : "pgagroal.log", strerror(errno)); errno = 0; + log_rotation_disable(); return 1; } + else + { + log_rotation_set_next_rotation_age(); + } } return 0; @@ -119,14 +226,7 @@ pgagroal_start_logging(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - if (strlen(config->log_path) > 0) - { - log_file = fopen(config->log_path, "a"); - } - else - { - log_file = fopen("pgagroal.log", "a"); - } + log_file_open(); if (!log_file) { @@ -143,6 +243,79 @@ pgagroal_start_logging(void) return 0; } +/** + * Opens the log file defined in the configuration. + * Works only for a real log file, i.e., the configuration + * must be set up to log to a file, not console. + * + * The function considers the settings in the configuration + * to determine the mode (append, create) and the filename + * to open. + * + * It sets the global variable 'log_file'. + * + * Returns 0 on success, 1 on error. + */ +int +log_file_open(void) +{ + struct configuration* config; + time_t htime; + struct tm *tm; + + config = (struct configuration*)shmem; + + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) + { + htime = time( NULL ); + if (!htime) + { + log_file = NULL; + return 1; + } + + tm = localtime(&htime); + if (tm == NULL ) + { + log_file = NULL; + return 1; + } + + if (strftime(current_log_path, sizeof(current_log_path), config->log_path, tm) <= 0 ) + { + // cannot parse the format string, fallback to default logging + memcpy(current_log_path, "pgagroal.log", strlen("pgagroal.log")); + log_rotation_disable(); + } + + log_file = fopen(current_log_path, + config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); + return 0; + } + + return 1; +} + +/** + * Performs a log file rotation. + * It flushes and closes the current log file, + * then re-opens it. + * + * DO NOT LOG WITHIN THIS FUNCTION as long as this + * is invoked by log_line + */ +void +log_file_rotate(void) +{ + if (log_rotation_enabled()) + { + fflush(log_file); + fclose(log_file); + log_file_open(); + } +} + + /** * */ @@ -229,6 +402,11 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) vfprintf(log_file, fmt, vl); fprintf(log_file, "\n"); fflush(log_file); + + if (log_rotation_required()) + { + log_file_rotate(); + } } else if (config->log_type == PGAGROAL_LOGGING_TYPE_SYSLOG) { From 6845c2cb39713c23880904305db05292dab887c4 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 05:22:22 -0500 Subject: [PATCH 03/11] Force log rotation disabled when logging to console. Ensure that the log rotation will always be disabled if the system is configured to log to something different than a file. Improvements on log rotation. log_rotation_age is now expressed as seconds. Avoid re-creating the log file when init + start are called in sequence. --- doc/CONFIGURATION.md | 2 +- src/libpgagroal/configuration.c | 26 +++++++++++------------ src/libpgagroal/logging.c | 37 +++++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index dc17f891..e28427d2 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -30,7 +30,7 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | | log_path | pgagroal.log | String | No | The log file location | -| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as minutes. Supports suffixes: 'H' (hours), 'D' (days), 'W' (weeks), 'M' (months) and 'Y' (years) | +| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as seconds. Supports suffixes: 'S' (seconds, the default), 'M' (minutes), 'H' (hours), 'D' (days), 'W' (weeks) | | log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | | log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | | log_connections | `off` | Bool | No | Log connects | diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index b48ed1e8..2bfbeff2 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -2870,13 +2870,13 @@ as_logging_rotation_size(char* str, int* size) /** * Parses the log_rotation_age string. * The string accepts + * - s for seconds + * - m for minutes * - h for hours * - d for days * - w for weeks - * - m for months - * - y for years * - * The default is expressed in minutes. + * The default is expressed in seconds. * The function sets the number of rotationg age as minutes. * Returns 1 for errors, 0 for correct parsing. */ @@ -2908,29 +2908,29 @@ as_logging_rotation_age(char* str, int* age) } else if (isalpha(str[i]) && ! multiplier_set) { - if (str[i] == 'h' || str[i] == 'H') + if (str[i] == 's' || str[i] == 'S') { - multiplier = 60; + multiplier = 1; multiplier_set = true; } - else if (str[i] == 'd' || str[i] == 'D') + else if (str[i] == 'm' || str[i] == 'M') { - multiplier = 24 * 60; + multiplier = 60; multiplier_set = true; } - else if (str[i] == 'w' || str[i] == 'W') + else if (str[i] == 'h' || str[i] == 'H') { - multiplier = 24 * 60 * 7; + multiplier = 3600; multiplier_set = true; } - else if (str[i] == 'm' || str[i] == 'M') + else if (str[i] == 'd' || str[i] == 'D') { - multiplier = 24 * 60 * 7 * 31; + multiplier = 24 * 3600; multiplier_set = true; } - else if (str[i] == 'y' || str[i] == 'Y') + else if (str[i] == 'w' || str[i] == 'W') { - multiplier = 24 * 60 * 365; + multiplier = 24 * 3600 * 7; multiplier_set = true; } } diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index 0ec21c70..b2f0dc1e 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -86,6 +86,7 @@ log_rotation_enabled(void) // logging is not to a file if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) { + log_rotation_disable(); return false; } @@ -140,7 +141,7 @@ log_rotation_required(void) return true; } - if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age >= log_stat.st_ctime) + if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age <= log_stat.st_ctime) { return true; } @@ -165,14 +166,14 @@ log_rotation_set_next_rotation_age(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && config->log_rotation_age > 0) { - now = time( NULL ); + now = time(NULL); if (!now) { config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; return false; } - next_log_rotation_age = now + config->log_rotation_age * 60; + next_log_rotation_age = now + config->log_rotation_age; return true; } else @@ -205,10 +206,6 @@ pgagroal_init_logging(void) log_rotation_disable(); return 1; } - else - { - log_rotation_set_next_rotation_age(); - } } return 0; @@ -224,7 +221,7 @@ pgagroal_start_logging(void) config = (struct configuration*)shmem; - if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && !log_file) { log_file_open(); @@ -254,6 +251,13 @@ pgagroal_start_logging(void) * * It sets the global variable 'log_file'. * + * If it succeed in opening the log file, it calls + * the log_rotation_set_next_rotation_age() function to + * determine the next instant at which the log file + * must be rotated. Calling such function is safe + * because if the log rotation is disabled, the function + * does nothing. + * * Returns 0 on success, 1 on error. */ int @@ -290,6 +294,20 @@ log_file_open(void) log_file = fopen(current_log_path, config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); + + if (!log_file) + return 1; + + log_rotation_set_next_rotation_age(); + /* printf("\n\nLog file %s initialized %d, rotation %sable (every %d bytes or %d seconds or so, next estimated at %ld epoch, now is %d)\n\n", + current_log_path, + config->log_level, + log_rotation_enabled() ? "en" : "dis", + config->log_rotation_size, + config->log_rotation_age, + (long) next_log_rotation_age, + (long) htime); + */ return 0; } @@ -405,7 +423,8 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) if (log_rotation_required()) { - log_file_rotate(); + printf("\nLOG ROTATION\n\n"); + log_file_rotate(); } } else if (config->log_type == PGAGROAL_LOGGING_TYPE_SYSLOG) From a3d5fd5aad64daede1130220affd6330431a2aab Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 10:59:48 -0500 Subject: [PATCH 04/11] [#44] Support for custom log line prefix. This introduces the configuration parameter `log_line_prefix` that accepts a strftime(3) valid string that is parsed and stored at the very first log entry in the configuration structure (`config->log_line_prefix`). Logging to a file and to the console is done using such prefix via strftime. Documentation updated. Close #44 --- doc/CONFIGURATION.md | 3 ++- src/include/logging.h | 1 + src/include/pgagroal.h | 2 +- src/libpgagroal/configuration.c | 16 ++++++++++++++++ src/libpgagroal/logging.c | 25 ++++++++++--------------- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index e28427d2..490d99ae 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -29,7 +29,8 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | management | 0 | Int | No | The remote management port (disable = 0) | | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | -| log_path | pgagroal.log | String | No | The log file location | +| log_line_prefix | %Y-%m-%d %H:%M:%S | String | No | A prefix to place on every log entry, accepts escape sequences that strftime(3) can parse. No spaces between the string parts are allowed. | +| log_path | pgagroal.log | String | No | The log file location. Can include escape sequences that strftime(3) accepts, e.g., pgagroal-%Y-%m-%d-%H-%M-%S.log | | log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as seconds. Supports suffixes: 'S' (seconds, the default), 'M' (minutes), 'H' (hours), 'D' (days), 'W' (weeks) | | log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | | log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | diff --git a/src/include/logging.h b/src/include/logging.h index 626076e8..5a80de9d 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -54,6 +54,7 @@ extern "C" { #define PGAGROAL_LOGGING_ROTATION_DISABLED -1 +#define PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX "%Y-%m-%d %H:%M:%S" #define pgagroal_log_trace(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG5, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_debug(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG1, __FILE__, __LINE__, __VA_ARGS__) diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index 25803631..81362ea4 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -306,7 +306,7 @@ struct configuration atomic_schar log_lock; /**< The logging lock */ int log_rotation_size; /**< bytes to force log rotation */ int log_rotation_age; /**< minutes for log rotation */ - bool log_truncate; /**< Truncate an existing log on rotation */ + char log_line_prefix[MISC_LENGTH]; /**< The logging prefix */ bool authquery; /**< Is authentication query enabled */ diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 2bfbeff2..59ce242d 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -626,6 +626,22 @@ pgagroal_read_configuration(void* shm, char* filename) unknown = true; } + } + else if (!strcmp(key, "log_line_prefix")) + { + if (!strcmp(section, "pgagroal")) + { + max = strlen(value); + if (max > MISC_LENGTH - 1) + max = MISC_LENGTH - 1; + + memcpy(config->log_line_prefix, value, max); + } + else + { + unknown = true; + } + } else if (!strcmp(key, "log_connections")) { diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index b2f0dc1e..5d71499b 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -86,8 +86,8 @@ log_rotation_enabled(void) // logging is not to a file if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) { - log_rotation_disable(); - return false; + log_rotation_disable(); + return false; } // log rotation is enabled if either log_rotation_age or @@ -203,7 +203,7 @@ pgagroal_init_logging(void) { printf("Failed to open log file %s due to %s\n", strlen(config->log_path) > 0 ? config->log_path : "pgagroal.log", strerror(errno)); errno = 0; - log_rotation_disable(); + log_rotation_disable(); return 1; } } @@ -299,15 +299,6 @@ log_file_open(void) return 1; log_rotation_set_next_rotation_age(); - /* printf("\n\nLog file %s initialized %d, rotation %sable (every %d bytes or %d seconds or so, next estimated at %ld epoch, now is %d)\n\n", - current_log_path, - config->log_level, - log_rotation_enabled() ? "en" : "dis", - config->log_rotation_size, - config->log_rotation_age, - (long) next_log_rotation_age, - (long) htime); - */ return 0; } @@ -400,11 +391,16 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) filename = file; } + if (config->log_line_prefix == NULL || strlen(config->log_line_prefix) == 0) + { + memcpy(config->log_line_prefix,PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX,strlen(PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX)); + } + va_start(vl, fmt); if (config->log_type == PGAGROAL_LOGGING_TYPE_CONSOLE) { - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)] = '\0'; + buf[strftime(buf, sizeof(buf), config->log_line_prefix, tm)] = '\0'; fprintf(stdout, "%s %s%-5s\x1b[0m \x1b[90m%s:%d\x1b[0m ", buf, colors[level - 1], levels[level - 1], filename, line); @@ -414,7 +410,7 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) } else if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)] = '\0'; + buf[strftime(buf, sizeof(buf), config->log_line_prefix, tm)] = '\0'; fprintf(log_file, "%s %-5s %s:%d ", buf, levels[level - 1], filename, line); vfprintf(log_file, fmt, vl); @@ -423,7 +419,6 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) if (log_rotation_required()) { - printf("\nLOG ROTATION\n\n"); log_file_rotate(); } } From 882e013051cce6b40f99688f47ab94cc6ba484f2 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 12:01:46 -0500 Subject: [PATCH 05/11] Utabify --- src/libpgagroal/configuration.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 59ce242d..824bf07a 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -627,15 +627,15 @@ pgagroal_read_configuration(void* shm, char* filename) } } - else if (!strcmp(key, "log_line_prefix")) + else if (!strcmp(key, "log_line_prefix")) { if (!strcmp(section, "pgagroal")) { max = strlen(value); - if (max > MISC_LENGTH - 1) - max = MISC_LENGTH - 1; + if (max > MISC_LENGTH - 1) + max = MISC_LENGTH - 1; - memcpy(config->log_line_prefix, value, max); + memcpy(config->log_line_prefix, value, max); } else { From 96c917d3af212e3b81a7a35c5044130490f756bb Mon Sep 17 00:00:00 2001 From: jesperpedersen Date: Thu, 24 Feb 2022 14:35:23 -0500 Subject: [PATCH 06/11] [#214] Flush the old primary in case of a failover --- src/include/pool.h | 7 +++++ src/libpgagroal/pool.c | 64 +++++++++++++++++++++++++++++++++++++++- src/libpgagroal/server.c | 13 ++++++-- src/main.c | 20 ++++++++++++- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/include/pool.h b/src/include/pool.h index 5ef3c2d3..f5e816a0 100644 --- a/src/include/pool.h +++ b/src/include/pool.h @@ -91,6 +91,13 @@ pgagroal_validation(void); void pgagroal_flush(int mode, char* database); +/** + * Flush the pool for a specific server + * @param server The server + */ +void +pgagroal_flush_server(signed char server); + /** * Prefill the pool * @param initial Use initial size diff --git a/src/libpgagroal/pool.c b/src/libpgagroal/pool.c index bd2e13f4..8909ce75 100644 --- a/src/libpgagroal/pool.c +++ b/src/libpgagroal/pool.c @@ -193,7 +193,7 @@ pgagroal_get_connection(char* username, char* database, bool reuse, bool transac if (!fork()) { - pgagroal_flush(FLUSH_GRACEFULLY, "*"); + pgagroal_flush_server(server); } if (config->failover) @@ -798,6 +798,68 @@ pgagroal_flush(int mode, char* database) exit(0); } +void +pgagroal_flush_server(signed char server) +{ + struct configuration* config; + + pgagroal_start_logging(); + pgagroal_memory_init(); + + config = (struct configuration*)shmem; + + pgagroal_log_debug("pgagroal_flush_server"); + for (int i = 0; i < config->max_connections; i++) + { + if (config->connections[i].server == server) + { + switch (atomic_load(&config->states[i])) + { + case STATE_NOTINIT: + case STATE_INIT: + /* Do nothing */ + break; + case STATE_FREE: + atomic_store(&config->states[i], STATE_GRACEFULLY); + if (pgagroal_socket_isvalid(config->connections[i].fd)) + { + pgagroal_write_terminate(NULL, config->connections[i].fd); + } + pgagroal_prometheus_connection_flush(); + pgagroal_tracking_event_slot(TRACKER_FLUSH, i); + pgagroal_kill_connection(i, NULL); + break; + case STATE_IN_USE: + case STATE_GRACEFULLY: + case STATE_FLUSH: + atomic_store(&config->states[i], STATE_GRACEFULLY); + break; + case STATE_IDLE_CHECK: + case STATE_VALIDATION: + case STATE_REMOVE: + atomic_store(&config->states[i], STATE_GRACEFULLY); + break; + default: + break; + } + } + } + + if (config->number_of_users > 0 && config->number_of_limits > 0) + { + if (!fork()) + { + pgagroal_prefill(false); + } + } + + pgagroal_pool_status(); + pgagroal_memory_destroy(); + pgagroal_stop_logging(); + + exit(0); +} + void pgagroal_prefill(bool initial) { diff --git a/src/libpgagroal/server.c b/src/libpgagroal/server.c index b6a45942..8ec7be29 100644 --- a/src/libpgagroal/server.c +++ b/src/libpgagroal/server.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -216,7 +217,8 @@ int pgagroal_server_failover(int slot) { signed char primary; - int old_primary; + signed char old_primary; + int ret = 1; struct configuration* config = NULL; config = (struct configuration*)shmem; @@ -227,10 +229,15 @@ pgagroal_server_failover(int slot) if (atomic_compare_exchange_strong(&config->servers[old_primary].state, &primary, SERVER_FAILOVER)) { - return failover(config->connections[slot].server); + ret = failover(old_primary); + + if (!fork()) + { + pgagroal_flush_server(old_primary); + } } - return 1; + return ret; } int diff --git a/src/main.c b/src/main.c index 12f5dc7e..36fdb438 100644 --- a/src/main.c +++ b/src/main.c @@ -1430,12 +1430,30 @@ accept_mgt_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) break; case MANAGEMENT_SWITCH_TO: pgagroal_log_debug("pgagroal: Management switch to"); + int old_primary = -1; + signed char server_state; + for (int i = 0; old_primary == -1 && i < config->number_of_servers; i++) + { + server_state = atomic_load(&config->servers[i].state); + if (server_state == SERVER_PRIMARY) + { + old_primary = i; + } + } + if (!pgagroal_server_switch(payload_s)) { if (!fork()) { shutdown_ports(); - pgagroal_flush(FLUSH_GRACEFULLY, "*"); + if (old_primary != -1) + { + pgagroal_flush_server(old_primary); + } + else + { + pgagroal_flush(FLUSH_GRACEFULLY, "*"); + } } pgagroal_prometheus_failed_servers(); } From 48c9f4bc480234cfe8c13378c6aae2de4fff7923 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Wed, 9 Feb 2022 05:41:51 -0500 Subject: [PATCH 07/11] [#45] Log rotation support. This is a possible implementation of log rotation. The idea is to have the following configuration option: - log_rotation_size expresses the bytes of max size for a file before its rotation; - log_rotation_age expresses the seconds since the last rotation. In order to allow log rotation, log_path must allow for strftime(3) format strings. Moreover, there is the need for a set of utility functions to quickly open and rotate the log file. The truncation is performed via the already existing log_mode configuration option. The parsing of log_roation_size accepts a number (bytes) and a few suffixes, like 'M' for megabytes. Similarly, log_rotation_age accepts a number (seconds) and a few suffixes like 'd' for days. Only one suffix is supported at a time. If the logging is done to console or in general not to a file, the rotation is disabled. There is a global variable within logginc.g that keeps track about the current filename as "expanded" from strftime, so that when a rotation is required, the file can be flushed and closed and a new one can be opened. Close #45 --- doc/CONFIGURATION.md | 4 +- src/include/logging.h | 19 +++ src/include/pgagroal.h | 3 + src/libpgagroal/configuration.c | 201 +++++++++++++++++++++++++++ src/libpgagroal/logging.c | 238 ++++++++++++++++++++++++++++---- 5 files changed, 434 insertions(+), 31 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 39ad7350..dc17f891 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -30,7 +30,9 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | | log_path | pgagroal.log | String | No | The log file location | -| log_mode | append | String | No | Append to or create the log file (append, create) | +| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as minutes. Supports suffixes: 'H' (hours), 'D' (days), 'W' (weeks), 'M' (months) and 'Y' (years) | +| log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | +| log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | | log_connections | `off` | Bool | No | Log connects | | log_disconnections | `off` | Bool | No | Log disconnects | | blocking_timeout | 30 | Int | No | The number of seconds the process will be blocking for a connection (disable = 0) | diff --git a/src/include/logging.h b/src/include/logging.h index ffd1bbf3..626076e8 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -52,6 +52,9 @@ extern "C" { #define PGAGROAL_LOGGING_MODE_CREATE 0 #define PGAGROAL_LOGGING_MODE_APPEND 1 +#define PGAGROAL_LOGGING_ROTATION_DISABLED -1 + + #define pgagroal_log_trace(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG5, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_debug(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG1, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_info(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) @@ -86,6 +89,22 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...); void pgagroal_log_mem(void* data, size_t size); + +bool +log_rotation_enabled(void); + +void +log_rotation_disable(void); + +bool +log_rotation_required(void); + +bool +log_rotation_set_next_rotation_age(void); + +int +log_file_open(void); + #ifdef __cplusplus } #endif diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index c51cf8a4..25803631 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -304,6 +304,9 @@ struct configuration bool log_disconnections; /**< Log disconnects */ int log_mode; /**< The logging mode */ atomic_schar log_lock; /**< The logging lock */ + int log_rotation_size; /**< bytes to force log rotation */ + int log_rotation_age; /**< minutes for log rotation */ + bool log_truncate; /**< Truncate an existing log on rotation */ bool authquery; /**< Is authentication query enabled */ diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index e1998110..028c9507 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -49,6 +49,7 @@ #ifdef HAVE_LINUX #include #endif +#include #define LINE_LENGTH 512 @@ -58,6 +59,8 @@ static int as_bool(char* str, bool* b); static int as_logging_type(char* str); static int as_logging_level(char* str); static int as_logging_mode(char* str); +static int as_logging_rotation_size(char* str, int* size); +static int as_logging_rotation_age(char* str, int* age); static int as_validation(char* str); static int as_pipeline(char* str); static int as_hugepage(char* str); @@ -594,6 +597,36 @@ pgagroal_read_configuration(void* shm, char* filename) unknown = true; } } + else if (!strcmp(key, "log_rotation_size")) + { + if (!strcmp(section, "pgagroal")) + { + if (as_logging_rotation_size(value, &config->log_rotation_size)) + { + unknown = true; + } + } + else + { + unknown = true; + } + + } + else if (!strcmp(key, "log_rotation_age")) + { + if (!strcmp(section, "pgagroal")) + { + if (as_logging_rotation_age(value, &config->log_rotation_age)) + { + unknown = true; + } + } + else + { + unknown = true; + } + + } else if (!strcmp(key, "log_connections")) { if (!strcmp(section, "pgagroal")) @@ -2749,3 +2782,171 @@ is_empty_string(char* s) return true; } + + +/** + * Parses a string to see if it contains + * a valid value for log rotation size. + * Returns 0 if parsing ok, 1 otherwise. + * + * Valid strings have one of the suffixes: + * - k for kilobytes + * - m for megabytes + * - g for gigabytes + * + * The default is expressed always as kilobytes. The functions sets the + * rotation size in kilobytes. + */ +static int +as_logging_rotation_size(char* str, int* size) +{ + int multiplier = 1; + int index; + char value[MISC_LENGTH]; + bool multiplier_set = false; + + if (is_empty_string(str)) + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 0; + } + + index = 0; + for (int i = 0; i < strlen(str); i++) + { + if (isdigit(str[i])) + { + value[index++] = str[i]; + } + else if (isalpha(str[i]) && multiplier_set) + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + else if (isalpha(str[i]) && ! multiplier_set) + { + + if (str[i] == 'M' || str[i] == 'm') + { + multiplier = 1024 * 1024; + multiplier_set = true; + } + else if(str[i] == 'G' || str[i] == 'g') + { + multiplier = 1024 * 1024 * 1024; + multiplier_set = true; + } + else if(str[i] == 'K' || str[i] == 'k') + { + multiplier = 1024; + multiplier_set = true; + } + else if(str[i] == 'B' || str[i] == 'b') + { + multiplier = 1; + multiplier_set = true; + } + } + else + // ignore alien chars + continue; + } + + value[index] = '\0'; + if (!as_int(value, size)) + { + *size = *size * multiplier; + return 0; + } + else + { + *size = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + +} + + +/** + * Parses the log_rotation_age string. + * The string accepts + * - h for hours + * - d for days + * - w for weeks + * - m for months + * - y for years + * + * The default is expressed in minutes. + * The function sets the number of rotationg age as minutes. + * Returns 1 for errors, 0 for correct parsing. + */ +static int +as_logging_rotation_age(char* str, int* age) +{ + int multiplier = 1; + int index; + char value[MISC_LENGTH]; + bool multiplier_set = false; + + if (is_empty_string(str)) + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 0; + } + + index = 0; + for (int i = 0; i < strlen(str); i++) + { + if (isdigit(str[i])) + { + value[index++] = str[i]; + } + else if (isalpha(str[i]) && multiplier_set) + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } + else if (isalpha(str[i]) && ! multiplier_set) + { + if (str[i] == 'h' || str[i] == 'H') + { + multiplier = 60; + multiplier_set = true; + } + else if (str[i] == 'd' || str[i] == 'D') + { + multiplier = 24 * 60; + multiplier_set = true; + } + else if (str[i] == 'w' || str[i] == 'W') + { + multiplier = 24 * 60 * 7; + multiplier_set = true; + } + else if (str[i] == 'm' || str[i] == 'M') + { + multiplier = 24 * 60 * 7 * 31; + multiplier_set = true; + } + else if (str[i] == 'y' || str[i] == 'Y') + { + multiplier = 24 * 60 * 365; + multiplier_set = true; + } + } + else + continue; + } + + value[index] = '\0'; + if (!as_int(value, age)) + { + *age = *age * multiplier; + return 0; + } + else + { + *age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return 1; + } +} diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index 97d08a5d..0ec21c70 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -36,11 +36,20 @@ #include #include #include +#include +#include +#include +#include +#include #define LINE_LENGTH 32 FILE *log_file; +time_t next_log_rotation_age; /* number of seconds at which the next location will happen */ + +char current_log_path[MAX_PATH]; /* the current log file */ + static const char* levels[] = { "TRACE", @@ -61,6 +70,120 @@ static const char* colors[] = "\x1b[35m" }; + +/** + * Utility function to understand if log rotation + * is enabled or not. + * Returns `true` if the rotation is enabled. + */ +bool +log_rotation_enabled(void) +{ + struct configuration* config; + config = (struct configuration*)shmem; + + // disable log rotation in the case + // logging is not to a file + if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) + { + return false; + } + + // log rotation is enabled if either log_rotation_age or + // log_rotation_size is enabled + return config->log_rotation_age != PGAGROAL_LOGGING_ROTATION_DISABLED + || config->log_rotation_size != PGAGROAL_LOGGING_ROTATION_DISABLED; +} + +/** + * Forces a disabling of the log rotation. + * Useful when the system cannot determine how to rotate logs. + */ +void +log_rotation_disable(void) +{ + struct configuration* config; + config = (struct configuration*)shmem; + + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + config->log_rotation_size = PGAGROAL_LOGGING_ROTATION_DISABLED; + next_log_rotation_age = 0; +} + +/** + * Checks if there are the requirements to perform a log rotation. + * It returns true in either the case of the size exceeded or + * the age exceeded. The age is contained into a global + * variable 'next_log_rotation_age' that express the number + * of seconds at which the next rotation will be performed. + */ +bool +log_rotation_required(void) +{ + struct stat log_stat; + struct configuration* config; + + config = (struct configuration*)shmem; + + if (!log_rotation_enabled()) + { + return false; + } + + if (stat(current_log_path, &log_stat)) + { + return false; + } + + if (config->log_rotation_size > 0 && log_stat.st_size >= config->log_rotation_size) + { + return true; + } + + if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age >= log_stat.st_ctime) + { + return true; + } + + return false; +} + +/** + * Function to compute the next instant at which a log rotation + * will happen. It computes only if the logging is to a file + * and only if the configuration tells to compute the rotation + * age. + * Returns true on success. + */ +bool +log_rotation_set_next_rotation_age(void) +{ + struct configuration* config; + time_t now; + + config = (struct configuration*)shmem; + + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && config->log_rotation_age > 0) + { + now = time( NULL ); + if (!now) + { + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; + } + + next_log_rotation_age = now + config->log_rotation_age * 60; + return true; + } + else + { + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; + } + +} + + /** * */ @@ -73,35 +196,19 @@ pgagroal_init_logging(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - if (strlen(config->log_path) > 0) - { - if (config->log_mode == PGAGROAL_LOGGING_MODE_APPEND) - { - log_file = fopen(config->log_path, "a"); - } - else - { - log_file = fopen(config->log_path, "w"); - } - } - else - { - if (config->log_mode == PGAGROAL_LOGGING_MODE_APPEND) - { - log_file = fopen("pgagroal.log", "a"); - } - else - { - log_file = fopen("pgagroal.log", "w"); - } - } + log_file_open(); if (!log_file) { printf("Failed to open log file %s due to %s\n", strlen(config->log_path) > 0 ? config->log_path : "pgagroal.log", strerror(errno)); errno = 0; + log_rotation_disable(); return 1; } + else + { + log_rotation_set_next_rotation_age(); + } } return 0; @@ -119,14 +226,7 @@ pgagroal_start_logging(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - if (strlen(config->log_path) > 0) - { - log_file = fopen(config->log_path, "a"); - } - else - { - log_file = fopen("pgagroal.log", "a"); - } + log_file_open(); if (!log_file) { @@ -143,6 +243,79 @@ pgagroal_start_logging(void) return 0; } +/** + * Opens the log file defined in the configuration. + * Works only for a real log file, i.e., the configuration + * must be set up to log to a file, not console. + * + * The function considers the settings in the configuration + * to determine the mode (append, create) and the filename + * to open. + * + * It sets the global variable 'log_file'. + * + * Returns 0 on success, 1 on error. + */ +int +log_file_open(void) +{ + struct configuration* config; + time_t htime; + struct tm *tm; + + config = (struct configuration*)shmem; + + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) + { + htime = time( NULL ); + if (!htime) + { + log_file = NULL; + return 1; + } + + tm = localtime(&htime); + if (tm == NULL ) + { + log_file = NULL; + return 1; + } + + if (strftime(current_log_path, sizeof(current_log_path), config->log_path, tm) <= 0 ) + { + // cannot parse the format string, fallback to default logging + memcpy(current_log_path, "pgagroal.log", strlen("pgagroal.log")); + log_rotation_disable(); + } + + log_file = fopen(current_log_path, + config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); + return 0; + } + + return 1; +} + +/** + * Performs a log file rotation. + * It flushes and closes the current log file, + * then re-opens it. + * + * DO NOT LOG WITHIN THIS FUNCTION as long as this + * is invoked by log_line + */ +void +log_file_rotate(void) +{ + if (log_rotation_enabled()) + { + fflush(log_file); + fclose(log_file); + log_file_open(); + } +} + + /** * */ @@ -229,6 +402,11 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) vfprintf(log_file, fmt, vl); fprintf(log_file, "\n"); fflush(log_file); + + if (log_rotation_required()) + { + log_file_rotate(); + } } else if (config->log_type == PGAGROAL_LOGGING_TYPE_SYSLOG) { From eae4fec1e20bd2e74d2107ec9c9acd415c16273e Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 05:22:22 -0500 Subject: [PATCH 08/11] Force log rotation disabled when logging to console. Ensure that the log rotation will always be disabled if the system is configured to log to something different than a file. Improvements on log rotation. log_rotation_age is now expressed as seconds. Avoid re-creating the log file when init + start are called in sequence. --- doc/CONFIGURATION.md | 2 +- src/libpgagroal/configuration.c | 26 +++++++++++------------ src/libpgagroal/logging.c | 37 +++++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index dc17f891..e28427d2 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -30,7 +30,7 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | | log_path | pgagroal.log | String | No | The log file location | -| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as minutes. Supports suffixes: 'H' (hours), 'D' (days), 'W' (weeks), 'M' (months) and 'Y' (years) | +| log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as seconds. Supports suffixes: 'S' (seconds, the default), 'M' (minutes), 'H' (hours), 'D' (days), 'W' (weeks) | | log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | | log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | | log_connections | `off` | Bool | No | Log connects | diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 028c9507..785f7f58 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -2870,13 +2870,13 @@ as_logging_rotation_size(char* str, int* size) /** * Parses the log_rotation_age string. * The string accepts + * - s for seconds + * - m for minutes * - h for hours * - d for days * - w for weeks - * - m for months - * - y for years * - * The default is expressed in minutes. + * The default is expressed in seconds. * The function sets the number of rotationg age as minutes. * Returns 1 for errors, 0 for correct parsing. */ @@ -2908,29 +2908,29 @@ as_logging_rotation_age(char* str, int* age) } else if (isalpha(str[i]) && ! multiplier_set) { - if (str[i] == 'h' || str[i] == 'H') + if (str[i] == 's' || str[i] == 'S') { - multiplier = 60; + multiplier = 1; multiplier_set = true; } - else if (str[i] == 'd' || str[i] == 'D') + else if (str[i] == 'm' || str[i] == 'M') { - multiplier = 24 * 60; + multiplier = 60; multiplier_set = true; } - else if (str[i] == 'w' || str[i] == 'W') + else if (str[i] == 'h' || str[i] == 'H') { - multiplier = 24 * 60 * 7; + multiplier = 3600; multiplier_set = true; } - else if (str[i] == 'm' || str[i] == 'M') + else if (str[i] == 'd' || str[i] == 'D') { - multiplier = 24 * 60 * 7 * 31; + multiplier = 24 * 3600; multiplier_set = true; } - else if (str[i] == 'y' || str[i] == 'Y') + else if (str[i] == 'w' || str[i] == 'W') { - multiplier = 24 * 60 * 365; + multiplier = 24 * 3600 * 7; multiplier_set = true; } } diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index 0ec21c70..b2f0dc1e 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -86,6 +86,7 @@ log_rotation_enabled(void) // logging is not to a file if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) { + log_rotation_disable(); return false; } @@ -140,7 +141,7 @@ log_rotation_required(void) return true; } - if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age >= log_stat.st_ctime) + if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age <= log_stat.st_ctime) { return true; } @@ -165,14 +166,14 @@ log_rotation_set_next_rotation_age(void) if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && config->log_rotation_age > 0) { - now = time( NULL ); + now = time(NULL); if (!now) { config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; return false; } - next_log_rotation_age = now + config->log_rotation_age * 60; + next_log_rotation_age = now + config->log_rotation_age; return true; } else @@ -205,10 +206,6 @@ pgagroal_init_logging(void) log_rotation_disable(); return 1; } - else - { - log_rotation_set_next_rotation_age(); - } } return 0; @@ -224,7 +221,7 @@ pgagroal_start_logging(void) config = (struct configuration*)shmem; - if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE && !log_file) { log_file_open(); @@ -254,6 +251,13 @@ pgagroal_start_logging(void) * * It sets the global variable 'log_file'. * + * If it succeed in opening the log file, it calls + * the log_rotation_set_next_rotation_age() function to + * determine the next instant at which the log file + * must be rotated. Calling such function is safe + * because if the log rotation is disabled, the function + * does nothing. + * * Returns 0 on success, 1 on error. */ int @@ -290,6 +294,20 @@ log_file_open(void) log_file = fopen(current_log_path, config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); + + if (!log_file) + return 1; + + log_rotation_set_next_rotation_age(); + /* printf("\n\nLog file %s initialized %d, rotation %sable (every %d bytes or %d seconds or so, next estimated at %ld epoch, now is %d)\n\n", + current_log_path, + config->log_level, + log_rotation_enabled() ? "en" : "dis", + config->log_rotation_size, + config->log_rotation_age, + (long) next_log_rotation_age, + (long) htime); + */ return 0; } @@ -405,7 +423,8 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) if (log_rotation_required()) { - log_file_rotate(); + printf("\nLOG ROTATION\n\n"); + log_file_rotate(); } } else if (config->log_type == PGAGROAL_LOGGING_TYPE_SYSLOG) From 246f3e30dbc6b0691701aaa6bfa42069444f6a81 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 10:59:48 -0500 Subject: [PATCH 09/11] [#44] Support for custom log line prefix. This introduces the configuration parameter `log_line_prefix` that accepts a strftime(3) valid string that is parsed and stored at the very first log entry in the configuration structure (`config->log_line_prefix`). Logging to a file and to the console is done using such prefix via strftime. Documentation updated. Close #44 --- doc/CONFIGURATION.md | 3 ++- src/include/logging.h | 1 + src/include/pgagroal.h | 2 +- src/libpgagroal/configuration.c | 16 ++++++++++++++++ src/libpgagroal/logging.c | 25 ++++++++++--------------- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index e28427d2..490d99ae 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -29,7 +29,8 @@ See a [sample](./etc/pgagroal/pgagroal.conf) configuration for running `pgagroal | management | 0 | Int | No | The remote management port (disable = 0) | | log_type | console | String | No | The logging type (console, file, syslog) | | log_level | info | String | No | The logging level (fatal, error, warn, info, debug1, ..., debug5) | -| log_path | pgagroal.log | String | No | The log file location | +| log_line_prefix | %Y-%m-%d %H:%M:%S | String | No | A prefix to place on every log entry, accepts escape sequences that strftime(3) can parse. No spaces between the string parts are allowed. | +| log_path | pgagroal.log | String | No | The log file location. Can include escape sequences that strftime(3) accepts, e.g., pgagroal-%Y-%m-%d-%H-%M-%S.log | | log_rotation_age | -1 | String | No | The age that will trigger a log file rotation. If expressed as a positive number, is managed as seconds. Supports suffixes: 'S' (seconds, the default), 'M' (minutes), 'H' (hours), 'D' (days), 'W' (weeks) | | log_rotation_size | -1 | String | No | The size of the log file that will trigger a log rotation. Supports suffixes: 'B' (bytes), the default if omitted, 'K' (kilobytes), 'M' (megabytes), 'G' (gigabytes). | | log_mode | append | String | No | Append to or create the log file (append, create). The create option truncates an already existing file. | diff --git a/src/include/logging.h b/src/include/logging.h index 626076e8..5a80de9d 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -54,6 +54,7 @@ extern "C" { #define PGAGROAL_LOGGING_ROTATION_DISABLED -1 +#define PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX "%Y-%m-%d %H:%M:%S" #define pgagroal_log_trace(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG5, __FILE__, __LINE__, __VA_ARGS__) #define pgagroal_log_debug(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_DEBUG1, __FILE__, __LINE__, __VA_ARGS__) diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index 25803631..81362ea4 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -306,7 +306,7 @@ struct configuration atomic_schar log_lock; /**< The logging lock */ int log_rotation_size; /**< bytes to force log rotation */ int log_rotation_age; /**< minutes for log rotation */ - bool log_truncate; /**< Truncate an existing log on rotation */ + char log_line_prefix[MISC_LENGTH]; /**< The logging prefix */ bool authquery; /**< Is authentication query enabled */ diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 785f7f58..350d442f 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -626,6 +626,22 @@ pgagroal_read_configuration(void* shm, char* filename) unknown = true; } + } + else if (!strcmp(key, "log_line_prefix")) + { + if (!strcmp(section, "pgagroal")) + { + max = strlen(value); + if (max > MISC_LENGTH - 1) + max = MISC_LENGTH - 1; + + memcpy(config->log_line_prefix, value, max); + } + else + { + unknown = true; + } + } else if (!strcmp(key, "log_connections")) { diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index b2f0dc1e..5d71499b 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -86,8 +86,8 @@ log_rotation_enabled(void) // logging is not to a file if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) { - log_rotation_disable(); - return false; + log_rotation_disable(); + return false; } // log rotation is enabled if either log_rotation_age or @@ -203,7 +203,7 @@ pgagroal_init_logging(void) { printf("Failed to open log file %s due to %s\n", strlen(config->log_path) > 0 ? config->log_path : "pgagroal.log", strerror(errno)); errno = 0; - log_rotation_disable(); + log_rotation_disable(); return 1; } } @@ -299,15 +299,6 @@ log_file_open(void) return 1; log_rotation_set_next_rotation_age(); - /* printf("\n\nLog file %s initialized %d, rotation %sable (every %d bytes or %d seconds or so, next estimated at %ld epoch, now is %d)\n\n", - current_log_path, - config->log_level, - log_rotation_enabled() ? "en" : "dis", - config->log_rotation_size, - config->log_rotation_age, - (long) next_log_rotation_age, - (long) htime); - */ return 0; } @@ -400,11 +391,16 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) filename = file; } + if (config->log_line_prefix == NULL || strlen(config->log_line_prefix) == 0) + { + memcpy(config->log_line_prefix,PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX,strlen(PGAGROAL_LOGGING_DEFAULT_LOG_LINE_PREFIX)); + } + va_start(vl, fmt); if (config->log_type == PGAGROAL_LOGGING_TYPE_CONSOLE) { - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)] = '\0'; + buf[strftime(buf, sizeof(buf), config->log_line_prefix, tm)] = '\0'; fprintf(stdout, "%s %s%-5s\x1b[0m \x1b[90m%s:%d\x1b[0m ", buf, colors[level - 1], levels[level - 1], filename, line); @@ -414,7 +410,7 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) } else if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) { - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)] = '\0'; + buf[strftime(buf, sizeof(buf), config->log_line_prefix, tm)] = '\0'; fprintf(log_file, "%s %-5s %s:%d ", buf, levels[level - 1], filename, line); vfprintf(log_file, fmt, vl); @@ -423,7 +419,6 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...) if (log_rotation_required()) { - printf("\nLOG ROTATION\n\n"); log_file_rotate(); } } From a290590411a1ef52bf507116bb286483143d5020 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 24 Feb 2022 12:01:46 -0500 Subject: [PATCH 10/11] Utabify --- src/libpgagroal/configuration.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 350d442f..365d0394 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -627,15 +627,15 @@ pgagroal_read_configuration(void* shm, char* filename) } } - else if (!strcmp(key, "log_line_prefix")) + else if (!strcmp(key, "log_line_prefix")) { if (!strcmp(section, "pgagroal")) { max = strlen(value); - if (max > MISC_LENGTH - 1) - max = MISC_LENGTH - 1; + if (max > MISC_LENGTH - 1) + max = MISC_LENGTH - 1; - memcpy(config->log_line_prefix, value, max); + memcpy(config->log_line_prefix, value, max); } else { From 304f7722304b063dac9ef6292d1affcd1212b1d4 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Fri, 25 Feb 2022 04:10:40 -0500 Subject: [PATCH 11/11] Move function documentation to header file; cleanup. See --- src/include/logging.h | 57 +++++++++- src/libpgagroal/logging.c | 223 +++++++++++++++----------------------- 2 files changed, 142 insertions(+), 138 deletions(-) diff --git a/src/include/logging.h b/src/include/logging.h index 5a80de9d..bd001358 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -90,21 +90,74 @@ pgagroal_log_line(int level, char *file, int line, char *fmt, ...); void pgagroal_log_mem(void* data, size_t size); - +/** + * Utility function to understand if log rotation + * is enabled or not. + * Returns `true` if the rotation is enabled. + */ bool log_rotation_enabled(void); +/** + * Forces a disabling of the log rotation. + * Useful when the system cannot determine how to rotate logs. + */ void log_rotation_disable(void); +/** + * Checks if there are the requirements to perform a log rotation. + * It returns true in either the case of the size exceeded or + * the age exceeded. The age is contained into a global + * variable 'next_log_rotation_age' that express the number + * of seconds at which the next rotation will be performed. + */ bool log_rotation_required(void); +/** + * Function to compute the next instant at which a log rotation + * will happen. It computes only if the logging is to a file + * and only if the configuration tells to compute the rotation + * age. + * Returns true on success. + */ bool log_rotation_set_next_rotation_age(void); +/** + * Opens the log file defined in the configuration. + * Works only for a real log file, i.e., the configuration + * must be set up to log to a file, not console. + * + * The function considers the settings in the configuration + * to determine the mode (append, create) and the filename + * to open. + * + * It sets the global variable 'log_file'. + * + * If it succeed in opening the log file, it calls + * the log_rotation_set_next_rotation_age() function to + * determine the next instant at which the log file + * must be rotated. Calling such function is safe + * because if the log rotation is disabled, the function + * does nothing. + * + * Returns 0 on success, 1 on error. + */ int -log_file_open(void); +log_file_open(void); + +/** + * Performs a log file rotation. + * It flushes and closes the current log file, + * then re-opens it. + * + * DO NOT LOG WITHIN THIS FUNCTION as long as this + * is invoked by log_line + */ +void +log_file_rotate(void); #ifdef __cplusplus } diff --git a/src/libpgagroal/logging.c b/src/libpgagroal/logging.c index 5d71499b..45d19f70 100644 --- a/src/libpgagroal/logging.c +++ b/src/libpgagroal/logging.c @@ -71,91 +71,70 @@ static const char* colors[] = }; -/** - * Utility function to understand if log rotation - * is enabled or not. - * Returns `true` if the rotation is enabled. - */ + bool log_rotation_enabled(void) { - struct configuration* config; - config = (struct configuration*)shmem; - - // disable log rotation in the case - // logging is not to a file - if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) - { - log_rotation_disable(); - return false; - } - - // log rotation is enabled if either log_rotation_age or - // log_rotation_size is enabled - return config->log_rotation_age != PGAGROAL_LOGGING_ROTATION_DISABLED - || config->log_rotation_size != PGAGROAL_LOGGING_ROTATION_DISABLED; + struct configuration* config; + config = (struct configuration*)shmem; + + // disable log rotation in the case + // logging is not to a file + if (config->log_type != PGAGROAL_LOGGING_TYPE_FILE) + { + log_rotation_disable(); + return false; + } + + // log rotation is enabled if either log_rotation_age or + // log_rotation_size is enabled + return config->log_rotation_age != PGAGROAL_LOGGING_ROTATION_DISABLED + || config->log_rotation_size != PGAGROAL_LOGGING_ROTATION_DISABLED; } -/** - * Forces a disabling of the log rotation. - * Useful when the system cannot determine how to rotate logs. - */ + void log_rotation_disable(void) { - struct configuration* config; - config = (struct configuration*)shmem; + struct configuration* config; + config = (struct configuration*)shmem; - config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; - config->log_rotation_size = PGAGROAL_LOGGING_ROTATION_DISABLED; - next_log_rotation_age = 0; + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + config->log_rotation_size = PGAGROAL_LOGGING_ROTATION_DISABLED; + next_log_rotation_age = 0; } -/** - * Checks if there are the requirements to perform a log rotation. - * It returns true in either the case of the size exceeded or - * the age exceeded. The age is contained into a global - * variable 'next_log_rotation_age' that express the number - * of seconds at which the next rotation will be performed. - */ bool log_rotation_required(void) { - struct stat log_stat; - struct configuration* config; + struct stat log_stat; + struct configuration* config; - config = (struct configuration*)shmem; + config = (struct configuration*)shmem; - if (!log_rotation_enabled()) - { - return false; - } + if (!log_rotation_enabled()) + { + return false; + } - if (stat(current_log_path, &log_stat)) - { - return false; - } + if (stat(current_log_path, &log_stat)) + { + return false; + } - if (config->log_rotation_size > 0 && log_stat.st_size >= config->log_rotation_size) - { - return true; - } + if (config->log_rotation_size > 0 && log_stat.st_size >= config->log_rotation_size) + { + return true; + } - if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age <= log_stat.st_ctime) - { - return true; - } + if (config->log_rotation_age > 0 && next_log_rotation_age > 0 && next_log_rotation_age <= log_stat.st_ctime) + { + return true; + } - return false; + return false; } -/** - * Function to compute the next instant at which a log rotation - * will happen. It computes only if the logging is to a file - * and only if the configuration tells to compute the rotation - * age. - * Returns true on success. - */ bool log_rotation_set_next_rotation_age(void) { @@ -169,8 +148,8 @@ log_rotation_set_next_rotation_age(void) now = time(NULL); if (!now) { - config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; - return false; + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; } next_log_rotation_age = now + config->log_rotation_age; @@ -178,10 +157,9 @@ log_rotation_set_next_rotation_age(void) } else { - config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; - return false; + config->log_rotation_age = PGAGROAL_LOGGING_ROTATION_DISABLED; + return false; } - } @@ -240,88 +218,61 @@ pgagroal_start_logging(void) return 0; } -/** - * Opens the log file defined in the configuration. - * Works only for a real log file, i.e., the configuration - * must be set up to log to a file, not console. - * - * The function considers the settings in the configuration - * to determine the mode (append, create) and the filename - * to open. - * - * It sets the global variable 'log_file'. - * - * If it succeed in opening the log file, it calls - * the log_rotation_set_next_rotation_age() function to - * determine the next instant at which the log file - * must be rotated. Calling such function is safe - * because if the log rotation is disabled, the function - * does nothing. - * - * Returns 0 on success, 1 on error. - */ + int log_file_open(void) { - struct configuration* config; - time_t htime; - struct tm *tm; + struct configuration* config; + time_t htime; + struct tm *tm; - config = (struct configuration*)shmem; - - if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) - { - htime = time( NULL ); - if (!htime) - { - log_file = NULL; - return 1; - } + config = (struct configuration*)shmem; + + if (config->log_type == PGAGROAL_LOGGING_TYPE_FILE) + { + htime = time( NULL ); + if (!htime) + { + log_file = NULL; + return 1; + } - tm = localtime(&htime); - if (tm == NULL ) - { - log_file = NULL; - return 1; - } - - if (strftime(current_log_path, sizeof(current_log_path), config->log_path, tm) <= 0 ) - { - // cannot parse the format string, fallback to default logging - memcpy(current_log_path, "pgagroal.log", strlen("pgagroal.log")); - log_rotation_disable(); - } + tm = localtime(&htime); + if (tm == NULL ) + { + log_file = NULL; + return 1; + } - log_file = fopen(current_log_path, - config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); + if (strftime(current_log_path, sizeof(current_log_path), config->log_path, tm) <= 0 ) + { + // cannot parse the format string, fallback to default logging + memcpy(current_log_path, "pgagroal.log", strlen("pgagroal.log")); + log_rotation_disable(); + } - if (!log_file) - return 1; + log_file = fopen(current_log_path, + config->log_mode == PGAGROAL_LOGGING_MODE_APPEND ? "a" : "w"); - log_rotation_set_next_rotation_age(); - return 0; - } + if (!log_file) + return 1; - return 1; + log_rotation_set_next_rotation_age(); + return 0; + } + + return 1; } -/** - * Performs a log file rotation. - * It flushes and closes the current log file, - * then re-opens it. - * - * DO NOT LOG WITHIN THIS FUNCTION as long as this - * is invoked by log_line - */ void log_file_rotate(void) { - if (log_rotation_enabled()) - { - fflush(log_file); - fclose(log_file); - log_file_open(); - } + if (log_rotation_enabled()) + { + fflush(log_file); + fclose(log_file); + log_file_open(); + } }