traceview 3.0.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 +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +58 -0
- data/Appraisals +10 -0
- data/CHANGELOG.md +490 -0
- data/CONFIG.md +16 -0
- data/Gemfile +95 -0
- data/LICENSE +199 -0
- data/README.md +380 -0
- data/Rakefile +109 -0
- data/examples/DNT.md +35 -0
- data/examples/carrying_context.rb +225 -0
- data/examples/instrumenting_metal_controller.rb +8 -0
- data/examples/puma_on_heroku_config.rb +17 -0
- data/examples/tracing_async_threads.rb +125 -0
- data/examples/tracing_background_jobs.rb +52 -0
- data/examples/tracing_forked_processes.rb +100 -0
- data/examples/unicorn_on_heroku_config.rb +28 -0
- data/ext/oboe_metal/extconf.rb +61 -0
- data/ext/oboe_metal/noop/noop.c +7 -0
- data/ext/oboe_metal/src/bson/bson.h +221 -0
- data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
- data/ext/oboe_metal/src/oboe.h +275 -0
- data/ext/oboe_metal/src/oboe.hpp +352 -0
- data/ext/oboe_metal/src/oboe_wrap.cxx +3886 -0
- data/ext/oboe_metal/tests/test.rb +11 -0
- data/gemfiles/mongo.gemfile +33 -0
- data/gemfiles/moped.gemfile +33 -0
- data/get_version.rb +5 -0
- data/init.rb +4 -0
- data/lib/joboe_metal.rb +206 -0
- data/lib/oboe.rb +7 -0
- data/lib/oboe/README +2 -0
- data/lib/oboe/backward_compatibility.rb +59 -0
- data/lib/oboe/inst/rack.rb +11 -0
- data/lib/oboe_metal.rb +151 -0
- data/lib/rails/generators/traceview/install_generator.rb +76 -0
- data/lib/rails/generators/traceview/templates/traceview_initializer.rb +159 -0
- data/lib/traceview.rb +62 -0
- data/lib/traceview/api.rb +18 -0
- data/lib/traceview/api/layerinit.rb +51 -0
- data/lib/traceview/api/logging.rb +209 -0
- data/lib/traceview/api/memcache.rb +31 -0
- data/lib/traceview/api/profiling.rb +50 -0
- data/lib/traceview/api/tracing.rb +135 -0
- data/lib/traceview/api/util.rb +121 -0
- data/lib/traceview/base.rb +225 -0
- data/lib/traceview/config.rb +238 -0
- data/lib/traceview/frameworks/grape.rb +97 -0
- data/lib/traceview/frameworks/padrino.rb +64 -0
- data/lib/traceview/frameworks/padrino/templates.rb +58 -0
- data/lib/traceview/frameworks/rails.rb +145 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_ajax_header.js.erb +5 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_footer.js.erb +1 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_header.js.erb +3 -0
- data/lib/traceview/frameworks/rails/inst/action_controller.rb +216 -0
- data/lib/traceview/frameworks/rails/inst/action_view.rb +56 -0
- data/lib/traceview/frameworks/rails/inst/action_view_2x.rb +54 -0
- data/lib/traceview/frameworks/rails/inst/action_view_30.rb +48 -0
- data/lib/traceview/frameworks/rails/inst/active_record.rb +24 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/oracle.rb +18 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/utils.rb +117 -0
- data/lib/traceview/frameworks/sinatra.rb +95 -0
- data/lib/traceview/frameworks/sinatra/templates.rb +56 -0
- data/lib/traceview/inst/cassandra.rb +279 -0
- data/lib/traceview/inst/dalli.rb +86 -0
- data/lib/traceview/inst/em-http-request.rb +99 -0
- data/lib/traceview/inst/excon.rb +111 -0
- data/lib/traceview/inst/faraday.rb +73 -0
- data/lib/traceview/inst/http.rb +87 -0
- data/lib/traceview/inst/httpclient.rb +173 -0
- data/lib/traceview/inst/memcache.rb +102 -0
- data/lib/traceview/inst/memcached.rb +94 -0
- data/lib/traceview/inst/mongo.rb +238 -0
- data/lib/traceview/inst/moped.rb +474 -0
- data/lib/traceview/inst/rack.rb +122 -0
- data/lib/traceview/inst/redis.rb +271 -0
- data/lib/traceview/inst/resque.rb +192 -0
- data/lib/traceview/inst/rest-client.rb +38 -0
- data/lib/traceview/inst/sequel.rb +162 -0
- data/lib/traceview/inst/typhoeus.rb +102 -0
- data/lib/traceview/instrumentation.rb +21 -0
- data/lib/traceview/loading.rb +94 -0
- data/lib/traceview/logger.rb +41 -0
- data/lib/traceview/method_profiling.rb +84 -0
- data/lib/traceview/ruby.rb +36 -0
- data/lib/traceview/support.rb +113 -0
- data/lib/traceview/thread_local.rb +26 -0
- data/lib/traceview/util.rb +250 -0
- data/lib/traceview/version.rb +16 -0
- data/lib/traceview/xtrace.rb +90 -0
- data/test/frameworks/apps/grape_nested.rb +30 -0
- data/test/frameworks/apps/grape_simple.rb +24 -0
- data/test/frameworks/apps/padrino_simple.rb +45 -0
- data/test/frameworks/apps/sinatra_simple.rb +24 -0
- data/test/frameworks/grape_test.rb +142 -0
- data/test/frameworks/padrino_test.rb +30 -0
- data/test/frameworks/sinatra_test.rb +30 -0
- data/test/instrumentation/cassandra_test.rb +380 -0
- data/test/instrumentation/dalli_test.rb +171 -0
- data/test/instrumentation/em_http_request_test.rb +86 -0
- data/test/instrumentation/excon_test.rb +207 -0
- data/test/instrumentation/faraday_test.rb +235 -0
- data/test/instrumentation/http_test.rb +140 -0
- data/test/instrumentation/httpclient_test.rb +296 -0
- data/test/instrumentation/memcache_test.rb +251 -0
- data/test/instrumentation/memcached_test.rb +226 -0
- data/test/instrumentation/mongo_test.rb +462 -0
- data/test/instrumentation/moped_test.rb +496 -0
- data/test/instrumentation/rack_test.rb +116 -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/instrumentation/resque_test.rb +62 -0
- data/test/instrumentation/rest-client_test.rb +294 -0
- data/test/instrumentation/sequel_mysql2_test.rb +326 -0
- data/test/instrumentation/sequel_mysql_test.rb +326 -0
- data/test/instrumentation/sequel_pg_test.rb +330 -0
- data/test/instrumentation/typhoeus_test.rb +285 -0
- data/test/minitest_helper.rb +187 -0
- data/test/profiling/method_test.rb +198 -0
- data/test/servers/rackapp_8101.rb +22 -0
- data/test/support/backcompat_test.rb +269 -0
- data/test/support/config_test.rb +128 -0
- data/test/support/dnt_test.rb +73 -0
- data/test/support/liboboe_settings_test.rb +104 -0
- data/test/support/xtrace_test.rb +35 -0
- data/traceview.gemspec +29 -0
- metadata +250 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'cgi'
|
|
6
|
+
|
|
7
|
+
module TraceView
|
|
8
|
+
class Rack
|
|
9
|
+
attr_reader :app
|
|
10
|
+
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def collect(req, env)
|
|
16
|
+
report_kvs = {}
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
report_kvs['HTTP-Host'] = req.host
|
|
20
|
+
report_kvs['Port'] = req.port
|
|
21
|
+
report_kvs['Proto'] = req.scheme
|
|
22
|
+
report_kvs[:Method] = req.request_method
|
|
23
|
+
report_kvs['AJAX'] = true if req.xhr?
|
|
24
|
+
report_kvs['ClientIP'] = req.ip
|
|
25
|
+
|
|
26
|
+
if TraceView::Config[:rack][:log_args]
|
|
27
|
+
report_kvs['Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
report_kvs['X-TV-Meta'] = env['HTTP_X_TV_META'] if env.key?('HTTP_X_TV_META')
|
|
31
|
+
|
|
32
|
+
# Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
|
|
33
|
+
report_kvs['Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
|
|
34
|
+
report_kvs['Request-Start'] = env['HTTP_X_QUEUE_START'] if env.key?('HTTP_X_QUEUE_START')
|
|
35
|
+
report_kvs['Queue-Time'] = env['HTTP_X_QUEUE_TIME'] if env.key?('HTTP_X_QUEUE_TIME')
|
|
36
|
+
|
|
37
|
+
report_kvs['Forwarded-For'] = env['HTTP_X_FORWARDED_FOR'] if env.key?('HTTP_X_FORWARDED_FOR')
|
|
38
|
+
report_kvs['Forwarded-Host'] = env['HTTP_X_FORWARDED_HOST'] if env.key?('HTTP_X_FORWARDED_HOST')
|
|
39
|
+
report_kvs['Forwarded-Proto'] = env['HTTP_X_FORWARDED_PROTO'] if env.key?('HTTP_X_FORWARDED_PROTO')
|
|
40
|
+
report_kvs['Forwarded-Port'] = env['HTTP_X_FORWARDED_PORT'] if env.key?('HTTP_X_FORWARDED_PORT')
|
|
41
|
+
|
|
42
|
+
report_kvs['Ruby.TraceView.Version'] = ::TraceView::Version::STRING
|
|
43
|
+
report_kvs['ProcessID'] = Process.pid
|
|
44
|
+
report_kvs['ThreadID'] = Thread.current.to_s[/0x\w*/]
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
# Discard any potential exceptions. Debug log and report whatever we can.
|
|
47
|
+
TraceView.logger.debug "[traceview/debug] Rack KV collection error: #{e.inspect}"
|
|
48
|
+
end
|
|
49
|
+
report_kvs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def call(env)
|
|
53
|
+
rack_skipped = false
|
|
54
|
+
|
|
55
|
+
# In the case of nested Ruby apps such as Grape inside of Rails
|
|
56
|
+
# or Grape inside of Grape, each app has it's own instance
|
|
57
|
+
# of rack middleware. We avoid tracing rack more than once and
|
|
58
|
+
# instead start instrumenting from the first rack pass.
|
|
59
|
+
|
|
60
|
+
# If we're already tracing a rack layer, dont't start another one.
|
|
61
|
+
if TraceView.tracing? && TraceView.layer == 'rack'
|
|
62
|
+
rack_skipped = true
|
|
63
|
+
TraceView.logger.debug "[traceview/rack] Rack skipped!"
|
|
64
|
+
return @app.call(env)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
req = ::Rack::Request.new(env)
|
|
68
|
+
report_kvs = {}
|
|
69
|
+
|
|
70
|
+
if TraceView::Config[:rack][:log_args]
|
|
71
|
+
report_kvs[:URL] = ::CGI.unescape(req.fullpath)
|
|
72
|
+
else
|
|
73
|
+
report_kvs[:URL] = ::CGI.unescape(req.path)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Check for and validate X-Trace request header to pick up tracing context
|
|
77
|
+
xtrace = env.is_a?(Hash) ? env['HTTP_X_TRACE'] : nil
|
|
78
|
+
xtrace_header = xtrace if xtrace && TraceView::XTrace.valid?(xtrace)
|
|
79
|
+
|
|
80
|
+
# Under JRuby, JTraceView may have already started a trace. Make note of this
|
|
81
|
+
# if so and don't clear context on log_end (see traceview/api/logging.rb)
|
|
82
|
+
TraceView.has_incoming_context = TraceView.tracing?
|
|
83
|
+
TraceView.has_xtrace_header = xtrace_header
|
|
84
|
+
TraceView.is_continued_trace = TraceView.has_incoming_context or TraceView.has_xtrace_header
|
|
85
|
+
|
|
86
|
+
TraceView::API.log_start('rack', xtrace_header, report_kvs)
|
|
87
|
+
|
|
88
|
+
# We only trace a subset of requests based off of sample rate so if
|
|
89
|
+
# TraceView::API.log_start really did start a trace, we act accordingly here.
|
|
90
|
+
if TraceView.tracing?
|
|
91
|
+
report_kvs = collect(req, env)
|
|
92
|
+
|
|
93
|
+
# We log an info event with the HTTP KVs found in TraceView::Rack.collect
|
|
94
|
+
# This is done here so in the case of stacks that try/catch/abort
|
|
95
|
+
# (looking at you Grape) we're sure the KVs get reported now as
|
|
96
|
+
# this code may not be returned to later.
|
|
97
|
+
TraceView::API.log_info('rack', report_kvs)
|
|
98
|
+
|
|
99
|
+
status, headers, response = @app.call(env)
|
|
100
|
+
|
|
101
|
+
xtrace = TraceView::API.log_end('rack', :Status => status)
|
|
102
|
+
else
|
|
103
|
+
status, headers, response = @app.call(env)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
[status, headers, response]
|
|
107
|
+
rescue Exception => e
|
|
108
|
+
unless rack_skipped
|
|
109
|
+
TraceView::API.log_exception('rack', e)
|
|
110
|
+
xtrace = TraceView::API.log_end('rack', :Status => 500)
|
|
111
|
+
end
|
|
112
|
+
raise
|
|
113
|
+
ensure
|
|
114
|
+
if !rack_skipped && headers && TraceView::XTrace.valid?(xtrace)
|
|
115
|
+
unless defined?(JRUBY_VERSION) && TraceView.is_continued_trace?
|
|
116
|
+
headers['X-Trace'] = xtrace if headers.is_a?(Hash)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Copyright (c) 2014 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module TraceView
|
|
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
|
+
::TraceView::Util.method_alias(klass, :call, ::Redis::Client)
|
|
63
|
+
::TraceView::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) || (command[1].is_a?(Array) && 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.key?(:ex)
|
|
102
|
+
kvs[:px] = options[:px] if options.key?(:px)
|
|
103
|
+
kvs[:nx] = options[:nx] if options.key?(:nx)
|
|
104
|
+
kvs[:xx] = options[:xx] if options.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] = TraceView::API.backtrace if TraceView::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
|
+
TraceView.logger.debug "Error collecting redis KVs: #{e.message}"
|
|
165
|
+
TraceView.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] = TraceView::API.backtrace if TraceView::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
|
+
TraceView.logger.debug "[traceview/debug] Error extracting pipelined commands: #{e.message}"
|
|
203
|
+
TraceView.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_traceview(command, &block)
|
|
214
|
+
if TraceView.tracing?
|
|
215
|
+
::TraceView::API.log_entry('redis', {})
|
|
216
|
+
|
|
217
|
+
begin
|
|
218
|
+
r = call_without_traceview(command, &block)
|
|
219
|
+
report_kvs = extract_trace_details(command, r)
|
|
220
|
+
r
|
|
221
|
+
rescue StandardError => e
|
|
222
|
+
::TraceView::API.log_exception('redis', e)
|
|
223
|
+
raise
|
|
224
|
+
ensure
|
|
225
|
+
::TraceView::API.log_exit('redis', report_kvs)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
else
|
|
229
|
+
call_without_traceview(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_traceview(pipeline)
|
|
238
|
+
if TraceView.tracing?
|
|
239
|
+
# Fall back to the raw tracing API so we can pass KVs
|
|
240
|
+
# back on exit (a limitation of the TraceView::API.trace
|
|
241
|
+
# block method) This removes the need for an info
|
|
242
|
+
# event to send additonal KVs
|
|
243
|
+
::TraceView::API.log_entry('redis', {})
|
|
244
|
+
|
|
245
|
+
report_kvs = extract_pipeline_details(pipeline)
|
|
246
|
+
|
|
247
|
+
begin
|
|
248
|
+
call_pipeline_without_traceview(pipeline)
|
|
249
|
+
rescue StandardError => e
|
|
250
|
+
::TraceView::API.log_exception('redis', e)
|
|
251
|
+
raise
|
|
252
|
+
ensure
|
|
253
|
+
::TraceView::API.log_exit('redis', report_kvs)
|
|
254
|
+
end
|
|
255
|
+
else
|
|
256
|
+
call_pipeline_without_traceview(pipeline)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
if TraceView::Config[:redis][:enabled]
|
|
266
|
+
if defined?(::Redis) && Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('3.0.0')
|
|
267
|
+
TraceView.logger.info '[traceview/loading] Instrumenting redis' if TraceView::Config[:verbose]
|
|
268
|
+
::TraceView::Util.send_include(::Redis::Client, ::TraceView::Inst::Redis::Client)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'socket'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module TraceView
|
|
8
|
+
module Inst
|
|
9
|
+
module Resque
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.send :extend, ::Resque
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def extract_trace_details(op, klass, args)
|
|
15
|
+
report_kvs = {}
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
report_kvs[:Op] = op.to_s
|
|
19
|
+
report_kvs[:Class] = klass.to_s if klass
|
|
20
|
+
|
|
21
|
+
if TraceView::Config[:resque][:log_args]
|
|
22
|
+
kv_args = args.to_json
|
|
23
|
+
|
|
24
|
+
# Limit the argument json string to 1024 bytes
|
|
25
|
+
if kv_args.length > 1024
|
|
26
|
+
report_kvs[:Args] = kv_args[0..1023] + '...[snipped]'
|
|
27
|
+
else
|
|
28
|
+
report_kvs[:Args] = kv_args
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
report_kvs[:Backtrace] = TraceView::API.backtrace if TraceView::Config[:resque][:collect_backtraces]
|
|
33
|
+
rescue
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
report_kvs
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def enqueue_with_traceview(klass, *args)
|
|
40
|
+
if TraceView.tracing?
|
|
41
|
+
report_kvs = extract_trace_details(:enqueue, klass, args)
|
|
42
|
+
|
|
43
|
+
TraceView::API.trace('resque-client', report_kvs, :enqueue) do
|
|
44
|
+
args.push(:parent_trace_id => TraceView::Context.toString) if TraceView::Config[:resque][:link_workers]
|
|
45
|
+
enqueue_without_traceview(klass, *args)
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
enqueue_without_traceview(klass, *args)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def enqueue_to_with_traceview(queue, klass, *args)
|
|
53
|
+
if TraceView.tracing? && !TraceView.tracing_layer_op?(:enqueue)
|
|
54
|
+
report_kvs = extract_trace_details(:enqueue_to, klass, args)
|
|
55
|
+
report_kvs[:Queue] = queue.to_s if queue
|
|
56
|
+
|
|
57
|
+
TraceView::API.trace('resque-client', report_kvs) do
|
|
58
|
+
args.push(:parent_trace_id => TraceView::Context.toString) if TraceView::Config[:resque][:link_workers]
|
|
59
|
+
enqueue_to_without_traceview(queue, klass, *args)
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
enqueue_to_without_traceview(queue, klass, *args)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def dequeue_with_traceview(klass, *args)
|
|
67
|
+
if TraceView.tracing?
|
|
68
|
+
report_kvs = extract_trace_details(:dequeue, klass, args)
|
|
69
|
+
|
|
70
|
+
TraceView::API.trace('resque-client', report_kvs) do
|
|
71
|
+
dequeue_without_traceview(klass, *args)
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
dequeue_without_traceview(klass, *args)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
module ResqueWorker
|
|
80
|
+
def perform_with_traceview(job)
|
|
81
|
+
report_kvs = {}
|
|
82
|
+
last_arg = nil
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
report_kvs[:Op] = :perform
|
|
86
|
+
|
|
87
|
+
# Set these keys for the ability to separate out
|
|
88
|
+
# background tasks into a separate app on the server-side UI
|
|
89
|
+
report_kvs[:Controller] = :Resque
|
|
90
|
+
report_kvs[:Action] = :perform
|
|
91
|
+
|
|
92
|
+
report_kvs['HTTP-Host'] = Socket.gethostname
|
|
93
|
+
report_kvs[:URL] = '/resque/' + job.queue
|
|
94
|
+
report_kvs[:Method] = 'NONE'
|
|
95
|
+
report_kvs[:Queue] = job.queue
|
|
96
|
+
|
|
97
|
+
report_kvs[:Class] = job.payload['class']
|
|
98
|
+
|
|
99
|
+
if TraceView::Config[:resque][:log_args]
|
|
100
|
+
kv_args = job.payload['args'].to_json
|
|
101
|
+
|
|
102
|
+
# Limit the argument json string to 1024 bytes
|
|
103
|
+
if kv_args.length > 1024
|
|
104
|
+
report_kvs[:Args] = kv_args[0..1023] + '...[snipped]'
|
|
105
|
+
else
|
|
106
|
+
report_kvs[:Args] = kv_args
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
last_arg = job.payload['args'].last
|
|
111
|
+
rescue
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if last_arg.is_a?(Hash) && last_arg.key?('parent_trace_id')
|
|
115
|
+
begin
|
|
116
|
+
# Since the enqueue was traced, we force trace the actual job execution and reference
|
|
117
|
+
# the enqueue trace with ParentTraceID
|
|
118
|
+
report_kvs[:ParentTraceID] = last_arg['parent_trace_id']
|
|
119
|
+
job.payload['args'].pop
|
|
120
|
+
|
|
121
|
+
rescue
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Force this trace regardless of sampling rate so that child trace can be
|
|
125
|
+
# link to parent trace.
|
|
126
|
+
TraceView::API.start_trace('resque-worker', nil, report_kvs.merge('Force' => true)) do
|
|
127
|
+
perform_without_traceview(job)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
else
|
|
131
|
+
TraceView::API.start_trace('resque-worker', nil, report_kvs) do
|
|
132
|
+
perform_without_traceview(job)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
module ResqueJob
|
|
139
|
+
def fail_with_traceview(exception)
|
|
140
|
+
if TraceView.tracing?
|
|
141
|
+
TraceView::API.log_exception('resque', exception)
|
|
142
|
+
end
|
|
143
|
+
fail_without_traceview(exception)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
if defined?(::Resque)
|
|
150
|
+
TraceView.logger.info '[traceview/loading] Instrumenting resque' if TraceView::Config[:verbose]
|
|
151
|
+
|
|
152
|
+
::Resque.module_eval do
|
|
153
|
+
include TraceView::Inst::Resque
|
|
154
|
+
|
|
155
|
+
[:enqueue, :enqueue_to, :dequeue].each do |m|
|
|
156
|
+
if method_defined?(m)
|
|
157
|
+
module_eval "alias #{m}_without_traceview #{m}"
|
|
158
|
+
module_eval "alias #{m} #{m}_with_traceview"
|
|
159
|
+
elsif TraceView::Config[:verbose]
|
|
160
|
+
TraceView.logger.warn "[traceview/loading] Couldn't properly instrument Resque (#{m}). Partial traces may occur."
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if defined?(::Resque::Worker)
|
|
166
|
+
::Resque::Worker.class_eval do
|
|
167
|
+
include TraceView::Inst::ResqueWorker
|
|
168
|
+
|
|
169
|
+
if method_defined?(:perform)
|
|
170
|
+
alias perform_without_traceview perform
|
|
171
|
+
alias perform perform_with_traceview
|
|
172
|
+
elsif TraceView::Config[:verbose]
|
|
173
|
+
TraceView.logger.warn '[traceview/loading] Couldn\'t properly instrument ResqueWorker (perform). Partial traces may occur.'
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
if defined?(::Resque::Job)
|
|
179
|
+
::Resque::Job.class_eval do
|
|
180
|
+
include TraceView::Inst::ResqueJob
|
|
181
|
+
|
|
182
|
+
if method_defined?(:fail)
|
|
183
|
+
alias fail_without_traceview fail
|
|
184
|
+
alias fail fail_with_traceview
|
|
185
|
+
elsif TraceView::Config[:verbose]
|
|
186
|
+
TraceView.logger.warn '[traceview/loading] Couldn\'t properly instrument ResqueWorker (fail). Partial traces may occur.'
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|