mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 00:28:26 -05:00
116 lines
3.8 KiB
C
116 lines
3.8 KiB
C
#include "redis.h"
|
|
#include "slowlog.h"
|
|
|
|
/* Slowlog implements a system that is able to remember the latest N
|
|
* queries that took more than M microseconds to execute.
|
|
*
|
|
* The execution time to reach to be logged in the slow log is set
|
|
* using the 'slowlog-log-slower-than' config directive, that is also
|
|
* readable and writable using the CONFIG SET/GET command.
|
|
*
|
|
* The slow queries log is actually not "logged" in the Redis log file
|
|
* but is accessible thanks to the SLOWLOG command. */
|
|
|
|
/* Create a new slowlog entry.
|
|
* Incrementing the ref count of all the objects retained is up to
|
|
* this function. */
|
|
slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
|
|
slowlogEntry *se = zmalloc(sizeof(*se));
|
|
int j;
|
|
|
|
se->argc = argc;
|
|
se->argv = zmalloc(sizeof(robj*)*argc);
|
|
for (j = 0; j < argc; j++) {
|
|
se->argv[j] = argv[j];
|
|
incrRefCount(argv[j]);
|
|
}
|
|
se->time = time(NULL);
|
|
se->duration = duration;
|
|
se->id = server.slowlog_entry_id++;
|
|
return se;
|
|
}
|
|
|
|
/* Free a slow log entry. The argument is void so that the prototype of this
|
|
* function matches the one of the 'free' method of adlist.c.
|
|
*
|
|
* This function will take care to release all the retained object. */
|
|
void slowlogFreeEntry(void *septr) {
|
|
slowlogEntry *se = septr;
|
|
int j;
|
|
|
|
for (j = 0; j < se->argc; j++)
|
|
decrRefCount(se->argv[j]);
|
|
zfree(se->argv);
|
|
zfree(se);
|
|
}
|
|
|
|
/* Initialize the slow log. This function should be called a single time
|
|
* at server startup. */
|
|
void slowlogInit(void) {
|
|
server.slowlog = listCreate();
|
|
server.slowlog_entry_id = 0;
|
|
listSetFreeMethod(server.slowlog,slowlogFreeEntry);
|
|
}
|
|
|
|
/* Push a new entry into the slow log.
|
|
* This function will make sure to trim the slow log accordingly to the
|
|
* configured max length. */
|
|
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
|
|
if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
|
|
if (duration >= server.slowlog_log_slower_than)
|
|
listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
|
|
|
|
/* Remove old entries if needed. */
|
|
while (listLength(server.slowlog) > server.slowlog_max_len)
|
|
listDelNode(server.slowlog,listLast(server.slowlog));
|
|
}
|
|
|
|
/* Remove all the entries from the current slow log. */
|
|
void slowlogReset(void) {
|
|
while (listLength(server.slowlog) > 0)
|
|
listDelNode(server.slowlog,listLast(server.slowlog));
|
|
}
|
|
|
|
/* The SLOWLOG command. Implements all the subcommands needed to handle the
|
|
* Redis slow log. */
|
|
void slowlogCommand(redisClient *c) {
|
|
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
|
|
slowlogReset();
|
|
addReply(c,shared.ok);
|
|
} else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
|
|
addReplyLongLong(c,listLength(server.slowlog));
|
|
} else if ((c->argc == 2 || c->argc == 3) &&
|
|
!strcasecmp(c->argv[1]->ptr,"get"))
|
|
{
|
|
long count = 10, sent = 0;
|
|
listIter li;
|
|
void *totentries;
|
|
listNode *ln;
|
|
slowlogEntry *se;
|
|
|
|
if (c->argc == 3 &&
|
|
getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK)
|
|
return;
|
|
|
|
listRewind(server.slowlog,&li);
|
|
totentries = addDeferredMultiBulkLength(c);
|
|
while(count-- && (ln = listNext(&li))) {
|
|
int j;
|
|
|
|
se = ln->value;
|
|
addReplyMultiBulkLen(c,4);
|
|
addReplyLongLong(c,se->id);
|
|
addReplyLongLong(c,se->time);
|
|
addReplyLongLong(c,se->duration);
|
|
addReplyMultiBulkLen(c,se->argc);
|
|
for (j = 0; j < se->argc; j++)
|
|
addReplyBulk(c,se->argv[j]);
|
|
sent++;
|
|
}
|
|
setDeferredMultiBulkLength(c,totentries,sent);
|
|
} else {
|
|
addReplyError(c,
|
|
"Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.");
|
|
}
|
|
}
|