ITS#6949 add support for logfile rotation

Uses debuglevel, not sysloglevel.
This commit is contained in:
Howard Chu 2021-07-15 16:27:51 +01:00 committed by Quanah Gibson-Mount
parent 27a5424371
commit 9f4de680e3
5 changed files with 215 additions and 25 deletions

View File

@ -567,10 +567,23 @@ option description. The default is 71.
Specify a file for recording slapd debug messages. By default these messages
only go to stderr, are not recorded anywhere else, and are unrelated to
messages exposed by the
.B loglevel
.B olcLogLevel
configuration parameter. Specifying a logfile copies messages to both stderr
and the logfile.
.TP
.B olcLogFileOnly: TRUE | FALSE
Specify that debug messages should only go to the configured logfile, and
not to stderr.
.TP
.B olcLogFileRotate: <max> <Mbytes> <hours>
Specify automatic rotation for the configured logfile as the maximum
number of old logfiles to retain, a maximum size in megabytes to allow a
logfile to grow before rotation, and a maximum age in hours for a logfile
to be used before rotation. The maximum number must be in the range 1-99.
Setting Mbytes or hours to zero disables the size or age check, respectively.
At least one of Mbytes or hours must be non-zero. By default no automatic
rotation will be performed.
.TP
.B olcLogLevel: <integer> [...]
Specify the level at which debugging statements and operation
statistics should be syslogged (currently logged to the

View File

@ -625,6 +625,19 @@ messages exposed by the
configuration parameter. Specifying a logfile copies messages to both stderr
and the logfile.
.TP
.B logfile-only on | off
Specify that debug messages should only go to the configured logfile, and
not to stderr.
.TP
.B logfile-rotate <max> <Mbytes> <hours>
Specify automatic rotation for the configured logfile as the maximum
number of old logfiles to retain, a maximum size in megabytes to allow a
logfile to grow before rotation, and a maximum age in hours for a logfile
to be used before rotation. The maximum number must be in the range 1-99.
Setting Mbytes or hours to zero disables the size or age check, respectively.
At least one of Mbytes or hours must be non-zero. By default no automatic
rotation will be performed.
.TP
.B loglevel <integer> [...]
Specify the level at which debugging statements and operation
statistics should be syslogged (currently logged to the

View File

@ -80,8 +80,6 @@ typedef struct {
static CfBackInfo cfBackInfo;
static char *passwd_salt;
static FILE *logfile;
static char *logfileName;
static AccessControl *defacl_parsed = NULL;
static struct berval cfdir;
@ -203,6 +201,7 @@ enum {
CFG_TLS_CACERT,
CFG_TLS_CERT,
CFG_TLS_KEY,
CFG_LOGFILE_ROTATE,
CFG_LAST
};
@ -486,6 +485,14 @@ static ConfigTable config_back_cf_table[] = {
&config_generic, "( OLcfgGlAt:27 NAME 'olcLogFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "logfile-only", "on|off", 2, 2, 0, ARG_ON_OFF,
&logfile_only, "( OLcfgGlAt:102 NAME 'olcLogFileOnly' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
{ "logfile-rotate", "max> <Mbyte> <hours", 4, 4, 0, ARG_MAGIC|CFG_LOGFILE_ROTATE,
&config_generic, "( OLcfgGlAt:103 NAME 'olcLogFileRotate' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "loglevel", "level", 2, 0, 0, ARG_MAGIC,
&config_loglevel, "( OLcfgGlAt:28 NAME 'olcLogLevel' "
"EQUALITY caseIgnoreMatch "
@ -980,7 +987,7 @@ static ConfigOCs cf_ocs[] = {
"olcIndexSubstrAnyLen $ olcIndexSubstrAnyStep $ olcIndexHash64 $ "
"olcIndexIntLen $ "
"olcListenerThreads $ olcLocalSSF $ olcLogFile $ olcLogLevel $ "
"olcMaxFilterDepth $ "
"olcLogFileOnly $ olcLogFileRotate $ olcMaxFilterDepth $ "
"olcPasswordCryptSaltFormat $ olcPasswordHash $ olcPidFile $ "
"olcPluginLogFile $ olcReadOnly $ olcReferral $ "
"olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
@ -1373,11 +1380,27 @@ config_generic(ConfigArgs *c) {
rc = 1;
}
break;
case CFG_LOGFILE:
if ( logfileName )
case CFG_LOGFILE: {
const char *logfileName = logfile_name();
if ( logfileName && *logfileName )
c->value_string = ch_strdup( logfileName );
else
rc = 1;
}
break;
case CFG_LOGFILE_ROTATE:
rc = 1;
if ( logfile_max ) {
char buf[64];
struct berval bv;
bv.bv_len = snprintf( buf, sizeof(buf), "%d %ld %ld", logfile_max,
(long) logfile_fslimit / 1048576, (long) logfile_age / 3600 );
if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
bv.bv_val = buf;
value_add_one( &c->rvalue_vals, &bv );
rc = 0;
}
}
break;
case CFG_LASTMOD:
c->value_int = (SLAP_NOLASTMOD(c->be) == 0);
@ -1610,12 +1633,11 @@ config_generic(ConfigArgs *c) {
break;
case CFG_LOGFILE:
ch_free( logfileName );
logfileName = NULL;
if ( logfile ) {
fclose( logfile );
logfile = NULL;
}
logfile_close();
break;
case CFG_LOGFILE_ROTATE:
logfile_max = logfile_fslimit = logfile_age = 0;
break;
case CFG_SERVERID: {
@ -2392,11 +2414,44 @@ sortval_reject:
}
break;
case CFG_LOGFILE: {
if ( logfileName ) ch_free( logfileName );
logfileName = c->value_string;
logfile = fopen(logfileName, "w");
if(logfile) lutil_debug_file(logfile);
} break;
int rc = logfile_open( c->value_string );
ch_free( c->value_string );
return rc;
}
break;
case CFG_LOGFILE_ROTATE: {
unsigned lf_max, lf_mbyte, lf_hour;
if ( lutil_atoux( &lf_max, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid max value \"%s\"", c->argv[0], c->argv[1] );
return 1;
}
if ( !lf_max || lf_max > 99 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid max value \"%s\" must be 1-99", c->argv[0], c->argv[1] );
return 1;
}
if ( lutil_atoux( &lf_mbyte, c->argv[2], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid Mbyte value \"%s\"", c->argv[0], c->argv[1] );
return 1;
}
if ( lutil_atoux( &lf_hour, c->argv[3], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"invalid hours value \"%s\"", c->argv[0], c->argv[2] );
return 1;
}
if ( !lf_mbyte && !lf_hour ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> "
"Mbyte and hours cannot both be zero", c->argv[0] );
return 1;
}
logfile_max = lf_max;
logfile_fslimit = lf_mbyte * 1048576; /* Megabytes to bytes */
logfile_age = lf_hour * 3600; /* hours to seconds */
}
break;
case CFG_LASTMOD:
if(SLAP_NOLASTMODCMD(c->be)) {

View File

@ -35,6 +35,10 @@
#include <ac/wait.h>
#include <ac/errno.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include "slap.h"
#include "lutil.h"
#include "ldif.h"
@ -377,27 +381,121 @@ usage( char *name )
);
}
static char logfile_suffix[sizeof(".xx.gz")];
char logfile_path[MAXPATHLEN - sizeof(logfile_suffix) -1];
long logfile_fslimit;
int logfile_age, logfile_only, logfile_max;
static ldap_pvt_thread_mutex_t logfile_mutex;
static off_t logfile_fsize;
static time_t logfile_fcreated;
static int logfile_fd;
static char logpaths[2][MAXPATHLEN];
static int logpathlen;
typedef void (BER_logger)(const char *buf);
static BER_logger *ber_logger;
static void debug_print( const char *data )
{
char buf[4136]; /* 4096 + 40 */
char prefix[sizeof("ssssssssssssssss.ffffffff 0xtttttttttttttttt ")];
struct iovec iov[2];
int rotate = 0;
#ifdef HAVE_CLOCK_GETTIME
struct timespec tv;
#define TS "%08x"
#define Tfrac tv.tv_nsec
clock_gettime( CLOCK_REALTIME, &tv );
#define gettime(tv) clock_gettime( CLOCK_REALTIME, tv )
#else
struct timeval tv;
#define TS "%05x"
#define Tfrac tv.tv_usec
gettimeofday( &tv, NULL );
#define gettime(tv) gettimeofday( tv, NULL )
#endif
buf[sizeof(buf)-1] = '\0';
snprintf( buf, sizeof(buf)-1, "%lx." TS " %p %s",
(long)tv.tv_sec, Tfrac, (void *)ldap_pvt_thread_self(), data );
ber_logger( buf );
gettime( &tv );
iov[0].iov_base = prefix;
iov[0].iov_len = sprintf( prefix, "%lx." TS " %p ",
(long)tv.tv_sec, (unsigned int)Tfrac, (void *)ldap_pvt_thread_self() );
iov[1].iov_base = (void *)data;
iov[1].iov_len = strlen( data );
if ( !logfile_only )
writev( 2, iov, 2 );
if ( logfile_fd ) {
int len = iov[0].iov_len + iov[1].iov_len;
if ( logfile_fslimit || logfile_age ) {
ldap_pvt_thread_mutex_lock( &logfile_mutex );
if ( logfile_fslimit && logfile_fsize + len > logfile_fslimit )
rotate = 1;
if ( logfile_age && tv.tv_sec - logfile_fcreated >= logfile_age )
rotate |= 2;
if ( rotate ) {
close( logfile_fd );
strcpy( logpaths[0]+logpathlen, ".tmp" );
rename( logfile_path, logpaths[0] );
logfile_open( logfile_path );
}
}
len = writev( logfile_fd, iov, 2 );
if ( len > 0 )
logfile_fsize += len;
if ( logfile_fslimit || logfile_age )
ldap_pvt_thread_mutex_unlock( &logfile_mutex );
}
if ( rotate ) {
int i;
for (i=logfile_max; i > 1; i--) {
sprintf( logpaths[0]+logpathlen, ".%02d", i );
sprintf( logpaths[1]+logpathlen, ".%02d", i-1 );
rename( logpaths[1], logpaths[0] );
}
sprintf( logpaths[0]+logpathlen, ".tmp" );
rename( logpaths[0], logpaths[1] );
}
}
void logfile_close()
{
if ( logfile_fd ) {
close( logfile_fd );
logfile_fd = 0;
}
logfile_path[0] = '\0';
}
int logfile_open( const char *path )
{
struct stat st;
int fd;
fd = open( path, O_CREAT|O_WRONLY, 0640 );
if ( fd < 0 )
return errno;
if ( fstat( fd, &st )) {
close( fd );
return errno;
}
if ( !logfile_path[0] ) {
logpathlen = strlen( path );
if ( logpathlen >= sizeof(logfile_path) )
return ENAMETOOLONG;
strcpy( logfile_path, path );
strcpy( logpaths[0], path );
strcpy( logpaths[1], path );
}
logfile_fsize = st.st_size;
logfile_fcreated = st.st_ctime; /* not strictly true but close enough */
logfile_fd = fd;
lseek( fd, 0, SEEK_END );
return 0;
}
const char *logfile_name()
{
return logfile_path[0] ? logfile_path : NULL;
}
#ifdef HAVE_NT_SERVICE_MANAGER
@ -449,8 +547,8 @@ int main( int argc, char **argv )
slap_sl_mem_init();
(void) ldap_pvt_thread_initialize();
ldap_pvt_thread_mutex_init( &logfile_mutex );
serverName = lutil_progname( "slapd", argc, argv );
@ -1164,6 +1262,7 @@ stop:
mal_dumpleaktrace( leakfile );
#endif
ldap_pvt_thread_mutex_destroy( &logfile_mutex );
MAIN_RETURN(rc);
}

View File

@ -1254,6 +1254,16 @@ LDAP_SLAPD_F (int)
parse_debug_unknowns LDAP_P(( char **unknowns, int *levelp ));
LDAP_SLAPD_F (void)
slap_check_unknown_level LDAP_P(( char *levelstr, int level ));
LDAP_SLAPD_F (int)
logfile_open LDAP_P(( const char *path ));
LDAP_SLAPD_F (void)
logfile_close LDAP_P(( void ));
LDAP_SLAPD_F (const char *)
logfile_name LDAP_P(( void ));
LDAP_SLAPD_V(int) logfile_age;
LDAP_SLAPD_V(int) logfile_only;
LDAP_SLAPD_V(int) logfile_max;
LDAP_SLAPD_V(long) logfile_fslimit;
/*
* matchedValues.c