Skip to content

Commit 7879c84

Browse files
authored
Merge pull request #178 from stanhu/sh-improve-log-messages
Improve error log message output
2 parents dfef587 + 5b0e6f7 commit 7879c84

File tree

9 files changed

+101
-34
lines changed

9 files changed

+101
-34
lines changed

hiredis-client/lib/redis_client/hiredis_connection.rb

+14-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ def initialize(ca_file: nil, ca_path: nil, cert: nil, key: nil, hostname: nil)
3939
end
4040
end
4141

42+
attr_reader :config
43+
4244
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
4345
super()
4446
@config = config
@@ -98,14 +100,20 @@ def read(timeout = nil)
98100
end
99101
end
100102
rescue SystemCallError, IOError => error
101-
raise ConnectionError, error.message
103+
raise ConnectionError.with_config(error.message, config)
104+
rescue Error => error
105+
error._set_config(config)
106+
raise error
102107
end
103108

104109
def write(command)
105110
_write(command)
106111
flush
107112
rescue SystemCallError, IOError => error
108-
raise ConnectionError, error.message
113+
raise ConnectionError.with_config(error.message, config)
114+
rescue Error => error
115+
error._set_config(config)
116+
raise error
109117
end
110118

111119
def write_multi(commands)
@@ -114,7 +122,10 @@ def write_multi(commands)
114122
end
115123
flush
116124
rescue SystemCallError, IOError => error
117-
raise ConnectionError, error.message
125+
raise ConnectionError.with_config(error.message, config)
126+
rescue Error => error
127+
error._set_config(config)
128+
raise error
118129
end
119130

120131
private

lib/redis_client.rb

+30-5
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,29 @@ def timeout=(timeout)
7777
end
7878
end
7979

80-
Error = Class.new(StandardError)
80+
module HasConfig
81+
attr_reader :config
82+
83+
def _set_config(config)
84+
@config = config
85+
end
86+
87+
def message
88+
return super unless config
89+
90+
"#{super} (#{config.server_url})"
91+
end
92+
end
93+
94+
class Error < StandardError
95+
include HasConfig
96+
97+
def self.with_config(message, config = nil)
98+
new(message).tap do |error|
99+
error._set_config(config)
100+
end
101+
end
102+
end
81103

