1 /* Slowlog implements a system that is able to remember the latest N 2 * queries that took more than M microseconds to execute. 3 * 4 * The execution time to reach to be logged in the slow log is set 5 * using the 'slowlog-log-slower-than' config directive, that is also 6 * readable and writable using the CONFIG SET/GET command. 7 * 8 * The slow queries log is actually not "logged" in the Redis log file 9 * but is accessible thanks to the SLOWLOG command. 10 * 11 * ---------------------------------------------------------------------------- 12 * 13 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 14 * All rights reserved. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions are met: 18 * 19 * * Redistributions of source code must retain the above copyright notice, 20 * this list of conditions and the following disclaimer. 21 * * Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * * Neither the name of Redis nor the names of its contributors may be used 25 * to endorse or promote products derived from this software without 26 * specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41 42 #include "redis.h" 43 #include "slowlog.h" 44 45 /* Create a new slowlog entry. 46 * Incrementing the ref count of all the objects retained is up to 47 * this function. */ 48 slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) { 49 slowlogEntry *se = zmalloc(sizeof(*se)); 50 int j, slargc = argc; 51 52 if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC; 53 se->argc = slargc; 54 se->argv = zmalloc(sizeof(robj*)*slargc); 55 for (j = 0; j < slargc; j++) { 56 /* Logging too many arguments is a useless memory waste, so we stop 57 * at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify 58 * how many remaining arguments there were in the original command. */ 59 if (slargc != argc && j == slargc-1) { 60 se->argv[j] = createObject(REDIS_STRING, 61 sdscatprintf(sdsempty(),"... (%d more arguments)", 62 argc-slargc+1)); 63 } else { 64 /* Trim too long strings as well... */ 65 if (argv[j]->type == REDIS_STRING && 66 argv[j]->encoding == REDIS_ENCODING_RAW && 67 sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING) 68 { 69 sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING); 70 71 s = sdscatprintf(s,"... (%lu more bytes)", 72 (unsigned long) 73 sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING); 74 se->argv[j] = createObject(REDIS_STRING,s); 75 } else { 76 se->argv[j] = argv[j]; 77 incrRefCount(argv[j]); 78 } 79 } 80 } 81 se->time = time(NULL); 82 se->duration = duration; 83 se->id = server.slowlog_entry_id++; 84 return se; 85 } 86 87 /* Free a slow log entry. The argument is void so that the prototype of this 88 * function matches the one of the 'free' method of adlist.c. 89 * 90 * This function will take care to release all the retained object. */ 91 void slowlogFreeEntry(void *septr) { 92 slowlogEntry *se = septr; 93 int j; 94 95 for (j = 0; j < se->argc; j++) 96 decrRefCount(se->argv[j]); 97 zfree(se->argv); 98 zfree(se); 99 } 100 101 /* Initialize the slow log. This function should be called a single time 102 * at server startup. */ 103 void slowlogInit(void) { 104 server.slowlog = listCreate(); 105 server.slowlog_entry_id = 0; 106 listSetFreeMethod(server.slowlog,slowlogFreeEntry); 107 } 108 109 /* Push a new entry into the slow log. 110 * This function will make sure to trim the slow log accordingly to the 111 * configured max length. */ 112 void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) { 113 if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ 114 if (duration >= server.slowlog_log_slower_than) 115 listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration)); 116 117 /* Remove old entries if needed. */ 118 while (listLength(server.slowlog) > server.slowlog_max_len) 119 listDelNode(server.slowlog,listLast(server.slowlog)); 120 } 121 122 /* Remove all the entries from the current slow log. */ 123 void slowlogReset(void) { 124 while (listLength(server.slowlog) > 0) 125 listDelNode(server.slowlog,listLast(server.slowlog)); 126 } 127 128 /* The SLOWLOG command. Implements all the subcommands needed to handle the 129 * Redis slow log. */ 130 void slowlogCommand(redisClient *c) { 131 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) { 132 slowlogReset(); 133 addReply(c,shared.ok); 134 } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) { 135 addReplyLongLong(c,listLength(server.slowlog)); 136 } else if ((c->argc == 2 || c->argc == 3) && 137 !strcasecmp(c->argv[1]->ptr,"get")) 138 { 139 long count = 10, sent = 0; 140 listIter li; 141 void *totentries; 142 listNode *ln; 143 slowlogEntry *se; 144 145 if (c->argc == 3 && 146 getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK) 147 return; 148 149 listRewind(server.slowlog,&li); 150 totentries = addDeferredMultiBulkLength(c); 151 while(count-- && (ln = listNext(&li))) { 152 int j; 153 154 se = ln->value; 155 addReplyMultiBulkLen(c,4); 156 addReplyLongLong(c,se->id); 157 addReplyLongLong(c,se->time); 158 addReplyLongLong(c,se->duration); 159 addReplyMultiBulkLen(c,se->argc); 160 for (j = 0; j < se->argc; j++) 161 addReplyBulk(c,se->argv[j]); 162 sent++; 163 } 164 setDeferredMultiBulkLength(c,totentries,sent); 165 } else { 166 addReplyError(c, 167 "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN."); 168 } 169 } 170