]> git.saurik.com Git - redis.git/blob - src/slowlog.c
Merge pull request #544 from dvirsky/2.6
[redis.git] / src / slowlog.c
1 #include "redis.h"
2 #include "slowlog.h"
3
4 /* Slowlog implements a system that is able to remember the latest N
5 * queries that took more than M microseconds to execute.
6 *
7 * The execution time to reach to be logged in the slow log is set
8 * using the 'slowlog-log-slower-than' config directive, that is also
9 * readable and writable using the CONFIG SET/GET command.
10 *
11 * The slow queries log is actually not "logged" in the Redis log file
12 * but is accessible thanks to the SLOWLOG command. */
13
14 /* Create a new slowlog entry.
15 * Incrementing the ref count of all the objects retained is up to
16 * this function. */
17 slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
18 slowlogEntry *se = zmalloc(sizeof(*se));
19 int j, slargc = argc;
20
21 if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;
22 se->argc = slargc;
23 se->argv = zmalloc(sizeof(robj*)*slargc);
24 for (j = 0; j < slargc; j++) {
25 /* Logging too many arguments is a useless memory waste, so we stop
26 * at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify
27 * how many remaining arguments there were in the original command. */
28 if (slargc != argc && j == slargc-1) {
29 se->argv[j] = createObject(REDIS_STRING,
30 sdscatprintf(sdsempty(),"... (%d more arguments)",
31 argc-slargc+1));
32 } else {
33 /* Trim too long strings as well... */
34 if (argv[j]->type == REDIS_STRING &&
35 argv[j]->encoding == REDIS_ENCODING_RAW &&
36 sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)
37 {
38 sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);
39
40 s = sdscatprintf(s,"... (%lu more bytes)",
41 (unsigned long)
42 sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);
43 se->argv[j] = createObject(REDIS_STRING,s);
44 } else {
45 se->argv[j] = argv[j];
46 incrRefCount(argv[j]);
47 }
48 }
49 }
50 se->time = time(NULL);
51 se->duration = duration;
52 se->id = server.slowlog_entry_id++;
53 return se;
54 }
55
56 /* Free a slow log entry. The argument is void so that the prototype of this
57 * function matches the one of the 'free' method of adlist.c.
58 *
59 * This function will take care to release all the retained object. */
60 void slowlogFreeEntry(void *septr) {
61 slowlogEntry *se = septr;
62 int j;
63
64 for (j = 0; j < se->argc; j++)
65 decrRefCount(se->argv[j]);
66 zfree(se->argv);
67 zfree(se);
68 }
69
70 /* Initialize the slow log. This function should be called a single time
71 * at server startup. */
72 void slowlogInit(void) {
73 server.slowlog = listCreate();
74 server.slowlog_entry_id = 0;
75 listSetFreeMethod(server.slowlog,slowlogFreeEntry);
76 }
77
78 /* Push a new entry into the slow log.
79 * This function will make sure to trim the slow log accordingly to the
80 * configured max length. */
81 void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
82 if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
83 if (duration >= server.slowlog_log_slower_than)
84 listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
85
86 /* Remove old entries if needed. */
87 while (listLength(server.slowlog) > server.slowlog_max_len)
88 listDelNode(server.slowlog,listLast(server.slowlog));
89 }
90
91 /* Remove all the entries from the current slow log. */
92 void slowlogReset(void) {
93 while (listLength(server.slowlog) > 0)
94 listDelNode(server.slowlog,listLast(server.slowlog));
95 }
96
97 /* The SLOWLOG command. Implements all the subcommands needed to handle the
98 * Redis slow log. */
99 void slowlogCommand(redisClient *c) {
100 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
101 slowlogReset();
102 addReply(c,shared.ok);
103 } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
104 addReplyLongLong(c,listLength(server.slowlog));
105 } else if ((c->argc == 2 || c->argc == 3) &&
106 !strcasecmp(c->argv[1]->ptr,"get"))
107 {
108 long count = 10, sent = 0;
109 listIter li;
110 void *totentries;
111 listNode *ln;
112 slowlogEntry *se;
113
114 if (c->argc == 3 &&
115 getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK)
116 return;
117
118 listRewind(server.slowlog,&li);
119 totentries = addDeferredMultiBulkLength(c);
120 while(count-- && (ln = listNext(&li))) {
121 int j;
122
123 se = ln->value;
124 addReplyMultiBulkLen(c,4);
125 addReplyLongLong(c,se->id);
126 addReplyLongLong(c,se->time);
127 addReplyLongLong(c,se->duration);
128 addReplyMultiBulkLen(c,se->argc);
129 for (j = 0; j < se->argc; j++)
130 addReplyBulk(c,se->argv[j]);
131 sent++;
132 }
133 setDeferredMultiBulkLength(c,totentries,sent);
134 } else {
135 addReplyError(c,
136 "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.");
137 }
138 }