oboe 2.4.0.1 → 2.5.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|