***********************************h********************************************************
#ifndef __SLOWLOG_H__
#define __SLOWLOG_H__
#define SLOWLOG_ENTRY_MAX_ARGC 32
#define SLOWLOG_ENTRY_MAX_STRING 128
/* This structure defines an entry inside the slow log list */
这个结构体定义了慢查询日志列表中的一个实体
typedef struct slowlogEntry {
robj **argv; 参数对象
int argc; 参数个数
long long id; /* Unique entry identifier. */ 每个实体的唯一标识
long long duration; /* Time spent by the query, in microseconds. */ 用微秒计数的查询消耗时间
time_t time; /* Unix time at which the query was executed. */ 查询喀什执行时的unix系统时间
sds cname; /* Client name. */ 执行的客户端名字
sds peerid; /* Client network address. */ 执行的客户端地址
} slowlogEntry;
/* Exported API */ 供调用的API
void slowlogInit(void); 慢日志初始化
void slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration); 如果需要将慢日志压入到列表
/* Exported commands */ 供调用的命令
void slowlogCommand(client *c);
#endif /* __SLOWLOG_H__ */
*************************************c*******************************************************
/* Slowlog implements a system that is able to remember the latest N
* queries that took more than M microseconds to execute.
慢查询实现了一个系统可以记住最近N个执行花费时间超过M微秒的查询命令
* 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.
达到被慢查询记录到日志中的执行时间由配置文件的参数slowlog-log-slower-than设置,
这个配置项可以使用CONFIG SET/GET 命令进行读写
* The slow queries log is actually not "logged" in the Redis log file
* but is accessible thanks to the SLOWLOG command.
慢查询日志实际上没有“记录”在Redis日志文件中,但可以通过SLOWLOG命令进行访问
* ----------------------------------------------------------------------------
#include "server.h"
#include "slowlog.h"
/* Create a new slowlog entry.
* Incrementing the ref count of all the objects retained is up to
* this function. */
创建一个新慢查询日志实体。
由这个函数决定是否增加所有保留对象的引用计数。
slowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long duration) {
slowlogEntry *se = zmalloc(sizeof(*se)); 创建慢日志实体结构
int j, slargc = argc;
if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC; 记录命令最多允许的参数
se->argc = slargc;
se->argv = zmalloc(sizeof(robj*)*slargc);根据参数个数分配空间
for (j = 0; j < slargc; j++) {
/* Logging too many arguments is a useless memory waste, so we stop
* at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify
* how many remaining arguments there were in the original command. */
记录太多的参数是一种无用的内存浪费,因此我们最多记录SLOWLOG_ENTRY_MAX_ARGC-1个参数变量,
最后一个位置的参数用来记录在原始命令中还剩多少参数。
if (slargc != argc && j == slargc-1) {
se->argv[j] = createObject(OBJ_STRING,
sdscatprintf(sdsempty(),"... (%d more arguments)",
argc-slargc+1)); 最后一个位置记录剩余参数的个数
} else {
/* Trim too long strings as well... */ 修剪过长的字符串
if (argv[j]->type == OBJ_STRING && 参数类型是字符串
sdsEncodedObject(argv[j]) && 是字符串编码
sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING) 字符串长度大于128
{
sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING); 最多知道128的长度
s = sdscatprintf(s,"... (%lu more bytes)",
(unsigned long)
sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING); 取前面128个字符
se->argv[j] = createObject(OBJ_STRING,s); 创建新对象
} else if (argv[j]->refcount == OBJ_SHARED_REFCOUNT) { 共享对象
se->argv[j] = argv[j];
} else {
/* Here we need to dupliacate the string objects composing the
* argument vector of the command, because those may otherwise
* end shared with string objects stored into keys. Having
* shared objects between any part of Redis, and the data
* structure holding the data, is a problem: FLUSHALL ASYNC
* may release the shared string object and create a race. */
在这里,我们需要复制构成命令参数向量的字符串对象,因为这些对象可能最终与存储在键中的字符串对象共享。
在Redis的任何部分和保存数据的数据结构之间拥有共享对象是一个问题:
FLUSHALL ASYNC可能会释放共享字符串对象并创建竞争
se->argv[j] = dupStringObject(argv[j]); 创建复制对象,避免竞争
}
}
}
se->time = time(NULL); 当前时间
se->duration = duration; 持续时间
se->id = server.slowlog_entry_id++; 日志唯一id
se->peerid = sdsnew(getClientPeerId(c)); 客户端地址
se->cname = c->name ? sdsnew(c->name->ptr) : sdsempty(); 客户端名称
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.
释放一个慢日志实体。参数为空,因此这个函数的原型匹配adlist.c中的free函数。
* 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);释放参数数组本身
sdsfree(se->peerid);释放客户端地址
sdsfree(se->cname);释放客户端名
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; 初始化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(client *c, robj **argv, int argc, long long duration) {
if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ 配置小于0时,为禁用慢日志
if (duration >= server.slowlog_log_slower_than) 如果持续时间大于 配置的持续时间 需要记录
listAddNodeHead(server.slowlog,
slowlogCreateEntry(c,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. */
慢查询命令。实现用来处理redis慢日志的所有子命令
void slowlogCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { 两个参数,是 命令 help 的格式,给出提示信息
const char *help[] = {
"GET [count] -- Return top entries from the slowlog (default: 10)." 从慢查询列表返回最上面的日志(默认10条)
" Entries are made of:", 实体格式如下:
" id, timestamp, time in microseconds, arguments array, client IP and port, client name",
唯一ID,执行时间点,执行时长用微秒计, 参数数组, 客户IP和端口, 客户端名字
"LEN -- Return the length of the slowlog.", 返回慢日志的长度(个数)
"RESET -- Reset the slowlog.", 重置慢日志(清空)
NULL
};
addReplyHelp(c, help); 返回给客户端
} else 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")) 如果是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) != C_OK) 当三个参数的时候,需要获取个数参数
return;
listRewind(server.slowlog,&li); 初始化列表迭代
totentries = addReplyDeferredLen(c); 长度占位
while(count-- && (ln = listNext(&li))) { 不够数 并且 下一个不为空
int j;
se = ln->value; 获取慢日志实体
addReplyArrayLen(c,6); 6个参数
addReplyLongLong(c,se->id); 唯一id
addReplyLongLong(c,se->time); 执行时间点
addReplyLongLong(c,se->duration); 持续时间
addReplyArrayLen(c,se->argc); 具体参数个数
for (j = 0; j < se->argc; j++)
addReplyBulk(c,se->argv[j]); 每个具体的参数
addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid)); 客户端地址
addReplyBulkCBuffer(c,se->cname,sdslen(se->cname)); 客户端名字
sent++; 计数++
}
setDeferredArrayLen(c,totentries,sent); 设置总长度
} else {
addReplySubcommandSyntaxError(c); 其余情况回复格式错误
}
}