xref: /redis-3.2.3/src/slowlog.c (revision 40eb548a)
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 "server.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. */
slowlogCreateEntry(robj ** argv,int argc,long long duration)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(OBJ_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 == OBJ_STRING &&
66                 sdsEncodedObject(argv[j]) &&
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(OBJ_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. */
slowlogFreeEntry(void * septr)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. */
slowlogInit(void)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. */
slowlogPushEntryIfNeeded(robj ** argv,int argc,long long duration)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. */
slowlogReset(void)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. */
slowlogCommand(client * c)130 void slowlogCommand(client *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) != C_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