82104
ProtocolError = Class.new(Error)
83105
UnsupportedServer = Class.new(Error)
@@ -114,7 +136,7 @@ def parse(error_message)
114136
end
115137
code ||= error_message.split(' ', 2).first
116138
klass = ERRORS.fetch(code, self)
117-
klass.new(error_message)
139+
klass.new(error_message.strip)
118140
end
119141
end
120142
end
@@ -750,10 +772,13 @@ def connect
750772
end
751773
end
752774
end
753-
rescue FailoverError, CannotConnectError
754-
raise
775+
rescue FailoverError, CannotConnectError => error
776+
error._set_config(config)
777+
raise error
755778
rescue ConnectionError => error
756-
raise CannotConnectError, error.message, error.backtrace
779+
connect_error = CannotConnectError.with_config(error.message, config)
780+
connect_error.set_backtrace(error.backtrace)
781+
raise connect_error
757782
rescue CommandError => error
758783
if error.message.match?(/ERR unknown command ['`]HELLO['`]/)
759784
raise UnsupportedServer,

lib/redis_client/config.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,17 @@ def ssl_context
124124

125125
def server_url
126126
if path
127-
"#{path}/#{db}"
127+
url = "unix://#{path}"
128+
if db != 0
129+
url = "#{url}?db=#{db}"
130+
end
128131
else
129-
"redis#{'s' if ssl?}://#{host}:#{port}/#{db}"
132+
url = "redis#{'s' if ssl?}://#{host}:#{port}"
133+
if db != 0
134+
url = "#{url}/#{db}"
135+
end
130136
end
137+
url
131138
end
132139

133140
private

lib/redis_client/connection_mixin.rb

+8-1
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_config(config)
3536
raise result
3637
else
3738
result
@@ -53,10 +54,16 @@ def call_pipelined(commands, timeouts)
5354

5455
# A multi/exec command can return an array of results.
5556
# An error from a multi/exec command is handled in Multi#_coerce!.
56-
if result.is_a?(Error)
57+
if result.is_a?(Array)
58+
result.each do |res|
59+
res._set_config(config) if res.is_a?(Error)
60+
end
61+
elsif result.is_a?(Error)
5762
result._set_command(commands[index])
63+
result._set_config(config)
5864
exception ||= result
5965
end
66+
6067
results[index] = result
6168
end
6269

lib/redis_client/ruby_connection.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def ssl_context(ssl_params)
4040

4141
SUPPORTS_RESOLV_TIMEOUT = Socket.method(:tcp).parameters.any? { |p| p.last == :resolv_timeout }
4242

43+
attr_reader :config
44+
4345
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
4446
super()
4547
@config = config
@@ -73,7 +75,7 @@ def write(command)
7375
begin
7476
@io.write(buffer)
7577
rescue SystemCallError, IOError => error
76-
raise ConnectionError, error.message
78+
raise ConnectionError.with_config(error.message, config)
7779
end
7880
end
7981

@@ -85,7 +87,7 @@ def write_multi(commands)
8587
begin
8688
@io.write(buffer)
8789
rescue SystemCallError, IOError => error
88-
raise ConnectionError, error.message
90+
raise ConnectionError.with_config(error.message, config)
8991
end
9092
end
9193

@@ -96,9 +98,9 @@ def read(timeout = nil)
9698
@io.with_timeout(timeout) { RESP3.load(@io) }
9799
end
98100
rescue RedisClient::RESP3::UnknownType => error
99-
raise RedisClient::ProtocolError, error.message
101+
raise RedisClient::ProtocolError.with_config(error.message, config)
100102
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError => error
101-
raise ConnectionError, error.message
103+
raise ConnectionError.with_config(error.message, config)
102104
end
103105

104106
def measure_round_trip_delay
@@ -130,9 +132,9 @@ def connect
130132
loop do
131133
case status = socket.connect_nonblock(exception: false)
132134
when :wait_readable
133-
socket.to_io.wait_readable(@connect_timeout) or raise CannotConnectError
135+
socket.to_io.wait_readable(@connect_timeout) or raise CannotConnectError.with_config("", config)
134136
when :wait_writable
135-
socket.to_io.wait_writable(@connect_timeout) or raise CannotConnectError
137+
socket.to_io.wait_writable(@connect_timeout) or raise CannotConnectError.with_config("", config)
136138
when socket
137139
break
138140
else

test/redis_client/config_test.rb

+5-5
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,13 @@ def test_overriding
198198
end
199199

200200
def test_server_url
201-
assert_equal "redis://localhost:6379/0", Config.new.server_url
202-
assert_equal "redis://localhost:6379/0", Config.new(username: "george", password: "hunter2").server_url
201+
assert_equal "redis://localhost:6379", Config.new.server_url
202+
assert_equal "redis://localhost:6379", Config.new(username: "george", password: "hunter2").server_url
203203
assert_equal "redis://localhost:6379/5", Config.new(db: 5).server_url
204-
assert_equal "redis://example.com:8080/0", Config.new(host: "example.com", port: 8080).server_url
205-
assert_equal "rediss://localhost:6379/0", Config.new(ssl: true).server_url
204+
assert_equal "redis://example.com:8080", Config.new(host: "example.com", port: 8080).server_url
205+
assert_equal "rediss://localhost:6379", Config.new(ssl: true).server_url
206206

207-
assert_equal "/var/redis/redis.sock/5", Config.new(path: "/var/redis/redis.sock", db: 5).server_url
207+
assert_equal "unix:///var/redis/redis.sock?db=5", Config.new(path: "/var/redis/redis.sock", db: 5).server_url
208208
end
209209

210210
def test_custom_field

test/redis_client/connection_test.rb

+17-6
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,32 @@ def test_connected?
2626

2727
def test_connect_failure
2828
client = new_client(host: "example.com")
29-
assert_raises RedisClient::ConnectionError do
29+
error = assert_raises RedisClient::ConnectionError do
3030
client.call("PING")
3131
end
32+
33+
assert_match(%r{ \(rediss?://example.com:.*\)$}, error.message)
3234
end
3335

3436
def test_redis_down_after_connect
3537
@redis.call("PING") # force connect
3638
Toxiproxy[/redis/].down do
37-
assert_raises RedisClient::ConnectionError do
39+
error = assert_raises RedisClient::ConnectionError do
3840
@redis.call("PING")
3941
end
42+
43+
assert_match(%r{ \(rediss?://127.0.0.1:.*\)$}, error.message)
4044
end
4145
end
4246

4347
def test_redis_down_before_connect
4448
@redis.close
4549
Toxiproxy[/redis/].down do
46-
assert_raises RedisClient::ConnectionError do
50+
error = assert_raises RedisClient::ConnectionError do
4751
@redis.call("PING")
4852
end
53+
54+
assert_match(%r{ \(rediss?://127.0.0.1:.*\)$}, error.message)
4955
end
5056
end
5157

@@ -291,9 +297,11 @@ class TCPConnectionTest < Minitest::Test
291297

292298
def test_connecting_to_a_ssl_server
293299
client = new_client(**ssl_config, ssl: false)
294-
assert_raises CannotConnectError do
300+
error = assert_raises CannotConnectError do
295301
client.call("PING")
296302
end
303+
304+
assert_match(%r{ \(rediss?://.*:.*\)$}, error.message)
297305
end
298306

299307
def test_protocol_error
@@ -307,9 +315,11 @@ def test_protocol_error
307315
session.close
308316
end
309317

310-
assert_raises RedisClient::ProtocolError do
318+
error = assert_raises RedisClient::ProtocolError do
311319
new_client(host: "127.0.0.1", port: port).call("PING")
312320
end
321+
322+
assert_match(%r{ \(rediss?://127.0.0.1:#{port}\)$}, error.message)
313323
ensure
314324
server_thread&.kill
315325
end
@@ -372,10 +382,11 @@ def test_reconnect_on_readonly
372382

373383
client = new_client(host: "127.0.0.1", port: port)
374384
client.call("PING")
375-
assert_raises RedisClient::ReadOnlyError do
385+
error = assert_raises RedisClient::ReadOnlyError do
376386
client.call("SET", "foo", "bar")
377387
end
378388
refute_predicate client, :connected?
389+
assert_match(%r{ \(rediss?://127.0.0.1:#{port}\)$}, error.message)
379390
ensure
380391
server_thread&.kill
381392
end

test/redis_client_test.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ def test_encoding
4343

4444
def test_dns_resolution_failure
4545
client = RedisClient.new(host: "does-not-exist.example.com")
46-
assert_raises RedisClient::ConnectionError do
46+
error = assert_raises RedisClient::ConnectionError do
4747
client.call("PING")
4848
end
49+
50+
assert_match(%r{ \(redis://does-not-exist.example.com:.*\)$}, error.message)
4951
end
5052

5153
def test_older_server
@@ -64,6 +66,7 @@ def call_pipelined(commands, *)
6466
client.call("PING")
6567
end
6668
assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
69+
assert_includes error.message, "(redis://"
6770
end
6871

6972
def test_redis_6_server_with_missing_hello_command
@@ -82,6 +85,7 @@ def call_pipelined(commands, *)
8285
client.call("PING")
8386
end
8487
assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
88+
assert_includes error.message, "(redis://"
8589
end
8690

8791
def test_handle_async_raise
@@ -123,7 +127,7 @@ def test_measure_round_trip_delay
123127
end
124128

125129
def test_server_url
126-
assert_equal "redis://#{Servers::HOST}:#{Servers::REDIS_TCP_PORT}/0", @redis.server_url
130+
assert_equal "redis://#{Servers::HOST}:#{Servers::REDIS_TCP_PORT}", @redis.server_url
127131
end
128132

129133
def test_timeout

test/shared/redis_client_tests.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -271,30 +271,30 @@ 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
281281
assert_equal ["SISMEMBER", "str", "member"], error.command
282-
assert_includes error.message, "WRONGTYPE Operation against a key holding the wrong kind of value"
282+
assert_match(/WRONGTYPE Operation against a key holding the wrong kind of value (.*:.*)/, error.message)
283283

284284
error = assert_raises RedisClient::CommandError do
285285
@redis.multi do |transaction|
286286
transaction.call("SISMEMBER", "str", "member")
287287
end
288288
end
289289
assert_equal ["SISMEMBER", "str", "member"], error.command
290-
assert_includes error.message, "WRONGTYPE Operation against a key holding the wrong kind of value"
290+
assert_match(/WRONGTYPE Operation against a key holding the wrong kind of value (.*:.*)/, error.message)
291291
end
292292

293293
def test_command_missing
294294
error = assert_raises RedisClient::CommandError do
295295
@redis.call("DOESNOTEXIST", "foo")
296296
end
297-
assert_match(/ERR unknown command .DOESNOTEXIST./, error.message)
297+
assert_match(/ERR unknown command .DOESNOTEXIST.*\(.*:.*\)/, error.message)
298298
end
299299

300300
def test_authentication

0 commit comments

Comments
 (0)