Initial attempt of log rotation based on file sizes
This commit is contained in:
@@ -132,6 +132,33 @@ struct UCLogModules {
|
|||||||
|
|
||||||
struct UCLogDestination;
|
struct UCLogDestination;
|
||||||
|
|
||||||
|
enum UC_LOG_ROTATE_POLICY {
|
||||||
|
/** Don't rotate. Default setting*/
|
||||||
|
UC_LOG_ROTATE_NONE,
|
||||||
|
/** Create a new log file every 'size' bytes */
|
||||||
|
UC_LOG_ROTATE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Log rotate settings that can be set on a UC_LDEST_FILE destination
|
||||||
|
* The default is to not rotate log files.
|
||||||
|
* The current log file wil always be the specified file name,
|
||||||
|
* For UC_LOG_ROTATE_SIZE the current file will be renamed when it exceeds
|
||||||
|
* the specified max_size with a .number suffix (e.g .0 , .1 , .2 and so on),
|
||||||
|
* up to num_files are kept and a new log file is created.
|
||||||
|
*/
|
||||||
|
struct UCLogRotateSettings {
|
||||||
|
/** Policy */
|
||||||
|
enum UC_LOG_ROTATE_POLICY policy;
|
||||||
|
/** For UC_LOG_ROTATE_SIZE policy */
|
||||||
|
struct {
|
||||||
|
/** Max size of an individual log file */
|
||||||
|
size_t max_size;
|
||||||
|
/** Max number of log files to keep.
|
||||||
|
* Is forced to be at least 1*/
|
||||||
|
unsigned int num_files;
|
||||||
|
} size_settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Returns a string representation of the log level.
|
/* Returns a string representation of the log level.
|
||||||
*
|
*
|
||||||
@@ -204,6 +231,17 @@ void uc_log_destination_set_loglevel(struct UCLogDestination *dest, enum UC_LOG_
|
|||||||
* @param log_location 1=log the locaton, 0=don't log the location
|
* @param log_location 1=log the locaton, 0=don't log the location
|
||||||
*/
|
*/
|
||||||
void uc_log_destination_set_log_location(struct UCLogDestination *dest, int log_location);
|
void uc_log_destination_set_log_location(struct UCLogDestination *dest, int log_location);
|
||||||
|
|
||||||
|
/** Sets the log rotation settings.
|
||||||
|
* Does nothing if the destination is not a UC_LDEST_FILE, or the policy is invalid.
|
||||||
|
* The default is to not do log rotation
|
||||||
|
*
|
||||||
|
* @param dest log destination to change
|
||||||
|
* @param settings new policy to set
|
||||||
|
*/
|
||||||
|
void uc_log_destination_set_rotate_policy(struct UCLogDestination *dest,
|
||||||
|
const struct UCLogRotateSettings *settings);
|
||||||
|
|
||||||
/** Remove and free a destination.
|
/** Remove and free a destination.
|
||||||
* This frees the memory allocated to the destination. For
|
* This frees the memory allocated to the destination. For
|
||||||
* UC_LDEST_FILE the log file is closed.
|
* UC_LDEST_FILE the log file is closed.
|
||||||
|
|||||||
+101
-13
@@ -1,7 +1,9 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
@@ -10,6 +12,10 @@
|
|||||||
#include "ucore/logging.h"
|
#include "ucore/logging.h"
|
||||||
#include "ucore/utils.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
|
//used to realise per destination settings
|
||||||
struct UCLogModule_setting {
|
struct UCLogModule_setting {
|
||||||
@@ -33,8 +39,11 @@ struct UCLogDestination {
|
|||||||
//for stderr and files
|
//for stderr and files
|
||||||
struct {
|
struct {
|
||||||
FILE* file;
|
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
|
size_t size; //keeps track of no of bytes written
|
||||||
|
struct UCLogRotateSettings rotate;
|
||||||
|
unsigned rotate_count; //current num_file
|
||||||
} file;
|
} file;
|
||||||
} type;
|
} 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_level = log_level;;
|
||||||
dest->log_location = log_location;
|
dest->log_location = log_location;
|
||||||
|
|
||||||
|
dest->type.file.rotate.policy = UC_LOG_ROTATE_NONE;
|
||||||
|
dest->type.file.rotate_count = 0;
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if a destination already exists (hold g_log_lock when calling this)
|
//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;
|
struct UCLogDestination *it;
|
||||||
|
|
||||||
@@ -265,7 +277,7 @@ void uc_log_add_destination(struct UCLogDestination *dest)
|
|||||||
|
|
||||||
pthread_mutex_lock(&g_log_lock);
|
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
|
//add if it isn't already in the list
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
uc_tailq_insert_head(&g_destinations, &dest->entry);
|
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.
|
//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;
|
int exists;
|
||||||
|
|
||||||
//check that it's not already removed
|
//check that it's not already removed
|
||||||
exists = uc_log_dest_exists_unlocked(dest);
|
exists = uc_log_dest_exists(dest);
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
uc_tailq_remove(&dest->entry);
|
uc_tailq_remove(&dest->entry);
|
||||||
@@ -294,7 +306,7 @@ void uc_log_remove_destination(struct UCLogDestination *dest)
|
|||||||
|
|
||||||
pthread_mutex_lock(&g_log_lock);
|
pthread_mutex_lock(&g_log_lock);
|
||||||
|
|
||||||
uc_log_remove_dest_unlocked(dest);
|
uc_log_remove_dest(dest);
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_log_lock);
|
pthread_mutex_unlock(&g_log_lock);
|
||||||
}
|
}
|
||||||
@@ -307,7 +319,7 @@ void uc_log_delete_destination(struct UCLogDestination *dest)
|
|||||||
pthread_mutex_lock(&g_log_lock);
|
pthread_mutex_lock(&g_log_lock);
|
||||||
|
|
||||||
//remove it, it's ok if the destination is already removed
|
//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
|
//clean up, according to the type
|
||||||
switch (dest->dest_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);
|
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)
|
static int uc_log_reopen_file(struct UCLogDestination *dest)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
@@ -404,20 +440,18 @@ void uc_log_init(struct UCLogModules *user_modules)
|
|||||||
g_modules = *user_modules;
|
g_modules = *user_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//hold g_log_lock while calling this
|
||||||
static inline void uc_vlog_file(const struct UCLogArgs *args, const char *fmt, va_list ap)
|
static inline void uc_vlog_file(const struct UCLogArgs *args, time_t tstamp, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
struct UCLogDestination *dest = args->dest;
|
struct UCLogDestination *dest = args->dest;
|
||||||
char time_buf[48];
|
char time_buf[48];
|
||||||
time_t now;
|
|
||||||
struct tm t;
|
struct tm t;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (dest->type.file.file == NULL)
|
if (dest->type.file.file == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
now = time(NULL);
|
localtime_r(&tstamp, &t);
|
||||||
localtime_r(&now, &t);
|
|
||||||
if (!args->raw) {
|
if (!args->raw) {
|
||||||
snprintf(time_buf, sizeof time_buf, "%04d-%02d-%02d %02d:%02d:%02d",
|
snprintf(time_buf, sizeof time_buf, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
t.tm_year + 1900,
|
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)
|
static inline void uc_vlog_syslog(const struct UCLogArgs *args, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
struct UCLogDestination *dest = args->dest;
|
struct UCLogDestination *dest = args->dest;
|
||||||
@@ -562,6 +597,53 @@ void uc_log_destination_set_mask(
|
|||||||
pthread_mutex_unlock(&g_log_lock);
|
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,
|
void uc_logf(int log_level, int module, int raw,
|
||||||
const char *location, const char *fmt, ...)
|
const char *location, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@@ -605,6 +687,7 @@ void uc_logf(int log_level, int module, int raw,
|
|||||||
.raw = raw
|
.raw = raw
|
||||||
};
|
};
|
||||||
|
|
||||||
|
time_t tstamp;
|
||||||
//log func will mess with the va_list,
|
//log func will mess with the va_list,
|
||||||
//so make a copy
|
//so make a copy
|
||||||
va_copy(apc, ap);
|
va_copy(apc, ap);
|
||||||
@@ -614,8 +697,13 @@ void uc_logf(int log_level, int module, int raw,
|
|||||||
uc_vlog_syslog(&args, fmt, apc);
|
uc_vlog_syslog(&args, fmt, apc);
|
||||||
break;
|
break;
|
||||||
case UC_LDEST_STDERR:
|
case UC_LDEST_STDERR:
|
||||||
|
tstamp = time(NULL);
|
||||||
|
uc_vlog_file(&args, tstamp,fmt, apc);
|
||||||
|
break;
|
||||||
case UC_LDEST_FILE:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -36,14 +36,23 @@ struct UCLogModules modules = {
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct UCLogDestination *dest;
|
struct UCLogDestination *dest;
|
||||||
|
struct UCLogRotateSettings settings;
|
||||||
|
settings.policy = UC_LOG_ROTATE_SIZE;
|
||||||
|
settings.size_settings.max_size = 300;
|
||||||
|
settings.size_settings.num_files = 2;
|
||||||
|
|
||||||
uc_log_init(&modules);
|
uc_log_init(&modules);
|
||||||
dest = uc_log_new_file("test.log", UC_LL_INFO, 1);
|
dest = uc_log_new_file("test.log", UC_LL_DEBUG, 1);
|
||||||
uc_log_add_destination(dest);
|
uc_log_add_destination(dest);
|
||||||
|
uc_log_destination_set_rotate_policy(dest, &settings);
|
||||||
dest = uc_log_new_syslog("log.test", LOG_DAEMON, UC_LL_INFO, 0);
|
dest = uc_log_new_syslog("log.test", LOG_DAEMON, UC_LL_INFO, 0);
|
||||||
uc_log_add_destination(dest);
|
uc_log_add_destination(dest);
|
||||||
dest = uc_log_new_stderr(UC_LL_INFO, 0);
|
dest = uc_log_new_stderr(UC_LL_INFO, 0);
|
||||||
uc_log_add_destination(dest);
|
uc_log_add_destination(dest);
|
||||||
UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 2);
|
UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 2);
|
||||||
|
UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 3);
|
||||||
|
UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 4);
|
||||||
|
UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 5);
|
||||||
UC_LOGF( UC_LL_WARNING, HO, "Test HO warning%d\n", 2);
|
UC_LOGF( UC_LL_WARNING, HO, "Test HO warning%d\n", 2);
|
||||||
UC_LOGF( UC_LL_INFO, MAIN, "Test MAIN info \n" );
|
UC_LOGF( UC_LL_INFO, MAIN, "Test MAIN info \n" );
|
||||||
UC_LOGF( UC_LL_WARNING, MAIN, "Test MAIN warning\n" );
|
UC_LOGF( UC_LL_WARNING, MAIN, "Test MAIN warning\n" );
|
||||||
|
|||||||
Reference in New Issue
Block a user