honeycomb-beeline 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +72 -0
- data/Appraisals +8 -0
- data/Gemfile.lock +2 -2
- data/README.md +1 -0
- data/lib/honeycomb-beeline.rb +1 -0
- data/lib/honeycomb/beeline/version.rb +1 -1
- data/lib/honeycomb/integrations/redis.rb +354 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd03586fceabfcdb1d51f914e4a61cae2c87bbe295341036969a9fca40b3484
|
4
|
+
data.tar.gz: cc72f4183017b81d07ee227a4a5aa48c7e933b24f551b61328575fe5d3cb880b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93c1a6e138fb39c9a83591c3c10b7427ff28b4f397c2844f631e22d241976e4d3d4f26bb63bdf2380fb35bfe2ae016ff3e659611808608ffeba36c7e10dd6f6c
|
7
|
+
data.tar.gz: ba29c324e453cb42180134008cd8586dba0c1b34ad71a3efc2b4a9165ad380a116d2fd533a9c06cdd7ea2e2fc033a9cf09b9d98f8e68a6ebd242631dba1c4c61
|
data/.circleci/config.yml
CHANGED
@@ -97,6 +97,14 @@ gemfiles:
|
|
97
97
|
steps:
|
98
98
|
- ruby:
|
99
99
|
gemfile: gemfiles/rails_6.gemfile
|
100
|
+
redis-three: &redis-three
|
101
|
+
steps:
|
102
|
+
- ruby:
|
103
|
+
gemfile: gemfiles/redis_3.gemfile
|
104
|
+
redis-four: &redis-four
|
105
|
+
steps:
|
106
|
+
- ruby:
|
107
|
+
gemfile: gemfiles/redis_4.gemfile
|
100
108
|
|
101
109
|
jobs:
|
102
110
|
publish:
|
@@ -269,6 +277,30 @@ jobs:
|
|
269
277
|
rails-six-ruby-two-six:
|
270
278
|
<<: *rails-six
|
271
279
|
executor: ruby-two-six
|
280
|
+
redis-three-ruby-two-three:
|
281
|
+
<<: *redis-three
|
282
|
+
executor: ruby-two-three
|
283
|
+
redis-three-ruby-two-four:
|
284
|
+
<<: *redis-three
|
285
|
+
executor: ruby-two-four
|
286
|
+
redis-three-ruby-two-five:
|
287
|
+
<<: *redis-three
|
288
|
+
executor: ruby-two-five
|
289
|
+
redis-three-ruby-two-six:
|
290
|
+
<<: *redis-three
|
291
|
+
executor: ruby-two-six
|
292
|
+
redis-four-ruby-two-three:
|
293
|
+
<<: *redis-four
|
294
|
+
executor: ruby-two-three
|
295
|
+
redis-four-ruby-two-four:
|
296
|
+
<<: *redis-four
|
297
|
+
executor: ruby-two-four
|
298
|
+
redis-four-ruby-two-five:
|
299
|
+
<<: *redis-four
|
300
|
+
executor: ruby-two-five
|
301
|
+
redis-four-ruby-two-six:
|
302
|
+
<<: *redis-four
|
303
|
+
executor: ruby-two-six
|
272
304
|
|
273
305
|
workflows:
|
274
306
|
version: 2
|
@@ -463,6 +495,38 @@ workflows:
|
|
463
495
|
<<: *tag_filters
|
464
496
|
requires:
|
465
497
|
- lint
|
498
|
+
- redis-three-ruby-two-three:
|
499
|
+
<<: *tag_filters
|
500
|
+
requires:
|
501
|
+
- lint
|
502
|
+
- redis-three-ruby-two-four:
|
503
|
+
<<: *tag_filters
|
504
|
+
requires:
|
505
|
+
- lint
|
506
|
+
- redis-three-ruby-two-five:
|
507
|
+
<<: *tag_filters
|
508
|
+
requires:
|
509
|
+
- lint
|
510
|
+
- redis-three-ruby-two-six:
|
511
|
+
<<: *tag_filters
|
512
|
+
requires:
|
513
|
+
- lint
|
514
|
+
- redis-four-ruby-two-three:
|
515
|
+
<<: *tag_filters
|
516
|
+
requires:
|
517
|
+
- lint
|
518
|
+
- redis-four-ruby-two-four:
|
519
|
+
<<: *tag_filters
|
520
|
+
requires:
|
521
|
+
- lint
|
522
|
+
- redis-four-ruby-two-five:
|
523
|
+
<<: *tag_filters
|
524
|
+
requires:
|
525
|
+
- lint
|
526
|
+
- redis-four-ruby-two-six:
|
527
|
+
<<: *tag_filters
|
528
|
+
requires:
|
529
|
+
- lint
|
466
530
|
- publish:
|
467
531
|
filters:
|
468
532
|
tags:
|
@@ -518,3 +582,11 @@ workflows:
|
|
518
582
|
- rails-five-two-ruby-two-six
|
519
583
|
- rails-six-ruby-two-five
|
520
584
|
- rails-six-ruby-two-six
|
585
|
+
- redis-three-ruby-two-three
|
586
|
+
- redis-three-ruby-two-four
|
587
|
+
- redis-three-ruby-two-five
|
588
|
+
- redis-three-ruby-two-six
|
589
|
+
- redis-four-ruby-two-three
|
590
|
+
- redis-four-ruby-two-four
|
591
|
+
- redis-four-ruby-two-five
|
592
|
+
- redis-four-ruby-two-six
|
data/Appraisals
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
honeycomb-beeline (1.
|
4
|
+
honeycomb-beeline (1.3.0)
|
5
5
|
libhoney (~> 1.8)
|
6
6
|
|
7
7
|
GEM
|
@@ -44,7 +44,7 @@ GEM
|
|
44
44
|
iniparse (1.4.4)
|
45
45
|
jaro_winkler (1.5.3)
|
46
46
|
json (2.2.0)
|
47
|
-
libhoney (1.14.
|
47
|
+
libhoney (1.14.1)
|
48
48
|
addressable (~> 2.0)
|
49
49
|
http (>= 2.0, < 5.0)
|
50
50
|
method_source (0.9.2)
|
data/README.md
CHANGED
data/lib/honeycomb-beeline.rb
CHANGED
@@ -0,0 +1,354 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redis"
|
4
|
+
|
5
|
+
module Honeycomb
|
6
|
+
module Redis
|
7
|
+
# Patches Redis with the option to configure the Honeycomb client.
|
8
|
+
#
|
9
|
+
# When you load this integration, each Redis call will be wrapped in a span
|
10
|
+
# containing information about the command being invoked.
|
11
|
+
#
|
12
|
+
# This module automatically gets mixed into the Redis class so you can
|
13
|
+
# change the underlying {Honeycomb::Client}. By default, we use the global
|
14
|
+
# {Honeycomb.client} to send events. A nil client will disable the
|
15
|
+
# integration altogether.
|
16
|
+
#
|
17
|
+
# @example Custom client
|
18
|
+
# Redis.honeycomb_client = Honeycomb::Client.new(...)
|
19
|
+
#
|
20
|
+
# @example Disabling instrumentation
|
21
|
+
# Redis.honeycomb_client = nil
|
22
|
+
module Configuration
|
23
|
+
attr_writer :honeycomb_client
|
24
|
+
|
25
|
+
def honeycomb_client
|
26
|
+
@honeycomb_client || Honeycomb.client
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Patches Redis::Client with Honeycomb instrumentation.
|
31
|
+
#
|
32
|
+
# Circa versions 3.x and 4.x of their gem, the Redis class is backed by an
|
33
|
+
# underlying Redis::Client object. The methods used to send commands to the
|
34
|
+
# Redis server - namely Redis::Client#call, Redis::Client#call_loop,
|
35
|
+
# Redis::Client#call_pipeline, Redis::Client#call_pipelined,
|
36
|
+
# Redis::Client#call_with_timeout, and Redis::Client#call_without_timeout -
|
37
|
+
# all eventually wind up calling the Redis::Client#process method to do the
|
38
|
+
# "dirty work" of writing commands out to an underlying connection. So this
|
39
|
+
# gives us a single point of entry that's ideal for introducing the
|
40
|
+
# Honeycomb span.
|
41
|
+
#
|
42
|
+
# An alternative interface provided since at least version 3.0.0 is
|
43
|
+
# Redis::Distributed. Underneath, though, it maintains a collection of
|
44
|
+
# Redis objects, and each call is forwarded to one or more members of the
|
45
|
+
# collection. So patching Redis::Client still captures spans originating
|
46
|
+
# from Redis::Distributed. Typical commands (i.e., ones that aren't
|
47
|
+
# "global" like `QUIT` or `FLUSHALL`) forward to just a single node anyway,
|
48
|
+
# so there's not much use to wrapping everything up in a span for the
|
49
|
+
# Redis::Distributed method call.
|
50
|
+
#
|
51
|
+
# Another alternative interface provided since v4.0.3 is Redis::Cluster,
|
52
|
+
# which you can configure the Redis class to use instead of Redis::Client.
|
53
|
+
# Again, though, Redis::Cluster maintains a collection of Redis::Client
|
54
|
+
# instances underneath. The tracing needs wind up being pretty much the
|
55
|
+
# same as Redis::Distributed, even though the actual architecture is
|
56
|
+
# significantly different.
|
57
|
+
#
|
58
|
+
# An implementation detail of pub/sub commands since v2.0.0 (well below our
|
59
|
+
# supported version of the redis gem!) is Redis::SubscribedClient, but that
|
60
|
+
# still wraps an underlying Redis::Client or Redis::Cluster instance.
|
61
|
+
#
|
62
|
+
# @see https://github.com/redis/redis-rb/blob/2e8577ad71d0efc32f31fb034f341e1eb10abc18/lib/redis/client.rb#L77-L180
|
63
|
+
# Relevant Redis::Client methods circa v3.0.0
|
64
|
+
# @see https://github.com/redis/redis-rb/blob/a2c562c002bc8f86d1f47818d63db2da1c5c3d3f/lib/redis/client.rb#L124-L239
|
65
|
+
# Relevant Redis::Client methods circa v4.1.3
|
66
|
+
# @see https://github.com/redis/redis-rb/commits/master/lib/redis/client.rb
|
67
|
+
# History of Redis::Client
|
68
|
+
#
|
69
|
+
# @see https://redis.io/topics/partitioning
|
70
|
+
# Partitioning (the basis for Redis::Distributed)
|
71
|
+
# @see https://github.com/redis/redis-rb/blob/2e8577ad71d0efc32f31fb034f341e1eb10abc18/lib/redis/distributed.rb
|
72
|
+
# Redis::Distributed circa v3.0.0
|
73
|
+
# @see https://github.com/redis/redis-rb/blob/a2c562c002bc8f86d1f47818d63db2da1c5c3d3f/lib/redis/distributed.rb
|
74
|
+
# Redis::Distributed circa v4.1.3
|
75
|
+
# @see https://github.com/redis/redis-rb/commits/master/lib/redis/distributed.rb
|
76
|
+
# History of Redis::Distributed
|
77
|
+
#
|
78
|
+
# @see https://redis.io/topics/cluster-spec
|
79
|
+
# Clustering (the basis for Redis::Cluster)
|
80
|
+
# @see https://github.com/redis/redis-rb/commit/7f48c0b02fa89256167bc481a73ce2e0c8cca89a
|
81
|
+
# Initial implementation of Redis::Cluster released in v4.0.3
|
82
|
+
# @see https://github.com/redis/redis-rb/blob/a2c562c002bc8f86d1f47818d63db2da1c5c3d3f/lib/redis/cluster.rb
|
83
|
+
# Redis::Cluster circa v4.1.3
|
84
|
+
# @see https://github.com/redis/redis-rb/commits/master/lib/redis/cluster.rb
|
85
|
+
# History of Redis::Cluster
|
86
|
+
#
|
87
|
+
# @see https://redis.io/topics/pubsub
|
88
|
+
# Pub/Sub in Redis
|
89
|
+
# @see https://github.com/redis/redis-rb/blob/17d40d80388b536ec53a8f19bb1404e93a61650f/lib/redis/subscribe.rb
|
90
|
+
# Redis::SubscribedClient circa v2.0.0
|
91
|
+
# @see https://github.com/redis/redis-rb/blob/2e8577ad71d0efc32f31fb034f341e1eb10abc18/lib/redis/subscribe.rb
|
92
|
+
# Redis::SubscribedClient circa v3.0.0
|
93
|
+
# @see https://github.com/redis/redis-rb/blob/a2c562c002bc8f86d1f47818d63db2da1c5c3d3f/lib/redis/subscribe.rb
|
94
|
+
# Redis::SubscribedClient circa v4.1.3
|
95
|
+
module Client
|
96
|
+
def process(commands)
|
97
|
+
return super if ::Redis.honeycomb_client.nil?
|
98
|
+
|
99
|
+
span = ::Redis.honeycomb_client.start_span(name: "redis")
|
100
|
+
begin
|
101
|
+
fields = Fields.new(self)
|
102
|
+
fields.options = @options
|
103
|
+
fields.command = commands
|
104
|
+
span.add fields
|
105
|
+
super
|
106
|
+
rescue StandardError => e
|
107
|
+
span.add_field "redis.error", e.class.name
|
108
|
+
span.add_field "redis.error_detail", e.message
|
109
|
+
raise
|
110
|
+
ensure
|
111
|
+
span.send
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# This structure contains the fields we'll add to each Redis span.
|
117
|
+
#
|
118
|
+
# The logic is in this class to avoid monkey-patching extraneous APIs into
|
119
|
+
# the Redis::Client via {Client}.
|
120
|
+
#
|
121
|
+
# @private
|
122
|
+
class Fields
|
123
|
+
def initialize(client)
|
124
|
+
@client = client
|
125
|
+
end
|
126
|
+
|
127
|
+
def options=(options)
|
128
|
+
options.each do |option, value|
|
129
|
+
values["redis.#{option}"] ||= value unless ignore?(option)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def command=(commands)
|
134
|
+
commands = Array(commands)
|
135
|
+
values["redis.command"] = commands.map { |cmd| format(cmd) }.join("\n")
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_hash
|
139
|
+
values
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def values
|
145
|
+
@values ||= {
|
146
|
+
"meta.package" => "redis",
|
147
|
+
"meta.package_version" => ::Redis::VERSION,
|
148
|
+
"redis.id" => @client.id,
|
149
|
+
"redis.location" => @client.location,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
# Do we ignore this Redis::Client option?
|
154
|
+
#
|
155
|
+
# * :url - unsafe because it might contain a password
|
156
|
+
# * :password - unsafe
|
157
|
+
# * :logger - just some Ruby object, not useful
|
158
|
+
# * :_parsed - implementation detail
|
159
|
+
def ignore?(option)
|
160
|
+
%i[url password logger _parsed].include?(option)
|
161
|
+
end
|
162
|
+
|
163
|
+
def format(cmd)
|
164
|
+
name, *args = cmd.flatten(1)
|
165
|
+
name = resolve(name)
|
166
|
+
sanitize(args) if name.casecmp("auth").zero?
|
167
|
+
[name.upcase, *args.map { |arg| prettify(arg) }].join(" ")
|
168
|
+
end
|
169
|
+
|
170
|
+
def resolve(name)
|
171
|
+
@client.command_map.fetch(name, name).to_s
|
172
|
+
end
|
173
|
+
|
174
|
+
def sanitize(args)
|
175
|
+
args.map! { "[sanitized]" }
|
176
|
+
end
|
177
|
+
|
178
|
+
def prettify(arg)
|
179
|
+
quotes = false
|
180
|
+
pretty = "".dup
|
181
|
+
arg.to_s.each_char do |c|
|
182
|
+
quotes ||= needs_quotes?(c)
|
183
|
+
pretty << escape(c)
|
184
|
+
end
|
185
|
+
quotes ? "\"#{pretty}\"" : pretty
|
186
|
+
end
|
187
|
+
|
188
|
+
# This aims to replicate the algorithms used by redis-cli.
|
189
|
+
#
|
190
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L940-L1067
|
191
|
+
# The redis-cli parsing algorithm
|
192
|
+
#
|
193
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L878-L907
|
194
|
+
# The redis-cli printing algorithm
|
195
|
+
def escape(char)
|
196
|
+
return escape_with_backslash(char) if escape_with_backslash?(char)
|
197
|
+
return escape_with_hex_codes(char) if escape_with_hex_codes?(char)
|
198
|
+
|
199
|
+
char
|
200
|
+
end
|
201
|
+
|
202
|
+
# A lookup table for backslash-escaped characters.
|
203
|
+
#
|
204
|
+
# This is used by {#escape_with_backslash?} and {#escape_with_backslash}
|
205
|
+
# to replicate the hard-coded `case` statements in redis-cli. As of this
|
206
|
+
# writing, Redis recognizes a handful of standard C escape sequences,
|
207
|
+
# like "\n" for newlines.
|
208
|
+
#
|
209
|
+
# Because {#prettify} will output double quoted strings if any escaping
|
210
|
+
# is needed, this table must additionally consider the double-quote to be
|
211
|
+
# a backslash-escaped character. For example, instead of generating
|
212
|
+
#
|
213
|
+
# '"hello"'
|
214
|
+
#
|
215
|
+
# we'll generate
|
216
|
+
#
|
217
|
+
# "\"hello\""
|
218
|
+
#
|
219
|
+
# even though redis-cli would technically recognize the single-quoted
|
220
|
+
# version.
|
221
|
+
#
|
222
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L888-L896
|
223
|
+
# The redis-cli algorithm for outputting standard escape sequences
|
224
|
+
BACKSLASHES = {
|
225
|
+
"\\" => "\\\\",
|
226
|
+
'"' => '\\"',
|
227
|
+
"\n" => "\\n",
|
228
|
+
"\r" => "\\r",
|
229
|
+
"\t" => "\\t",
|
230
|
+
"\a" => "\\a",
|
231
|
+
"\b" => "\\b",
|
232
|
+
}.freeze
|
233
|
+
|
234
|
+
def escape_with_backslash?(char)
|
235
|
+
BACKSLASHES.key?(char)
|
236
|
+
end
|
237
|
+
|
238
|
+
def escape_with_backslash(char)
|
239
|
+
BACKSLASHES.fetch(char, char)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Do we need to hex-encode this character?
|
243
|
+
#
|
244
|
+
# This replicates the C isprint() function that redis-cli uses to decide
|
245
|
+
# whether to escape a character in hexadecimal notation, "\xhh". Any
|
246
|
+
# non-printable character must be represented as a hex escape sequence.
|
247
|
+
#
|
248
|
+
# Normally, we could match this using a negated POSIX bracket expression:
|
249
|
+
#
|
250
|
+
# /[^[:print:]]/
|
251
|
+
#
|
252
|
+
# You can read that as "not printable".
|
253
|
+
#
|
254
|
+
# However, in Ruby, these character classes also encompass non-ASCII
|
255
|
+
# characters. In contrast, since most platforms have 8-bit `char` types,
|
256
|
+
# the C isprint() function generally does not recognize any Unicode code
|
257
|
+
# points. This effectively limits the redis-cli interpretation of the
|
258
|
+
# printable character range to just printable ASCII characters.
|
259
|
+
#
|
260
|
+
# Thus, we match using a combination of the previous regexp with a
|
261
|
+
# non-POSIX character class that Ruby defines:
|
262
|
+
#
|
263
|
+
# /[^[:print:]&&[:ascii:]]/
|
264
|
+
#
|
265
|
+
# You can read this like
|
266
|
+
#
|
267
|
+
# NOT (printable AND ascii)
|
268
|
+
#
|
269
|
+
# which by DeMorgan's Law is equivalent to
|
270
|
+
#
|
271
|
+
# (NOT printable) OR (NOT ascii)
|
272
|
+
#
|
273
|
+
# That is, if the character is not printable (even in Unicode), we'll
|
274
|
+
# escape it; if the character is printable but non-ASCII, we'll also
|
275
|
+
# escape it.
|
276
|
+
#
|
277
|
+
# What's more, Ruby's Regexp#=~ method will blow up if the string does
|
278
|
+
# not have a valid encoding (e.g., in UTF-8). In this case, though,
|
279
|
+
# {#escape_with_hex_codes} can still convert the bytes that make up the
|
280
|
+
# invalid character into a hex code. So we preemptively check for
|
281
|
+
# invalidly-encoded characters before testing the above match.
|
282
|
+
#
|
283
|
+
# @see https://ruby-doc.org/core-2.6.5/Regexp.html
|
284
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L878-L880
|
285
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L898-L901
|
286
|
+
# @see https://www.justinweiss.com/articles/3-steps-to-fix-encoding-problems-in-ruby/
|
287
|
+
def escape_with_hex_codes?(char)
|
288
|
+
!char.valid_encoding? || char =~ /[^[:print:]&&[:ascii:]]/
|
289
|
+
end
|
290
|
+
|
291
|
+
# Hex-encodes a (presumably non-printable or non-ASCII) character.
|
292
|
+
#
|
293
|
+
# Aside from standard backslash escape sequences, redis-cli also
|
294
|
+
# recognizes "\xhh" notation, where `hh` is a hexadecimal number.
|
295
|
+
#
|
296
|
+
# Of note is that redis-cli only recognizes *exactly* two-digit
|
297
|
+
# hexadecimal numbers. This is in accordance with IEEE Std 1003.1-2001,
|
298
|
+
# Chapter 7, Locale:
|
299
|
+
#
|
300
|
+
# > A character can be represented as a hexadecimal constant. A
|
301
|
+
# > hexadecimal constant shall be specified as the escape character
|
302
|
+
# > followed by an 'x' followed by two hexadecimal digits. Each constant
|
303
|
+
# > shall represent a byte value. Multi-byte values can be represented by
|
304
|
+
# > concatenated constants specified in byte order with the last constant
|
305
|
+
# > specifying the least significant byte of the character.
|
306
|
+
#
|
307
|
+
# Unlike the C `char` type, Ruby's conception of a character can span
|
308
|
+
# multiple bytes (and possibly bytes that aren't valid in Ruby's string
|
309
|
+
# encoding). So we take care to escape the input properly into the
|
310
|
+
# redis-cli compatible version by iterating through each byte and
|
311
|
+
# formatting it as a (zero-padded) 2-digit hexadecimal number prefixed by
|
312
|
+
# `\x`.
|
313
|
+
#
|
314
|
+
# @see https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html
|
315
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L878-L880
|
316
|
+
# @see https://github.com/antirez/redis/blob/0f026af185e918a9773148f6ceaa1b084662be88/src/sds.c#L898-L901
|
317
|
+
def escape_with_hex_codes(char)
|
318
|
+
char.bytes.map { |b| Kernel.format("\\x%02x", b) }.join
|
319
|
+
end
|
320
|
+
|
321
|
+
def escape?(char)
|
322
|
+
escape_with_backslash?(char) || escape_with_hex_codes?(char)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Should this character cause {#prettify} to wrap its output in quotes?
|
326
|
+
#
|
327
|
+
# The overall string returned by {#prettify} should only be quoted if at
|
328
|
+
# least one of the following holds:
|
329
|
+
#
|
330
|
+
# 1. The string contains a character that needs to be escaped. This
|
331
|
+
# includes standard backslash escape sequences (like "\n" and "\t") as
|
332
|
+
# well as hex-encoded bytes using the "\x" escape sequence. Since
|
333
|
+
# {#prettify} uses double quotes on its output string, we must also
|
334
|
+
# force quotes if the string itself contains a literal double quote.
|
335
|
+
# This double quote behavior is handled tacitly by {BACKSLASHES}.
|
336
|
+
#
|
337
|
+
# 2. The string contains a single quote. Since redis-cli recognizes
|
338
|
+
# single-quoted strings, we want to wrap the {#prettify} output in
|
339
|
+
# double quotes so that the literal single quote character isn't
|
340
|
+
# mistaken as the delimiter of a new string.
|
341
|
+
#
|
342
|
+
# 3. The string contains any whitespace characters. If the {#prettify}
|
343
|
+
# output weren't wrapped in quotes, whitespace would act as a
|
344
|
+
# separator between arguments to the Redis command. To group things
|
345
|
+
# together, we need to quote the string.
|
346
|
+
def needs_quotes?(char)
|
347
|
+
escape?(char) || char == "'" || char =~ /\s/
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
Redis.extend(Honeycomb::Redis::Configuration)
|
354
|
+
Redis::Client.prepend(Honeycomb::Redis::Client)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeycomb-beeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Holman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-11-
|
11
|
+
date: 2019-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: libhoney
|
@@ -233,6 +233,7 @@ files:
|
|
233
233
|
- lib/honeycomb/integrations/rails.rb
|
234
234
|
- lib/honeycomb/integrations/railtie.rb
|
235
235
|
- lib/honeycomb/integrations/rake.rb
|
236
|
+
- lib/honeycomb/integrations/redis.rb
|
236
237
|
- lib/honeycomb/integrations/sequel.rb
|
237
238
|
- lib/honeycomb/integrations/sinatra.rb
|
238
239
|
- lib/honeycomb/integrations/warden.rb
|