| 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; |
| 20 | |
| 21 | se->argc = argc; |
| 22 | se->argv = zmalloc(sizeof(robj*)*argc); |
| 23 | for (j = 0; j < argc; j++) { |
| 24 | se->argv[j] = argv[j]; |
| 25 | incrRefCount(argv[j]); |
| 26 | } |
| 27 | se->time = time(NULL); |
| 28 | se->duration = duration; |
| 29 | se->id = server.slowlog_entry_id++; |
| 30 | return se; |
| 31 | } |
| 32 | |
| 33 | /* Free a slow log entry. The argument is void so that the prototype of this |
| 34 | * function matches the one of the 'free' method of adlist.c. |
| 35 | * |
| 36 | * This function will take care to release all the retained object. */ |
| 37 | void slowlogFreeEntry(void *septr) { |
| 38 | slowlogEntry *se = septr; |
| 39 | int j; |
| 40 | |
| 41 | for (j = 0; j < se->argc; j++) |
| 42 | decrRefCount(se->argv[j]); |
| 43 | zfree(se->argv); |
| 44 | zfree(se); |
| 45 | } |
| 46 | |
| 47 | /* Initialize the slow log. This function should be called a single time |
| 48 | * at server startup. */ |
| 49 | void slowlogInit(void) { |
| 50 | server.slowlog = listCreate(); |
| 51 | server.slowlog_entry_id = 0; |
| 52 | listSetFreeMethod(server.slowlog,slowlogFreeEntry); |
| 53 | } |
| 54 | |
| 55 | /* Push a new entry into the slow log. |
| 56 | * This function will make sure to trim the slow log accordingly to the |
| 57 | * configured max length. */ |
| 58 | void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) { |
| 59 | if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ |
| 60 | if (duration >= server.slowlog_log_slower_than) |
| 61 | listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration)); |
| 62 | |
| 63 | /* Remove old entries if needed. */ |
| 64 | while (listLength(server.slowlog) > server.slowlog_max_len) |
| 65 | listDelNode(server.slowlog,listLast(server.slowlog)); |
| 66 | } |
| 67 | |
| 68 | /* Remove all the entries from the current slow log. */ |
| 69 | void slowlogReset(void) { |
| 70 | while (listLength(server.slowlog) > 0) |
| 71 | listDelNode(server.slowlog,listLast(server.slowlog)); |
| 72 | } |
| 73 | |
| 74 | /* The SLOWLOG command. Implements all the subcommands needed to handle the |
| 75 | * Redis slow log. */ |
| 76 | void slowlogCommand(redisClient *c) { |
| 77 | if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) { |
| 78 | slowlogReset(); |
| 79 | addReply(c,shared.ok); |
| 80 | } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) { |
| 81 | addReplyLongLong(c,listLength(server.slowlog)); |
| 82 | } else if ((c->argc == 2 || c->argc == 3) && |
| 83 | !strcasecmp(c->argv[1]->ptr,"get")) |
| 84 | { |
| 85 | long count = 10, sent = 0; |
| 86 | listIter li; |
| 87 | void *totentries; |
| 88 | listNode *ln; |
| 89 | slowlogEntry *se; |
| 90 | |
| 91 | if (c->argc == 3 && |
| 92 | getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK) |
| 93 | return; |
| 94 | |
| 95 | listRewind(server.slowlog,&li); |
| 96 | totentries = addDeferredMultiBulkLength(c); |
| 97 | while(count-- && (ln = listNext(&li))) { |
| 98 | int j; |
| 99 | |
| 100 | se = ln->value; |
| 101 | addReplyMultiBulkLen(c,4); |
| 102 | addReplyLongLong(c,se->id); |
| 103 | addReplyLongLong(c,se->time); |
| 104 | addReplyLongLong(c,se->duration); |
| 105 | addReplyMultiBulkLen(c,se->argc); |
| 106 | for (j = 0; j < se->argc; j++) |
| 107 | addReplyBulk(c,se->argv[j]); |
| 108 | sent++; |
| 109 | } |
| 110 | setDeferredMultiBulkLength(c,totentries,sent); |
| 111 | } else { |
| 112 | addReplyError(c, |
| 113 | "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN."); |
| 114 | } |
| 115 | } |