Skip to content

Commit

Permalink
[agroal#45][agroal#44] Log rotation and formatting support.
Browse files Browse the repository at this point in the history
See initial work in agroal#213.
This patch implements the log rotation and formatting support.
There are now three new configuration options:
- log_line_prefix is a strftime(3) compatible string that is used
  as a prefix whenever a new line is logged to console or file;
- log_rotation_age provides the age (in seconds) of when the log must
  be rotated. There is raw support for human strings like '1d' for
  'one day';
- log_rotation_size provides the size in bytes when the log must be
  rotated.

Log rotation is evaluated at every new log line issuing, that means
the age and the size are not honored effectively until a new log line
is issued. When the system decides to rotate the log, the log file
is flushed and a new one is opened. To allow rotation, log_path
supports a strftime(3) compatible string like
log_path  = /var/log/pgagroal/pgagroal-%Y-%m-%d-%H-%M-%S.log
so that whnever a new log file must to be created, it can be
placed side by side the closed one.
In the case the new log file already exists, the log_mode is
used to decide if the log must be truncated or used in append mode.

In the case the log_mode is set to console or to syslog, the
log rotation is automatically disabled, therefore the overhead of
the rotation machinery will be applied only when logging to a file.

Example of configuration:

log_type  = file
log_level = info
log_path  = /var/log/pgagroal/pgagroal-%Y-%m-%d-%H-%M-%S.log
log_mode  = create
log_rotation_size = 2M
log_rotation_age = 1m
log_line_prefix  = PGAGROAL-%Y-%m-%d-%H:%M:%S'

Close agroal#44
Close agroal#45
  • Loading branch information
fluca1978 committed Feb 28, 2022
1 parent 841a974 commit c4f7597
Show file tree
Hide file tree
Showing 4 changed files with 460 additions and 33 deletions.
73 changes: 73 additions & 0 deletions src/include/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ extern "C" {
#define PGAGROAL_LOGGING_MODE_CREATE 0
#define PGAGROAL_LOGGING_MODE_APPEND 1

#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__)
#define pgagroal_log_info(...) pgagroal_log_line(PGAGROAL_LOGGING_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
Expand Down Expand Up @@ -86,6 +90,75 @@ 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);

/**
* 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
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/include/pgagroal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
char log_line_prefix[MISC_LENGTH]; /**< The logging prefix */

bool authquery; /**< Is authentication query enabled */

Expand Down
212 changes: 212 additions & 0 deletions src/libpgagroal/configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#ifdef HAVE_LINUX
#include <systemd/sd-daemon.h>
#endif
#include <ctype.h>

#define LINE_LENGTH 512

Expand All @@ -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);
Expand Down Expand Up @@ -594,6 +597,51 @@ 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_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"))
{
if (!strcmp(section, "pgagroal"))
Expand Down Expand Up @@ -2758,3 +2806,167 @@ 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
* - s for seconds
* - m for minutes
* - h for hours
* - d for days
* - w for weeks
*
* The default is expressed in seconds.
* 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] == 's' || str[i] == 'S')
{
multiplier = 1;
multiplier_set = true;
}
else if (str[i] == 'm' || str[i] == 'M')
{
multiplier = 60;
multiplier_set = true;
}
else if (str[i] == 'h' || str[i] == 'H')
{
multiplier = 3600;
multiplier_set = true;
}
else if (str[i] == 'd' || str[i] == 'D')
{
multiplier = 24 * 3600;
multiplier_set = true;
}
else if (str[i] == 'w' || str[i] == 'W')
{
multiplier = 24 * 3600 * 7;
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;
}
}
Loading

0 comments on commit c4f7597

Please sign in to comment.