oboe 2.4.0.1 → 2.5.0.7
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/.travis.yml +2 -0
- data/{CHANGELOG → CHANGELOG.md} +9 -0
- data/Gemfile +2 -1
- data/README.md +20 -5
- data/lib/base.rb +5 -5
- data/lib/joboe_metal.rb +5 -1
- data/lib/oboe.rb +7 -4
- data/lib/oboe/api/layerinit.rb +24 -1
- data/lib/oboe/config.rb +5 -1
- data/lib/oboe/frameworks/rails.rb +2 -0
- data/lib/oboe/inst/rack.rb +1 -1
- data/lib/oboe/inst/redis.rb +273 -0
- data/lib/oboe/version.rb +2 -2
- data/lib/oboe/xtrace.rb +4 -7
- data/lib/oboe_metal.rb +5 -1
- data/lib/rails/generators/oboe/templates/oboe_initializer.rb +2 -0
- data/test/frameworks/apps/padrino_simple.rb +37 -0
- data/test/frameworks/test_padrino.rb +32 -0
- data/test/instrumentation/redis_hashes_test.rb +265 -0
- data/test/instrumentation/redis_keys_test.rb +318 -0
- data/test/instrumentation/redis_lists_test.rb +310 -0
- data/test/instrumentation/redis_misc_test.rb +160 -0
- data/test/instrumentation/redis_sets_test.rb +293 -0
- data/test/instrumentation/redis_sortedsets_test.rb +325 -0
- data/test/instrumentation/redis_strings_test.rb +333 -0
- data/test/minitest_helper.rb +1 -2
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 812e394b380905601c8d1fbae241aaedce6231b4
|
4
|
+
data.tar.gz: 317bb57275d9eb6b23054d9d2e51dd0c8fbd64d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af3e85508ec28a8a56e05ebed1f1ee6a90c8475ca258c9b9127ee81c8d33226331f4604f856426cb1aca24b90592f7d0ec22e2c6da0df4d47e3e30a0e96f4294
|
7
|
+
data.tar.gz: a8744c90e5d216672d774b094deadf1d1ccf031a25de3f262984c10d83593fb5908fdb05527671ee7a53f99b5719c9238f65cea6965c197861815cf482970d86
|
data/.travis.yml
CHANGED
data/{CHANGELOG → CHANGELOG.md}
RENAMED
@@ -1,3 +1,12 @@
|
|
1
|
+
# oboe 2.5.0.7 (02/13/14)
|
2
|
+
|
3
|
+
* Added new Redis redis-rb gem (>= 3.0.0) instrumentation
|
4
|
+
* Fix a SampleSource bitmask high bit issue
|
5
|
+
* Expanded __Init reports
|
6
|
+
* Fix Ruby standalone returning nil X-Trace headers (1B000000...)
|
7
|
+
* Test against Ruby 2.1.0 on TravisCI
|
8
|
+
* Fix errant Oboe::Config warning
|
9
|
+
|
1
10
|
# oboe 2.4.0.1 (01/12/13)
|
2
11
|
|
3
12
|
* Report SampleRate & SampleSource per updated SWIG API
|
data/Gemfile
CHANGED
@@ -15,7 +15,7 @@ end
|
|
15
15
|
group :development do
|
16
16
|
gem 'ruby-debug', :platform => :mri_18
|
17
17
|
gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
|
18
|
-
gem '
|
18
|
+
gem 'byebug', :platform => :mri_20
|
19
19
|
gem 'perftools.rb', :platform => :mri, :require => 'perftools'
|
20
20
|
gem 'pry'
|
21
21
|
end
|
@@ -29,4 +29,5 @@ gem 'mongo'
|
|
29
29
|
gem 'bson_ext' # For Mongo, Yours Truly
|
30
30
|
gem 'moped' unless (RUBY_VERSION =~ /^1.8/) == 0
|
31
31
|
gem 'resque'
|
32
|
+
gem 'redis'
|
32
33
|
|
data/README.md
CHANGED
@@ -76,9 +76,16 @@ The oboe gem has the ability to instrument any arbitrary Ruby application or scr
|
|
76
76
|
Bundler.require
|
77
77
|
|
78
78
|
require 'oboe'
|
79
|
+
|
80
|
+
# Tracing mode can be 'never', 'through' (to follow upstream) or 'always'
|
81
|
+
Oboe::Config[:tracing_mode] = 'always'
|
82
|
+
|
83
|
+
# Number of requests to trace out of each million
|
84
|
+
Oboe::Config[:sample_rate] = 1000000
|
85
|
+
|
79
86
|
Oboe::Ruby.initialize
|
80
87
|
|
81
|
-
From here, you can use the Tracing API to instrument areas of code (see below).
|
88
|
+
From here, you can use the Tracing API to instrument areas of code using `Oboe::API.start_trace` (see below). If you prefer to instead dive directly into code, take a look at [this example](https://gist.github.com/pglombardo/8550713) of an instrumented Ruby script.
|
82
89
|
|
83
90
|
## Other
|
84
91
|
|
@@ -88,13 +95,15 @@ You can send deploy notifications to TraceView and have the events show up on yo
|
|
88
95
|
|
89
96
|
## The Tracing API
|
90
97
|
|
91
|
-
You can instrument any arbitrary block of code using
|
98
|
+
You can instrument any arbitrary block of code using `Oboe::API.trace`:
|
92
99
|
|
93
100
|
# layer_name will show up in the TraceView app dashboard
|
94
101
|
layer_name = 'subsystemX'
|
95
102
|
|
96
|
-
# report_kvs are a set of information Key/Value pairs that are sent to
|
97
|
-
#
|
103
|
+
# report_kvs are a set of information Key/Value pairs that are sent to
|
104
|
+
# TraceView dashboard along with the performance metrics. These KV
|
105
|
+
# pairs are used to report request, environment and/or client specific
|
106
|
+
# information.
|
98
107
|
|
99
108
|
report_kvs = {}
|
100
109
|
report_kvs[:mykey] = @client.id
|
@@ -103,7 +112,13 @@ You can instrument any arbitrary block of code using the following pattern:
|
|
103
112
|
# the block of code to be traced
|
104
113
|
end
|
105
114
|
|
106
|
-
|
115
|
+
`Oboe::API.trace` is used within the context of a request. It will follow the upstream state of the request being traced. i.e. the block of code will only be traced when the parent request is being traced.
|
116
|
+
|
117
|
+
This tracing state of a request can also be queried by using `Oboe.tracing?`.
|
118
|
+
|
119
|
+
If you need to instrument code outside the context of a request (such as a cron job, background job or an arbitrary ruby script), use `Oboe::API.start_trace` instead which will initiate new traces based on configuration and probability (based on the sample rate).
|
120
|
+
|
121
|
+
Find more details in the [RubyDoc page](http://rdoc.info/gems/oboe/Oboe/API/Tracing) or in [this example](https://gist.github.com/pglombardo/8550713) on how to use the Tracing API in an independent Ruby script.
|
107
122
|
|
108
123
|
## Tracing Methods
|
109
124
|
|
data/lib/base.rb
CHANGED
@@ -14,13 +14,13 @@ OBOE_SAMPLE_RATE_SOURCE_DEFAULT_MISCONFIGURED = 5
|
|
14
14
|
OBOE_SAMPLE_RATE_SOURCE_OBOE_DEFAULT = 6
|
15
15
|
|
16
16
|
# Masks for bitwise ops
|
17
|
-
ZERO_MASK =
|
17
|
+
ZERO_MASK = 0b0000000000000000000000000000
|
18
18
|
|
19
|
-
SAMPLE_RATE_MASK =
|
20
|
-
SAMPLE_SOURCE_MASK =
|
19
|
+
SAMPLE_RATE_MASK = 0b0000111111111111111111111111
|
20
|
+
SAMPLE_SOURCE_MASK = 0b1111000000000000000000000000
|
21
21
|
|
22
|
-
ZERO_SAMPLE_RATE_MASK =
|
23
|
-
ZERO_SAMPLE_SOURCE_MASK =
|
22
|
+
ZERO_SAMPLE_RATE_MASK = 0b1111000000000000000000000000
|
23
|
+
ZERO_SAMPLE_SOURCE_MASK = 0b0000111111111111111111111111
|
24
24
|
|
25
25
|
module OboeBase
|
26
26
|
attr_accessor :reporter
|
data/lib/joboe_metal.rb
CHANGED
@@ -81,7 +81,11 @@ module Oboe_metal < MetalBase
|
|
81
81
|
Oboe.reporter = Oboe::UdpReporter.new(Oboe::Config[:reporter_host])
|
82
82
|
end
|
83
83
|
|
84
|
-
|
84
|
+
# Only report __Init from here if we are not instrumenting a framework.
|
85
|
+
# Otherwise, frameworks will handle reporting __Init after full initialization
|
86
|
+
unless defined?(::Rails) or defined?(::Sinatra) or defined?(::Padrino)
|
87
|
+
Oboe::API.report_init('rack') unless ["development", "test"].include? ENV['RACK_ENV']
|
88
|
+
end
|
85
89
|
|
86
90
|
rescue Exception => e
|
87
91
|
$stderr.puts e.message
|
data/lib/oboe.rb
CHANGED
@@ -21,10 +21,13 @@ begin
|
|
21
21
|
end
|
22
22
|
rescue LoadError
|
23
23
|
Oboe.loaded = false
|
24
|
-
|
25
|
-
$
|
26
|
-
|
27
|
-
|
24
|
+
|
25
|
+
unless $ENV['RAILS_GROUP'] == 'assets'
|
26
|
+
$stderr.puts "=============================================================="
|
27
|
+
$stderr.puts "Missing TraceView libraries. Tracing disabled."
|
28
|
+
$stderr.puts "See: https://support.tv.appneta.com/solution/articles/137973"
|
29
|
+
$stderr.puts "=============================================================="
|
30
|
+
end
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
data/lib/oboe/api/layerinit.rb
CHANGED
@@ -28,6 +28,19 @@ module Oboe
|
|
28
28
|
elsif defined?(::Sinatra)
|
29
29
|
platform_info['Ruby.Sinatra.Version'] = "Sinatra-#{::Sinatra::VERSION}"
|
30
30
|
end
|
31
|
+
|
32
|
+
# Report the instrumented libraries
|
33
|
+
platform_info['Ruby.Cassandra.Version'] = "Cassandra-#{::Cassandra.VERSION}" if defined?(::Cassandra)
|
34
|
+
platform_info['Ruby.Dalli.Version'] = "Dalli-#{::Dalli::VERSION}" if defined?(::Dalli)
|
35
|
+
platform_info['Ruby.MemCache.Version'] = "MemCache-#{::MemCache::VERSION}" if defined?(::MemCache)
|
36
|
+
platform_info['Ruby.Moped.Version'] = "Moped-#{::Moped::VERSION}" if defined?(::Moped)
|
37
|
+
platform_info['Ruby.Redis.Version'] = "Redis-#{::Redis::VERSION}" if defined?(::Redis)
|
38
|
+
platform_info['Ruby.Resque.Version'] = "Resque-#{::Resque::VERSION}" if defined?(::Resque)
|
39
|
+
|
40
|
+
# Special case since the Mongo 1.x driver doesn't embed the version number in the gem directly
|
41
|
+
if ::Gem.loaded_specs.has_key?('mongo')
|
42
|
+
platform_info['Ruby.Mongo.Version'] = "Mongo-#{::Gem.loaded_specs['mongo'].version.to_s}"
|
43
|
+
end
|
31
44
|
|
32
45
|
# Report the server in use (if possible)
|
33
46
|
if defined?(::Unicorn)
|
@@ -44,10 +57,20 @@ module Oboe
|
|
44
57
|
platform_info['Ruby.AppContainer.Version'] = "Mongrel2-#{::Mongrel2::VERSION}"
|
45
58
|
elsif defined?(::Trinidad)
|
46
59
|
platform_info['Ruby.AppContainer.Version'] = "Trinidad-#{::Trinidad::VERSION}"
|
60
|
+
elsif defined?(::WEBrick)
|
61
|
+
platform_info['Ruby.AppContainer.Version'] = "WEBrick-#{::WEBrick::VERSION}"
|
47
62
|
else
|
48
63
|
platform_info['Ruby.AppContainer.Version'] = "Unknown"
|
49
64
|
end
|
50
|
-
|
65
|
+
|
66
|
+
# If we couldn't load the c extension correctly, report the error to the dashboard.
|
67
|
+
unless Oboe.loaded
|
68
|
+
platform_info['Error'] = "Missing TraceView libraries. Tracing disabled."
|
69
|
+
end
|
70
|
+
|
71
|
+
rescue StandardError => e
|
72
|
+
Oboe.logger.debug "Error in layerinit: #{e.message}"
|
73
|
+
Oboe.logger.debug e.backtrace
|
51
74
|
end
|
52
75
|
|
53
76
|
start_trace(layer, nil, platform_info) { }
|
data/lib/oboe/config.rb
CHANGED
@@ -12,7 +12,7 @@ module Oboe
|
|
12
12
|
@@config = {}
|
13
13
|
|
14
14
|
@@instrumentation = [ :cassandra, :dalli, :nethttp, :memcached, :memcache, :mongo,
|
15
|
-
:moped, :rack, :resque, :action_controller, :action_view,
|
15
|
+
:moped, :rack, :redis, :resque, :action_controller, :action_view,
|
16
16
|
:active_record ]
|
17
17
|
|
18
18
|
##
|
@@ -42,6 +42,7 @@ module Oboe
|
|
42
42
|
Oboe::Config[:mongo][:collect_backtraces] = true
|
43
43
|
Oboe::Config[:moped][:collect_backtraces] = true
|
44
44
|
Oboe::Config[:nethttp][:collect_backtraces] = true
|
45
|
+
Oboe::Config[:redis][:collect_backtraces] = false
|
45
46
|
Oboe::Config[:resque][:collect_backtraces] = true
|
46
47
|
|
47
48
|
# Special instrument specific flags
|
@@ -56,6 +57,9 @@ module Oboe
|
|
56
57
|
# Setup an empty host blacklist (see: Oboe::API::Util.blacklisted?)
|
57
58
|
@@config[:blacklist] = []
|
58
59
|
|
60
|
+
# Access Key is empty until loaded from config file or env var
|
61
|
+
@@config[:access_key] = ""
|
62
|
+
|
59
63
|
# The oboe Ruby client has the ability to sanitize query literals
|
60
64
|
# from SQL statements. By default this is disabled. Enable to
|
61
65
|
# avoid collecting and reporting query literals to TraceView.
|
@@ -110,6 +110,7 @@ if defined?(::Rails)
|
|
110
110
|
Oboe::Loading.load_access_key
|
111
111
|
Oboe::Inst.load_instrumentation
|
112
112
|
Oboe::Rails.load_instrumentation
|
113
|
+
Oboe::API.report_init('rack') unless ["development", "test"].include? ENV['RACK_ENV']
|
113
114
|
end
|
114
115
|
end
|
115
116
|
end
|
@@ -125,6 +126,7 @@ if defined?(::Rails)
|
|
125
126
|
Oboe::Inst.load_instrumentation
|
126
127
|
Oboe::Rails.load_instrumentation
|
127
128
|
Oboe::Rails.include_helpers
|
129
|
+
Oboe::API.report_init('rack') unless ["development", "test"].include? ENV['RACK_ENV']
|
128
130
|
end
|
129
131
|
end
|
130
132
|
end
|
data/lib/oboe/inst/rack.rb
CHANGED
@@ -0,0 +1,273 @@
|
|
1
|
+
# Copyright (c) 2014 AppNeta, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
module Oboe
|
5
|
+
module Inst
|
6
|
+
module Redis
|
7
|
+
module Client
|
8
|
+
# The operations listed in this constant skip collecting KVKey
|
9
|
+
NO_KEY_OPS = [ :keys, :randomkey, :scan, :sdiff, :sdiffstore, :sinter,
|
10
|
+
:sinterstore, :smove, :sunion, :sunionstore, :zinterstore,
|
11
|
+
:zunionstore, :publish, :select, :eval, :evalsha, :script ]
|
12
|
+
|
13
|
+
# Instead of a giant switch statement, we use a hash constant to map out what
|
14
|
+
# KVs need to be collected for each of the many many Redis operations
|
15
|
+
# Hash formatting by undiagnosed OCD
|
16
|
+
KV_COLLECT_MAP = {
|
17
|
+
:brpoplpush => { :destination => 2 }, :rpoplpush => { :destination => 2 },
|
18
|
+
:sdiffstore => { :destination => 1 }, :sinterstore => { :destination => 1 },
|
19
|
+
:sunionstore => { :destination => 1 }, :zinterstore => { :destination => 1 },
|
20
|
+
:zunionstore => { :destination => 1 }, :publish => { :channel => 1 },
|
21
|
+
:incrby => { :increment => 2 }, :incrbyfloat => { :increment => 2 },
|
22
|
+
:pexpire => { :milliseconds => 2 }, :pexpireat => { :milliseconds => 2 },
|
23
|
+
:expireat => { :timestamp => 2 }, :decrby => { :decrement => 2 },
|
24
|
+
:psetex => { :ttl => 2 }, :restore => { :ttl => 2 },
|
25
|
+
:setex => { :ttl => 2 }, :setnx => { :ttl => 2 },
|
26
|
+
:move => { :db => 2 }, :select => { :db => 1 },
|
27
|
+
:lindex => { :index => 2 }, :getset => { :value => 2 },
|
28
|
+
:keys => { :pattern => 1 }, :expire => { :seconds => 2 },
|
29
|
+
:rename => { :newkey => 2 }, :renamenx => { :newkey => 2 },
|
30
|
+
:getbit => { :offset => 2 }, :setbit => { :offset => 2 },
|
31
|
+
:setrange => { :offset => 2 }, :evalsha => { :sha => 1 },
|
32
|
+
:getrange => { :start => 2, :end => 3 },
|
33
|
+
:zrange => { :start => 2, :end => 3 },
|
34
|
+
:bitcount => { :start => 2, :stop => 3 },
|
35
|
+
:lrange => { :start => 2, :stop => 3 },
|
36
|
+
:zrevrange => { :start => 2, :stop => 3 },
|
37
|
+
:hincrby => { :field => 2, :increment => 3 },
|
38
|
+
:smove => { :source => 1, :destination => 2 },
|
39
|
+
:bitop => { :operation => 1, :destkey => 2 },
|
40
|
+
:hincrbyfloat => { :field => 2, :increment => 3 },
|
41
|
+
:zremrangebyrank => { :start => 2, :stop => 3 },
|
42
|
+
}
|
43
|
+
|
44
|
+
# The following operations don't require any special handling. For these,
|
45
|
+
# we only collect KVKey and KVOp
|
46
|
+
#
|
47
|
+
# :append, :blpop, :brpop, :decr, :del, :dump, :exists,
|
48
|
+
# :hgetall, :hkeys, :hlen, :hvals, :hmset, :incr, :linsert,
|
49
|
+
# :llen, :lpop, :lpush, :lpushx, :lrem, :lset, :ltrim,
|
50
|
+
# :persist, :pttl, :hscan, :rpop, :rpush, :rpushx, :sadd,
|
51
|
+
# :scard, :sismember, :smembers, :strlen, :sort, :spop,
|
52
|
+
# :srandmember, :srem, :sscan, :ttl, :type, :zadd, :zcard,
|
53
|
+
# :zcount, :zincrby, :zrangebyscore, :zrank, :zrem,
|
54
|
+
# :zremrangebyscore, :zrevrank, :zrevrangebyscore, :zscore
|
55
|
+
#
|
56
|
+
# For the operations in NO_KEY_OPS (above) we only collect
|
57
|
+
# KVOp (no KVKey)
|
58
|
+
|
59
|
+
def self.included(klass)
|
60
|
+
# We wrap two of the Redis methods to instrument
|
61
|
+
# operations
|
62
|
+
::Oboe::Util.method_alias(klass, :call, ::Redis::Client)
|
63
|
+
::Oboe::Util.method_alias(klass, :call_pipeline, ::Redis::Client)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Given any Redis operation command array, this method
|
67
|
+
# extracts the Key/Values to report to the TraceView
|
68
|
+
# dashboard.
|
69
|
+
#
|
70
|
+
# @param command [Array] the Redis operation array
|
71
|
+
# @param r [Return] the return value from the operation
|
72
|
+
# @return [Hash] the Key/Values to report
|
73
|
+
def extract_trace_details(command, r)
|
74
|
+
kvs = {}
|
75
|
+
op = command.first
|
76
|
+
|
77
|
+
begin
|
78
|
+
kvs[:KVOp] = command[0]
|
79
|
+
kvs[:RemoteHost] = @options[:host]
|
80
|
+
|
81
|
+
unless NO_KEY_OPS.include? op or (command[1].is_a?(Array) and command[1].count > 1)
|
82
|
+
if command[1].is_a?(Array)
|
83
|
+
kvs[:KVKey] = command[1].first
|
84
|
+
else
|
85
|
+
kvs[:KVKey] = command[1]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if KV_COLLECT_MAP[op]
|
90
|
+
# Extract KVs from command for this op
|
91
|
+
KV_COLLECT_MAP[op].each { |k, v|
|
92
|
+
kvs[k] = command[v]
|
93
|
+
}
|
94
|
+
else
|
95
|
+
# This case statement handle special cases not handled
|
96
|
+
# by KV_COLLECT_MAP
|
97
|
+
case op
|
98
|
+
when :set
|
99
|
+
if command.count > 3
|
100
|
+
options = command[3]
|
101
|
+
kvs[:ex] = options[:ex] if options.has_key?(:ex)
|
102
|
+
kvs[:px] = options[:px] if options.has_key?(:px)
|
103
|
+
kvs[:nx] = options[:nx] if options.has_key?(:nx)
|
104
|
+
kvs[:xx] = options[:xx] if options.has_key?(:xx)
|
105
|
+
end
|
106
|
+
|
107
|
+
when :get
|
108
|
+
kvs[:KVHit] = r.nil? ? 0 : 1
|
109
|
+
|
110
|
+
when :hdel, :hexists, :hget, :hset, :hsetnx
|
111
|
+
kvs[:field] = command[2] unless command[2].is_a?(Array)
|
112
|
+
if op == :hget
|
113
|
+
kvs[:KVHit] = r.nil? ? 0 : 1
|
114
|
+
end
|
115
|
+
|
116
|
+
when :eval
|
117
|
+
if command[1].length > 1024
|
118
|
+
kvs[:Script] = command[1][0..1023] + "(...snip...)"
|
119
|
+
else
|
120
|
+
kvs[:Script] = command[1]
|
121
|
+
end
|
122
|
+
|
123
|
+
when :script
|
124
|
+
kvs[:subcommand] = command[1]
|
125
|
+
kvs[:Backtrace] = Oboe::API.backtrace if Oboe::Config[:redis][:collect_backtraces]
|
126
|
+
if command[1] == "load"
|
127
|
+
if command[1].length > 1024
|
128
|
+
kvs[:Script] = command[2][0..1023] + "(...snip...)"
|
129
|
+
else
|
130
|
+
kvs[:Script] = command[2]
|
131
|
+
end
|
132
|
+
elsif command[1] == :exists
|
133
|
+
if command[2].is_a?(Array)
|
134
|
+
kvs[:KVKey] = command[2].inspect
|
135
|
+
else
|
136
|
+
kvs[:KVKey] = command[2]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
when :mget
|
141
|
+
if command[1].is_a?(Array)
|
142
|
+
kvs[:KVKeyCount] = command[1].count
|
143
|
+
else
|
144
|
+
kvs[:KVKeyCount] = command.count - 1
|
145
|
+
end
|
146
|
+
values = r.select{ |i| i }
|
147
|
+
kvs[:KVHitCount] = values.count
|
148
|
+
|
149
|
+
when :hmget
|
150
|
+
kvs[:KVKeyCount] = command.count - 2
|
151
|
+
values = r.select{ |i| i }
|
152
|
+
kvs[:KVHitCount] = values.count
|
153
|
+
|
154
|
+
when :mset, :msetnx
|
155
|
+
if command[1].is_a?(Array)
|
156
|
+
kvs[:KVKeyCount] = command[1].count / 2
|
157
|
+
else
|
158
|
+
kvs[:KVKeyCount] = (command.count - 1) / 2
|
159
|
+
end
|
160
|
+
end # case op
|
161
|
+
end # if KV_COLLECT_MAP[op]
|
162
|
+
|
163
|
+
rescue StandardError => e
|
164
|
+
Oboe.logger.debug "Error collecting redis KVs: #{e.message}"
|
165
|
+
Oboe.logger.debug e.backtrace.join("\n")
|
166
|
+
end
|
167
|
+
|
168
|
+
kvs
|
169
|
+
end
|
170
|
+
|
171
|
+
# Extracts the Key/Values to report from a pipelined
|
172
|
+
# call to the TraceView dashboard.
|
173
|
+
#
|
174
|
+
# @param pipeline [Redis::Pipeline] the Redis pipeline instance
|
175
|
+
# @return [Hash] the Key/Values to report
|
176
|
+
def extract_pipeline_details(pipeline)
|
177
|
+
kvs = {}
|
178
|
+
|
179
|
+
begin
|
180
|
+
kvs[:RemoteHost] = @options[:host]
|
181
|
+
kvs[:Backtrace] = Oboe::API.backtrace if Oboe::Config[:redis][:collect_backtraces]
|
182
|
+
|
183
|
+
command_count = pipeline.commands.count
|
184
|
+
kvs[:KVOpCount] = command_count
|
185
|
+
|
186
|
+
if pipeline.commands.first == :multi
|
187
|
+
kvs[:KVOp] = :multi
|
188
|
+
else
|
189
|
+
kvs[:KVOp] = :pipeline
|
190
|
+
end
|
191
|
+
|
192
|
+
# Report pipelined operations if the number
|
193
|
+
# of ops is reasonable
|
194
|
+
if command_count < 12
|
195
|
+
ops = []
|
196
|
+
pipeline.commands.each do |c|
|
197
|
+
ops << c.first
|
198
|
+
end
|
199
|
+
kvs[:KVOps] = ops.join(", ")
|
200
|
+
end
|
201
|
+
rescue StandardError => e
|
202
|
+
Oboe.logger.debug "[oboe/debug] Error extracting pipelined commands: #{e.message}"
|
203
|
+
Oboe.logger.debug e.backtrace
|
204
|
+
end
|
205
|
+
kvs
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# The wrapper method for Redis::Client.call. Here
|
210
|
+
# (when tracing) we capture KVs to report and pass
|
211
|
+
# the call along
|
212
|
+
#
|
213
|
+
def call_with_oboe(command, &block)
|
214
|
+
if Oboe.tracing?
|
215
|
+
::Oboe::API.log_entry('redis', {})
|
216
|
+
|
217
|
+
begin
|
218
|
+
r = call_without_oboe(command, &block)
|
219
|
+
report_kvs = extract_trace_details(command, r)
|
220
|
+
r
|
221
|
+
rescue StandardError => e
|
222
|
+
::Oboe::API.log_exception('redis', e)
|
223
|
+
raise
|
224
|
+
ensure
|
225
|
+
::Oboe::API.log_exit('redis', report_kvs)
|
226
|
+
end
|
227
|
+
|
228
|
+
else
|
229
|
+
call_without_oboe(command, &block)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# The wrapper method for Redis::Client.call_pipeline. Here
|
235
|
+
# (when tracing) we capture KVs to report and pass the call along
|
236
|
+
#
|
237
|
+
def call_pipeline_with_oboe(pipeline)
|
238
|
+
if Oboe.tracing?
|
239
|
+
report_kvs = {}
|
240
|
+
|
241
|
+
# Fall back to the raw tracing API so we can pass KVs
|
242
|
+
# back on exit (a limitation of the Oboe::API.trace
|
243
|
+
# block method) This removes the need for an info
|
244
|
+
# event to send additonal KVs
|
245
|
+
::Oboe::API.log_entry('redis', {})
|
246
|
+
|
247
|
+
report_kvs = extract_pipeline_details(pipeline)
|
248
|
+
|
249
|
+
begin
|
250
|
+
call_pipeline_without_oboe(pipeline)
|
251
|
+
rescue StandardError => e
|
252
|
+
::Oboe::API.log_exception('redis', e)
|
253
|
+
raise
|
254
|
+
ensure
|
255
|
+
::Oboe::API.log_exit('redis', report_kvs)
|
256
|
+
end
|
257
|
+
else
|
258
|
+
call_pipeline_without_oboe(pipeline)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
if Oboe::Config[:redis][:enabled]
|
268
|
+
if defined?(::Redis) and Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('3.0.0')
|
269
|
+
Oboe.logger.info "[oboe/loading] Instrumenting redis" if Oboe::Config[:verbose]
|
270
|
+
::Oboe::Util.send_include(::Redis::Client, ::Oboe::Inst::Redis::Client)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|