diff options
Diffstat (limited to '')
-rw-r--r-- | src/slowlog.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/slowlog.c b/src/slowlog.c new file mode 100644 index 0000000..320f334 --- /dev/null +++ b/src/slowlog.c @@ -0,0 +1,205 @@ +/* Slowlog implements a system that is able to remember the latest N + * queries that took more than M microseconds to execute. + * + * The execution time to reach to be logged in the slow log is set + * using the 'slowlog-log-slower-than' config directive, that is also + * readable and writable using the CONFIG SET/GET command. + * + * The slow queries log is actually not "logged" in the Redis log file + * but is accessible thanks to the SLOWLOG command. + * + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "server.h" +#include "slowlog.h" + +/* Create a new slowlog entry. + * Incrementing the ref count of all the objects retained is up to + * this function. */ +slowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long duration) { + slowlogEntry *se = zmalloc(sizeof(*se)); + int j, slargc = argc; + + if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC; + se->argc = slargc; + se->argv = zmalloc(sizeof(robj*)*slargc); + for (j = 0; j < slargc; j++) { + /* Logging too many arguments is a useless memory waste, so we stop + * at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify + * how many remaining arguments there were in the original command. */ + if (slargc != argc && j == slargc-1) { + se->argv[j] = createObject(OBJ_STRING, + sdscatprintf(sdsempty(),"... (%d more arguments)", + argc-slargc+1)); + } else { + /* Trim too long strings as well... */ + if (argv[j]->type == OBJ_STRING && + sdsEncodedObject(argv[j]) && + sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING) + { + sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING); + + s = sdscatprintf(s,"... (%lu more bytes)", + (unsigned long) + sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING); + se->argv[j] = createObject(OBJ_STRING,s); + } else if (argv[j]->refcount == OBJ_SHARED_REFCOUNT) { + se->argv[j] = argv[j]; + } else { + /* Here we need to duplicate the string objects composing the + * argument vector of the command, because those may otherwise + * end shared with string objects stored into keys. Having + * shared objects between any part of Redis, and the data + * structure holding the data, is a problem: FLUSHALL ASYNC + * may release the shared string object and create a race. */ + se->argv[j] = dupStringObject(argv[j]); + } + } + } + se->time = time(NULL); + se->duration = duration; + se->id = server.slowlog_entry_id++; + se->peerid = sdsnew(getClientPeerId(c)); + se->cname = c->name ? sdsnew(c->name->ptr) : sdsempty(); + return se; +} + +/* Free a slow log entry. The argument is void so that the prototype of this + * function matches the one of the 'free' method of adlist.c. + * + * This function will take care to release all the retained object. */ +void slowlogFreeEntry(void *septr) { + slowlogEntry *se = septr; + int j; + + for (j = 0; j < se->argc; j++) + decrRefCount(se->argv[j]); + zfree(se->argv); + sdsfree(se->peerid); + sdsfree(se->cname); + zfree(se); +} + +/* Initialize the slow log. This function should be called a single time + * at server startup. */ +void slowlogInit(void) { + server.slowlog = listCreate(); + server.slowlog_entry_id = 0; + listSetFreeMethod(server.slowlog,slowlogFreeEntry); +} + +/* Push a new entry into the slow log. + * This function will make sure to trim the slow log accordingly to the + * configured max length. */ +void slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration) { + if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ + if (duration >= server.slowlog_log_slower_than) + listAddNodeHead(server.slowlog, + slowlogCreateEntry(c,argv,argc,duration)); + + /* Remove old entries if needed. */ + while (listLength(server.slowlog) > server.slowlog_max_len) + listDelNode(server.slowlog,listLast(server.slowlog)); +} + +/* Remove all the entries from the current slow log. */ +void slowlogReset(void) { + while (listLength(server.slowlog) > 0) + listDelNode(server.slowlog,listLast(server.slowlog)); +} + +/* The SLOWLOG command. Implements all the subcommands needed to handle the + * Redis slow log. */ +void slowlogCommand(client *c) { + if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { + const char *help[] = { +"GET [<count>]", +" Return top <count> entries from the slowlog (default: 10, -1 mean all).", +" Entries are made of:", +" id, timestamp, time in microseconds, arguments array, client IP and port,", +" client name", +"LEN", +" Return the length of the slowlog.", +"RESET", +" Reset the slowlog.", +NULL + }; + addReplyHelp(c, help); + } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) { + slowlogReset(); + addReply(c,shared.ok); + } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) { + addReplyLongLong(c,listLength(server.slowlog)); + } else if ((c->argc == 2 || c->argc == 3) && + !strcasecmp(c->argv[1]->ptr,"get")) + { + long count = 10, sent = 0; + listIter li; + void *totentries; + listNode *ln; + slowlogEntry *se; + + if (c->argc == 3) { + /* Consume count arg. */ + if (getRangeLongFromObjectOrReply(c, c->argv[2], -1, + LONG_MAX, &count, "count should be greater than or equal to -1") != C_OK) + return; + + if (count == -1) { + /* We treat -1 as a special value, which means to get all slow logs. + * Simply set count to the length of server.slowlog.*/ + count = listLength(server.slowlog); + } + } + + listRewind(server.slowlog,&li); + totentries = addReplyDeferredLen(c); + while(count-- && (ln = listNext(&li))) { + int j; + + se = ln->value; + addReplyArrayLen(c,6); + addReplyLongLong(c,se->id); + addReplyLongLong(c,se->time); + addReplyLongLong(c,se->duration); + addReplyArrayLen(c,se->argc); + for (j = 0; j < se->argc; j++) + addReplyBulk(c,se->argv[j]); + addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid)); + addReplyBulkCBuffer(c,se->cname,sdslen(se->cname)); + sent++; + } + setDeferredArrayLen(c,totentries,sent); + } else { + addReplySubcommandSyntaxError(c); + } +} |