Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for multiple log destinations and syslog #30

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 19 additions & 4 deletions redis.conf
Expand Up @@ -45,10 +45,25 @@ timeout 300
# warning (only very important / critical messages are logged) # warning (only very important / critical messages are logged)
loglevel verbose loglevel verbose


# Specify the log file name. Also 'stdout' can be used to force # Specify the log destinations. Must be one or more of the following:
# Redis to log on the standard output. Note that if you use standard # stdout - standard output
# output for logging but daemonize, logs will be sent to /dev/null # stderr - standard error
logfile stdout # file - a custom log file (must also be set by logfile)
# syslog - the system logger
#
# NOTE: If you use standard output or standard error for logging but
# daemonize, logs will be sent to /dev/null
#
# logdest stdout

# Specify the syslog identity.
# logident redis

# Specify the syslog facility. Must be between LOCAL0-LOCAL7.
# logfacility LOCAL0

# Specify the log file name.
# logfile /var/log/redis.log


# Set the number of databases. The default database is DB 0, you can select # Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where # a different one on a per-connection basis using SELECT <dbid> where
Expand Down
141 changes: 126 additions & 15 deletions src/config.c
Expand Up @@ -95,25 +95,98 @@ void loadServerConfig(char *filename) {
err = "Invalid log level. Must be one of debug, notice, warning"; err = "Invalid log level. Must be one of debug, notice, warning";
goto loaderr; goto loaderr;
} }
} else if (!strcasecmp(argv[0],"logdest") && argc >= 2) {
struct
{
const char *name;
const int value;
} validLogDestinations[] =
{
{"stdout", REDIS_LOG_STDOUT},
{"stderr", REDIS_LOG_STDERR},
{"file", REDIS_LOG_FILE},
{"syslog", REDIS_LOG_SYSLOG},
{NULL, 0}
};
int i;

/* We set a default, so clear it first. */
server.logdest = 0;

for (i = 1 ; i < argc ; i++)
{
int j, value = 0;

for (j = 0; validLogDestinations[j].name; j++)
{
if (!strcasecmp(validLogDestinations[j].name, argv[i]))
{
value = validLogDestinations[j].value;
break;
}
}

if (value)
server.logdest |= value;
else
{
err = "Invalid log destination. Must be one of stdout, file, syslog";
goto loaderr;
}
}
} else if (!strcasecmp(argv[0],"logfacility") && argc == 2) {
struct
{
const char *name;
const int value;
} validSyslogFacilities[] =
{
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7},
{NULL, 0}
};
int i;

for (i = 0; validSyslogFacilities[i].name; i++)
{
if (!strcasecmp(validSyslogFacilities[i].name, argv[1]))
{
server.logfacility = validSyslogFacilities[i].value;
break;
}
}

if (!validSyslogFacilities[i].name)
{
err = "Invalid log facility. Must be one of LOCAL0-LOCAL7";
goto loaderr;
}

} else if (!strcasecmp(argv[0],"logfile") && argc == 2) { } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
FILE *logfp; FILE *logfp;


server.logfile = zstrdup(argv[1]); /*
if (!strcasecmp(server.logfile,"stdout")) { * Test if we are able to open the file. The server will not
* be able to abort just for this problem later...
*/
if (server.logfile)
zfree(server.logfile); zfree(server.logfile);
server.logfile = NULL; server.logfile = zstrdup(argv[1]);
} logfp = fopen(server.logfile,"a");
if (server.logfile) { if (logfp == NULL) {
/* Test if we are able to open the file. The server will not err = sdscatprintf(sdsempty(),
* be able to abort just for this problem later... */ "Can't open the log file: %s", strerror(errno));
logfp = fopen(server.logfile,"a"); goto loaderr;
if (logfp == NULL) {
err = sdscatprintf(sdsempty(),
"Can't open the log file: %s", strerror(errno));
goto loaderr;
}
fclose(logfp);
} }
fclose(logfp);
} else if (!strcasecmp(argv[0],"logident") && argc == 2) {
server.logident = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"databases") && argc == 2) { } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
server.dbnum = atoi(argv[1]); server.dbnum = atoi(argv[1]);
if (server.dbnum < 1) { if (server.dbnum < 1) {
Expand Down Expand Up @@ -153,7 +226,7 @@ void loadServerConfig(char *filename) {
server.masterport = atoi(argv[2]); server.masterport = atoi(argv[2]);
server.replstate = REDIS_REPL_CONNECT; server.replstate = REDIS_REPL_CONNECT;
} else if (!strcasecmp(argv[0],"masterauth") && argc == 2) { } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
server.masterauth = zstrdup(argv[1]); server.masterauth = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) { } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) { if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr; err = "argument must be 'yes' or 'no'"; goto loaderr;
Expand Down Expand Up @@ -263,6 +336,44 @@ void loadServerConfig(char *filename) {
sdsfree(line); sdsfree(line);
} }
if (fp != stdin) fclose(fp); if (fp != stdin) fclose(fp);

/*
* Logging configurations require several parameters which may not always
* be provided in order.
*/
if (server.logdest & REDIS_LOG_SYSLOG)
{
openlog(server.logident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
server.logfacility);
}

