diff --git a/redis.conf b/redis.conf index 5ec2d0295dd373c957a5c862782d9a4d2d0bb3cb..05374ed7ea3adb482298edb65b7ef0bbe81b0577 100644 --- a/redis.conf +++ b/redis.conf @@ -50,6 +50,16 @@ loglevel verbose # output for logging but daemonize, logs will be sent to /dev/null logfile stdout +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + # Set the number of databases. The default database is DB 0, you can select # a different one on a per-connection basis using SELECT where # dbid is a number between 0 and 'databases'-1 diff --git a/src/config.c b/src/config.c index ad60cced36affb06772c45b07cdfe781af66d262..79c367bf7e68c027bac4b986655c6a577a78ba67 100644 --- a/src/config.c +++ b/src/config.c @@ -114,6 +114,42 @@ void loadServerConfig(char *filename) { } fclose(logfp); } + } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) { + if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) { + err = "argument must be 'yes' or 'no'"; goto loaderr; + } + } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) { + if (server.syslog_ident) zfree(server.syslog_ident); + server.syslog_ident = zstrdup(argv[1]); + } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) { + struct { + const char *name; + const int value; + } validSyslogFacilities[] = { + {"user", LOG_USER}, + {"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.syslog_facility = validSyslogFacilities[i].value; + break; + } + } + + if (!validSyslogFacilities[i].name) { + err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7"; + goto loaderr; + } } else if (!strcasecmp(argv[0],"databases") && argc == 2) { server.dbnum = atoi(argv[1]); if (server.dbnum < 1) { diff --git a/src/redis.c b/src/redis.c index 208a3332f591f5b82a95420dc67cb838ff06e3ae..fb6eb46948a46b7f8f910f0f4d6044b90dcb8d34 100644 --- a/src/redis.c +++ b/src/redis.c @@ -193,11 +193,13 @@ struct redisCommand readonlyCommandTable[] = { /*============================ Utility functions ============================ */ void redisLog(int level, const char *fmt, ...) { + const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; + const char *c = ".-*#"; + time_t now = time(NULL); va_list ap; FILE *fp; - char *c = ".-*#"; char buf[64]; - time_t now; + char msg[REDIS_MAX_LOGMSG_LEN]; if (level < server.verbosity) return; @@ -205,15 +207,16 @@ void redisLog(int level, const char *fmt, ...) { if (!fp) return; va_start(ap, fmt); - now = time(NULL); - strftime(buf,64,"%d %b %H:%M:%S",localtime(&now)); - fprintf(fp,"[%d] %s %c ",(int)getpid(),buf,c[level]); - vfprintf(fp, fmt, ap); - fprintf(fp,"\n"); - fflush(fp); + vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); + strftime(buf,sizeof(buf),"%d %b %H:%M:%S",localtime(&now)); + fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); + fflush(fp); + if (server.logfile) fclose(fp); + + if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); } /* Redis generally does not try to recover from out of memory conditions @@ -762,6 +765,9 @@ void initServerConfig() { server.saveparams = NULL; server.loading = 0; server.logfile = NULL; /* NULL = log on standard output */ + server.syslog_enabled = 0; + server.syslog_ident = zstrdup("redis"); + server.syslog_facility = LOG_LOCAL0; server.glueoutputbuf = 1; server.daemonize = 0; server.appendonly = 0; @@ -832,6 +838,11 @@ void initServer() { signal(SIGPIPE, SIG_IGN); setupSigSegvAction(); + if (server.syslog_enabled) { + openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT, + server.syslog_facility); + } + server.mainthread = pthread_self(); server.clients = listCreate(); server.slaves = listCreate(); diff --git a/src/redis.h b/src/redis.h index 4a6af106b92b3a4168b2a09062a1bcc0c09d35de..c3309f3329a946706da7bb7e5daa86a8f38c106c 100644 --- a/src/redis.h +++ b/src/redis.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "ae.h" /* Event driven programming library */ #include "sds.h" /* Dynamic safe strings */ @@ -47,6 +48,7 @@ #define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */ #define REDIS_SHARED_INTEGERS 10000 #define REDIS_REPLY_CHUNK_BYTES (5*1500) /* 5 TCP packets with default MTU */ +#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */ /* If more then REDIS_WRITEV_THRESHOLD write packets are pending use writev */ #define REDIS_WRITEV_THRESHOLD 3 @@ -408,6 +410,9 @@ struct redisServer { struct saveparam *saveparams; int saveparamslen; char *logfile; + int syslog_enabled; + char *syslog_ident; + int syslog_facility; char *dbfilename; char *appendfilename; char *requirepass;