daa70b17 |
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)); |
590d55a2 |
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); |
daa70b17 |
39 | |
590d55a2 |
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 | } |
daa70b17 |
49 | } |
50 | se->time = time(NULL); |
51 | se->duration = duration; |
2cb68284 |
52 | se->id = server.slowlog_entry_id++; |
daa70b17 |
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(); |
2cb68284 |
74 | server.slowlog_entry_id = 0; |
daa70b17 |
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) { |
35a60441 |
82 | if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ |
de32c37c |
83 | if (duration >= server.slowlog_log_slower_than) |
daa70b17 |
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; |
2cb68284 |
124 | addReplyMultiBulkLen(c,4); |
125 | addReplyLongLong(c,se->id); |
daa70b17 |
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 | } |