/*
* This check is kinda redundant, but because we have a default value
* for the server logfile, we have to evaluate that the default is
* valid as well as whether they specified a log file destination,
* but somehow unset the logfile.
*/
if (server.logdest & REDIS_LOG_FILE)
{
linenum = 0;

if (server.logfile)
{
FILE *logfp = fopen(server.logfile, "a");
if (logfp == NULL) {
err = sdscatprintf(sdsempty(),
"Can't open the log file: %s", strerror(errno));
goto loaderr;
}
fclose(logfp);
}
else
{
err = "FILE found in logdest, but logfile is undefined";
goto loaderr;
}
}

return; return;


loaderr: loaderr:
Expand Down
66 changes: 55 additions & 11 deletions src/redis.c
Expand Up @@ -190,25 +190,66 @@ struct redisCommand readonlyCommandTable[] = {
void redisLog(int level, const char *fmt, ...) { void redisLog(int level, const char *fmt, ...) {
va_list ap; va_list ap;
FILE *fp; FILE *fp;
int logdest = server.logdest;
int logmask = 1;
char *c = ".-*#"; char *c = ".-*#";
char buf[64]; char buf[64];
time_t now; time_t now;


if (level < server.verbosity) return; if (level < server.verbosity) return;


fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
if (!fp) return;

va_start(ap, fmt);
now = time(NULL); now = time(NULL);
strftime(buf,64,"%d %b %H:%M:%S",localtime(&now)); strftime(buf,64,"%d %b %H:%M:%S",localtime(&now));
fprintf(fp,"[%d] %s %c ",(int)getpid(),buf,c[level]); va_start(ap, fmt);
vfprintf(fp, fmt, ap);
fprintf(fp,"\n"); /*
fflush(fp); * Handle writing to standard out and/or a custom log file. For a flag to
va_end(ap); * be set, it would have to pass validation during config, so members such
* as server.logfile are guaranteed to be set if REDIS_LOG_FILE is.
*/
while (logdest & (REDIS_LOG_STDOUT | REDIS_LOG_STDERR | REDIS_LOG_FILE))
{
if (logmask & logdest)
{
logdest &= ~logmask;

if (logmask & REDIS_LOG_STDOUT)
fp = stdout;
else if (logmask & REDIS_LOG_STDERR)
fp = stderr;
else
fp = fopen(server.logfile, "a");

fprintf(fp,"[%d] %s %c ",(int)getpid(),buf,c[level]);
vfprintf(fp, fmt, ap);
fprintf(fp,"\n");
fflush(fp);
va_end(ap);
va_start(ap, fmt);


if (server.logfile) fclose(fp); if (fp && (logmask & REDIS_LOG_FILE))
fclose(fp);

}

logmask <<= 1;
}

if (server.logdest & REDIS_LOG_SYSLOG)
{
static const int redisToSyslogLevelMapping[] =
{
[REDIS_DEBUG] = LOG_DEBUG,
[REDIS_VERBOSE] = LOG_DEBUG,
[REDIS_NOTICE] = LOG_NOTICE,
[REDIS_WARNING] = LOG_WARNING,
};

/* NOTE this table requires that */
vsyslog(redisToSyslogLevelMapping[level], fmt, ap);
}

va_end(ap);
} }


/* Redis generally does not try to recover from out of memory conditions /* Redis generally does not try to recover from out of memory conditions
Expand Down Expand Up @@ -742,7 +783,10 @@ void initServerConfig() {
server.maxidletime = REDIS_MAXIDLETIME; server.maxidletime = REDIS_MAXIDLETIME;
server.saveparams = NULL; server.saveparams = NULL;
server.loading = 0; server.loading = 0;
server.logfile = NULL; /* NULL = log on standard output */ server.logfile = zstrdup("/var/log/redis.log");
server.logdest = REDIS_LOG_STDOUT;
server.logident = zstrdup("redis");
server.logfacility = LOG_LOCAL0;
server.glueoutputbuf = 1; server.glueoutputbuf = 1;
server.daemonize = 0; server.daemonize = 0;
server.appendonly = 0; server.appendonly = 0;
Expand Down
10 changes: 10 additions & 0 deletions src/redis.h
Expand Up @@ -17,6 +17,7 @@
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <pthread.h> #include <pthread.h>
#include <syslog.h>


#include "ae.h" /* Event driven programming library */ #include "ae.h" /* Event driven programming library */
#include "sds.h" /* Dynamic safe strings */ #include "sds.h" /* Dynamic safe strings */
Expand Down Expand Up @@ -174,6 +175,12 @@
#define REDIS_SORT_DESC 2 #define REDIS_SORT_DESC 2
#define REDIS_SORTKEY_MAX 1024 #define REDIS_SORTKEY_MAX 1024


/* Log flags */
#define REDIS_LOG_STDOUT 0x01
#define REDIS_LOG_STDERR 0x02
#define REDIS_LOG_FILE 0x04
#define REDIS_LOG_SYSLOG 0x08

/* Log levels */ /* Log levels */
#define REDIS_DEBUG 0 #define REDIS_DEBUG 0
#define REDIS_VERBOSE 1 #define REDIS_VERBOSE 1
Expand Down Expand Up @@ -402,6 +409,9 @@ struct redisServer {
struct saveparam *saveparams; struct saveparam *saveparams;
int saveparamslen; int saveparamslen;
char *logfile; char *logfile;
int logdest;
char *logident;
int logfacility;
char *dbfilename; char *dbfilename;
char *appendfilename; char *appendfilename;
char *requirepass; char *requirepass;
Expand Down