]>
Commit | Line | Data |
---|---|---|
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 | } |