Skip to content

Commit 5ef5641

Browse files
committed
Improve error log message output
Previously when an error were received by a Redis server, it was not clear whether this came from a Redis Sentinel or a Redis server in the cluster. This commit adds the hostname/port of the server to the exception message via a log context. Note that this commit also fixes an existing bug where an error in a MULTI/EXEC command would not properly be processed. Closes redis-rb#177
1 parent 26d3554 commit 5ef5641

File tree

6 files changed

+48
-12
lines changed

6 files changed

+48
-12
lines changed

hiredis-client/lib/redis_client/hiredis_connection.rb

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ def write_multi(commands)
119119

120120
private
121121

122+
def host_context
123+
@config.path || "#{@config.host}:#{@config.port}"
124+
end
125+
122126
def connect
123127
_connect(@config.path, @config.host, @config.port, @config.ssl_context)
124128
rescue SystemCallError => error

lib/redis_client.rb

+22-5
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,27 @@ def timeout=(timeout)
7979

8080
Error = Class.new(StandardError)
8181

82-
ProtocolError = Class.new(Error)
83-
UnsupportedServer = Class.new(Error)
82+
class ErrorWithContext < Error
83+
attr_reader :context
84+
alias_method :original_to_s, :to_s
85+
86+
def _set_context(context)
87+
@context = context
88+
end
89+
90+
def to_s
91+
if context
92+
"#{original_to_s} (#{context})"
93+
else
94+
original_to_s
95+
end
96+
end
97+
end
98+
99+
ProtocolError = Class.new(ErrorWithContext)
100+
UnsupportedServer = Class.new(ErrorWithContext)
101+
ConnectionError = Class.new(ErrorWithContext)
84102

85-
ConnectionError = Class.new(Error)
86103
CannotConnectError = Class.new(ConnectionError)
87104

88105
FailoverError = Class.new(ConnectionError)
@@ -100,7 +117,7 @@ def _set_command(command)
100117
end
101118
end
102119

103-
class CommandError < Error
120+
class CommandError < ErrorWithContext
104121
include HasCommand
105122

106123
class << self
@@ -114,7 +131,7 @@ def parse(error_message)
114131
end
115132
code ||= error_message.split(' ', 2).first
116133
klass = ERRORS.fetch(code, self)
117-
klass.new(error_message)
134+
klass.new(error_message&.strip)
118135
end
119136
end
120137
end

lib/redis_client/connection_mixin.rb

+11-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def call(command, timeout)
3232
@pending_reads -= 1
3333
if result.is_a?(Error)
3434
result._set_command(command)
35+
result._set_context(host_context) if result.is_a?(ErrorWithContext)
3536
raise result
3637
else
3738
result
@@ -50,10 +51,17 @@ def call_pipelined(commands, timeouts)
5051
timeout = timeouts && timeouts[index]
5152
result = read(timeout)
5253
@pending_reads -= 1
53-
if result.is_a?(Error)
54-
result._set_command(commands[index])
55-
exception ||= result
54+
55+
# A multi/exec command can return an array of results
56+
result_arr = Array(result)
57+
result_arr.each do |res|
58+
next unless res.is_a?(Error)
59+
60+
res._set_command(commands[index])
61+
res._set_context(host_context) if res.is_a?(ErrorWithContext)
62+
exception ||= res
5663
end
64+
5765
results[index] = result
5866
end
5967

lib/redis_client/ruby_connection.rb

+4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ def measure_round_trip_delay
109109

110110
private
111111

112+
def host_context
113+
@config.path || "#{@config.host}:#{@config.port}"
114+
end
115+
112116
def connect
113117
socket = if @config.path
114118
UNIXSocket.new(@config.path)

test/redis_client_test.rb

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def call_pipelined(commands, *)
6464
client.call("PING")
6565
end
6666
assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
67+
assert_includes error.message, "(redis://"
6768
end
6869

6970
def test_redis_6_server_with_missing_hello_command
@@ -82,6 +83,7 @@ def call_pipelined(commands, *)
8283
client.call("PING")
8384
end
8485
assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
86+
assert_includes error.message, "(redis://"
8587
end
8688

8789
def test_handle_async_raise

test/shared/redis_client_tests.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -271,28 +271,29 @@ def test_wrong_type
271271
@redis.call("SISMEMBER", "str", "member")
272272
end
273273
assert_equal ["SISMEMBER", "str", "member"], error.command
274-
assert_includes error.message, "WRONGTYPE Operation against a key holding the wrong kind of value"
274+
assert_match(/WRONGTYPE Operation against a key holding the wrong kind of value (.*:.*)/, error.message)
275275

276276
error = assert_raises RedisClient::CommandError do
277277
@redis.pipelined do |pipeline|
278278
pipeline.call("SISMEMBER", "str", "member")
279279
end
280280
end
281-
assert_includes error.message, "WRONGTYPE Operation against a key holding the wrong kind of value"
281+
assert_match(/WRONGTYPE Operation against a key holding the wrong kind of value (.*:.*)/, error.message)
282282

283283
error = assert_raises RedisClient::CommandError do
284284
@redis.multi do |transaction|
285285
transaction.call("SISMEMBER", "str", "member")
286286
end
287287
end
288-
assert_includes error.message, "WRONGTYPE Operation against a key holding the wrong kind of value"
288+
289+
assert_match(/WRONGTYPE Operation against a key holding the wrong kind of value (.*:.*)/, error.message)
289290
end
290291

291292
def test_command_missing
292293
error = assert_raises RedisClient::CommandError do
293294
@redis.call("DOESNOTEXIST", "foo")
294295
end
295-
assert_match(/ERR unknown command .DOESNOTEXIST./, error.message)
296+
assert_match(/ERR unknown command .DOESNOTEXIST.*\(.*:.*\)/, error.message)
296297
end
297298

298299
def test_authentication

0 commit comments

Comments
 (0)