Initial attempt of log rotation based on file sizes

This commit is contained in:
Nils O. Selåsdal
2014-04-23 02:04:08 +02:00
parent b75048ceef
commit 7bd1074c1d
3 changed files with 149 additions and 14 deletions
+101 -13
View File
@@ -1,7 +1,9 @@
#include <pthread.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
@@ -10,6 +12,10 @@
#include "ucore/logging.h"
#include "ucore/utils.h"
/** Max suffixes we'll try to apply to a log file when rotating before
* giving up
*/
#define UC_MAX_RENAME_CNT (128)
//used to realise per destination settings
struct UCLogModule_setting {
@@ -33,8 +39,11 @@ struct UCLogDestination {
//for stderr and files
struct {
FILE* file;
char *file_name; //NULL for stderr destinaton
char *file_name; //NULL for stderr destinaton. If rotate policy is in place
//this is the base file name (e.g. witout a date or count)
size_t size; //keeps track of no of bytes written
struct UCLogRotateSettings rotate;
unsigned rotate_count; //current num_file
} file;
} type;
};
@@ -239,11 +248,14 @@ struct UCLogDestination *uc_log_new_file(const char *filename, int log_level, in
dest->log_level = log_level;;
dest->log_location = log_location;
dest->type.file.rotate.policy = UC_LOG_ROTATE_NONE;
dest->type.file.rotate_count = 0;
return dest;
}
//Check if a destination already exists (hold g_log_lock when calling this)
static int uc_log_dest_exists_unlocked(struct UCLogDestination *dest)
static int uc_log_dest_exists(struct UCLogDestination *dest)
{
struct UCLogDestination *it;
@@ -265,7 +277,7 @@ void uc_log_add_destination(struct UCLogDestination *dest)
pthread_mutex_lock(&g_log_lock);
exists = uc_log_dest_exists_unlocked(dest);
exists = uc_log_dest_exists(dest);
//add if it isn't already in the list
if (!exists) {
uc_tailq_insert_head(&g_destinations, &dest->entry);
@@ -275,12 +287,12 @@ void uc_log_add_destination(struct UCLogDestination *dest)
}
//hold g_log_lock while calling this.
static inline void uc_log_remove_dest_unlocked(struct UCLogDestination *dest)
static inline void uc_log_remove_dest(struct UCLogDestination *dest)
{
int exists;
//check that it's not already removed
exists = uc_log_dest_exists_unlocked(dest);
exists = uc_log_dest_exists(dest);
if (exists) {
uc_tailq_remove(&dest->entry);
@@ -294,7 +306,7 @@ void uc_log_remove_destination(struct UCLogDestination *dest)
pthread_mutex_lock(&g_log_lock);
uc_log_remove_dest_unlocked(dest);
uc_log_remove_dest(dest);
pthread_mutex_unlock(&g_log_lock);
}
@@ -307,7 +319,7 @@ void uc_log_delete_destination(struct UCLogDestination *dest)
pthread_mutex_lock(&g_log_lock);
//remove it, it's ok if the destination is already removed
uc_log_remove_dest_unlocked(dest);
uc_log_remove_dest(dest);
//clean up, according to the type
switch (dest->dest_type) {
@@ -357,6 +369,30 @@ void uc_log_destination_set_log_location(struct UCLogDestination *dest, int log_
pthread_mutex_unlock(&g_log_lock);
}
void uc_log_destination_set_rotate_policy(struct UCLogDestination *dest,
const struct UCLogRotateSettings *settings)
{
if (dest->dest_type != UC_LDEST_FILE) {
return;
}
if (settings->policy != UC_LOG_ROTATE_NONE && settings->policy != UC_LOG_ROTATE_SIZE) {
return;
}
pthread_mutex_lock(&g_log_lock);
dest->type.file.rotate = *settings;
if (dest->type.file.rotate.size_settings.num_files == 0) {
dest->type.file.rotate.size_settings.num_files = 1;
}
pthread_mutex_unlock(&g_log_lock);
}
//hold g_log_lock while calling this
static int uc_log_reopen_file(struct UCLogDestination *dest)
{
int rc = 0;
@@ -404,20 +440,18 @@ void uc_log_init(struct UCLogModules *user_modules)
g_modules = *user_modules;
}
static inline void uc_vlog_file(const struct UCLogArgs *args, const char *fmt, va_list ap)
//hold g_log_lock while calling this
static inline void uc_vlog_file(const struct UCLogArgs *args, time_t tstamp, const char *fmt, va_list ap)
{
struct UCLogDestination *dest = args->dest;
char time_buf[48];
time_t now;
struct tm t;
int rc;
if (dest->type.file.file == NULL)
return;
now = time(NULL);
localtime_r(&now, &t);
localtime_r(&tstamp, &t);
if (!args->raw) {
snprintf(time_buf, sizeof time_buf, "%04d-%02d-%02d %02d:%02d:%02d",
t.tm_year + 1900,
@@ -451,6 +485,7 @@ static inline void uc_vlog_file(const struct UCLogArgs *args, const char *fmt, v
}
}
//hold g_log_lock while calling this
static inline void uc_vlog_syslog(const struct UCLogArgs *args, const char *fmt, va_list ap)
{
struct UCLogDestination *dest = args->dest;
@@ -562,6 +597,53 @@ void uc_log_destination_set_mask(
pthread_mutex_unlock(&g_log_lock);
}
//assumes the destination is a UC_LDEST_FILE
//hold g_log_lock while calling this
static int uc_log_file_rotate_size(struct UCLogDestination *dest)
{
char new_filename[_POSIX_PATH_MAX * 2];
int rc;
int rotate_count;
if (dest->type.file.file_name == NULL) {
return 0;
}
rotate_count = ++dest->type.file.rotate_count;
rotate_count %= dest->type.file.rotate.size_settings.num_files;
snprintf(new_filename, sizeof new_filename, "%s.%d",
dest->type.file.file_name,
rotate_count);
rc = rename(dest->type.file.file_name, new_filename);
if (rc != -1) {
dest->type.file.rotate_count = rotate_count;
}
uc_log_reopen_file(dest); //try to reopen the log file regardless if renaming failed
return rc;
}
//assumes the destination is a UC_LDEST_FILE
//hold g_log_lock while calling this
static inline void uc_log_file_rotate_if_neeed(struct UCLogDestination *dest, time_t tstamp)
{
struct UCLogRotateSettings *rotate = &dest->type.file.rotate;
switch (rotate->policy) {
case UC_LOG_ROTATE_NONE:
//Nothing to do
break;
case UC_LOG_ROTATE_SIZE:
if (dest->type.file.size >= rotate->size_settings.max_size) {
uc_log_file_rotate_size(dest);
}
break;
}
}
void uc_logf(int log_level, int module, int raw,
const char *location, const char *fmt, ...)
{
@@ -605,6 +687,7 @@ void uc_logf(int log_level, int module, int raw,
.raw = raw
};
time_t tstamp;
//log func will mess with the va_list,
//so make a copy
va_copy(apc, ap);
@@ -614,8 +697,13 @@ void uc_logf(int log_level, int module, int raw,
uc_vlog_syslog(&args, fmt, apc);
break;
case UC_LDEST_STDERR:
tstamp = time(NULL);
uc_vlog_file(&args, tstamp,fmt, apc);
break;
case UC_LDEST_FILE:
uc_vlog_file(&args, fmt, apc);
tstamp = time(NULL);
uc_vlog_file(&args, tstamp, fmt, apc);
uc_log_file_rotate_if_neeed(dest, tstamp);
break;
}