From d3701d27141b8e400ccdf5fbf22c504d112fab63 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Sat, 21 Apr 2012 19:20:03 +0200
Subject: [PATCH] Limit memory used by big SLOWLOG entries.

Two limits are added:

1) Up to SLOWLOG_ENTRY_MAX_ARGV arguments are logged.
2) Up to SLOWLOG_ENTRY_MAX_STRING bytes per argument are logged.
3) slowlog-max-len is set to 128 by default (was 1024).

The number of remaining arguments / bytes is logged in the entry
so that the user can understand better the nature of the logged command.
---
 redis.conf             |  2 +-
 src/redis.h            |  2 +-
 src/slowlog.c          | 35 +++++++++++++++++++++++++++++------
 src/slowlog.h          |  3 +++
 tests/unit/slowlog.tcl | 17 +++++++++++++++++
 5 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/redis.conf b/redis.conf
index b1ea6c2e..ed0e2f0e 100644
--- a/redis.conf
+++ b/redis.conf
@@ -447,7 +447,7 @@ slowlog-log-slower-than 10000
 
 # There is no limit to this length. Just be aware that it will consume memory.
 # You can reclaim memory used by the slow log with SLOWLOG RESET.
-slowlog-max-len 1024
+slowlog-max-len 128
 
 ############################### ADVANCED CONFIG ###############################
 
diff --git a/src/redis.h b/src/redis.h
index 42a9b581..3b9d4e78 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -52,7 +52,7 @@
 #define REDIS_AOF_REWRITE_MIN_SIZE (1024*1024)
 #define REDIS_AOF_REWRITE_ITEMS_PER_CMD 64
 #define REDIS_SLOWLOG_LOG_SLOWER_THAN 10000
-#define REDIS_SLOWLOG_MAX_LEN 64
+#define REDIS_SLOWLOG_MAX_LEN 128
 #define REDIS_MAX_CLIENTS 10000
 
 #define REDIS_REPL_TIMEOUT 60
diff --git a/src/slowlog.c b/src/slowlog.c
index cfd66dc6..53c44a01 100644
--- a/src/slowlog.c
+++ b/src/slowlog.c
@@ -16,13 +16,36 @@
  * this function. */
 slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
     slowlogEntry *se = zmalloc(sizeof(*se));
-    int j;
+    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. */
+        if (slargc != argc && j == slargc-1) {
+            se->argv[j] = createObject(REDIS_STRING,
+                sdscatprintf(sdsempty(),"... (%d more arguments)",
+                argc-slargc+1));
+        } else {
+            /* Trim too long strings as well... */
+            if (argv[j]->type == REDIS_STRING &&
+                argv[j]->encoding == REDIS_ENCODING_RAW &&
+                sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)
+            {
+                sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);
 
-    se->argc = argc;
-    se->argv = zmalloc(sizeof(robj*)*argc);
-    for (j = 0; j < argc; j++) {
-        se->argv[j] = argv[j];
-        incrRefCount(argv[j]);
+                s = sdscatprintf(s,"... (%lu more bytes)",
+                    (unsigned long)
+                    sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);
+                se->argv[j] = createObject(REDIS_STRING,s);
+            } else {
+                se->argv[j] = argv[j];
+                incrRefCount(argv[j]);
+            }
+        }
     }
     se->time = time(NULL);
     se->duration = duration;
diff --git a/src/slowlog.h b/src/slowlog.h
index bad770db..bcc961cc 100644
--- a/src/slowlog.h
+++ b/src/slowlog.h
@@ -1,3 +1,6 @@
+#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;
diff --git a/tests/unit/slowlog.tcl b/tests/unit/slowlog.tcl
index 55a71e98..2216e925 100644
--- a/tests/unit/slowlog.tcl
+++ b/tests/unit/slowlog.tcl
@@ -38,4 +38,21 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
         assert_equal [expr {[lindex $e 2] > 100000}] 1
         assert_equal [lindex $e 3] {debug sleep 0.2}
     }
+
+    test {SLOWLOG - commands with too many arguments are trimmed} {
+        r config set slowlog-log-slower-than 0
+        r slowlog reset
+        r sadd set 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
+        set e [lindex [r slowlog get] 0]
+        lindex $e 3
+    } {sadd set 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 {... (2 more arguments)}}
+
+    test {SLOWLOG - too long arguments are trimmed} {
+        r config set slowlog-log-slower-than 0
+        r slowlog reset
+        set arg [string repeat A 129]
+        r sadd set foo $arg
+        set e [lindex [r slowlog get] 0]
+        lindex $e 3
+    } {sadd set foo {AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... (1 more bytes)}}
 }
-- 
2.47.2