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.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +58 -0
  5. data/Appraisals +10 -0
  6. data/CHANGELOG.md +490 -0
  7. data/CONFIG.md +16 -0
  8. data/Gemfile +95 -0
  9. data/LICENSE +199 -0
  10. data/README.md +380 -0
  11. data/Rakefile +109 -0
  12. data/examples/DNT.md +35 -0
  13. data/examples/carrying_context.rb +225 -0
  14. data/examples/instrumenting_metal_controller.rb +8 -0
  15. data/examples/puma_on_heroku_config.rb +17 -0
  16. data/examples/tracing_async_threads.rb +125 -0
  17. data/examples/tracing_background_jobs.rb +52 -0
  18. data/examples/tracing_forked_processes.rb +100 -0
  19. data/examples/unicorn_on_heroku_config.rb +28 -0
  20. data/ext/oboe_metal/extconf.rb +61 -0
  21. data/ext/oboe_metal/noop/noop.c +7 -0
  22. data/ext/oboe_metal/src/bson/bson.h +221 -0
  23. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  24. data/ext/oboe_metal/src/oboe.h +275 -0
  25. data/ext/oboe_metal/src/oboe.hpp +352 -0
  26. data/ext/oboe_metal/src/oboe_wrap.cxx +3886 -0
  27. data/ext/oboe_metal/tests/test.rb +11 -0
  28. data/gemfiles/mongo.gemfile +33 -0
  29. data/gemfiles/moped.gemfile +33 -0
  30. data/get_version.rb +5 -0
  31. data/init.rb +4 -0
  32. data/lib/joboe_metal.rb +206 -0
  33. data/lib/oboe.rb +7 -0
  34. data/lib/oboe/README +2 -0
  35. data/lib/oboe/backward_compatibility.rb +59 -0
  36. data/lib/oboe/inst/rack.rb +11 -0
  37. data/lib/oboe_metal.rb +151 -0
  38. data/lib/rails/generators/traceview/install_generator.rb +76 -0
  39. data/lib/rails/generators/traceview/templates/traceview_initializer.rb +159 -0
  40. data/lib/traceview.rb +62 -0
  41. data/lib/traceview/api.rb +18 -0
  42. data/lib/traceview/api/layerinit.rb +51 -0
  43. data/lib/traceview/api/logging.rb +209 -0
  44. data/lib/traceview/api/memcache.rb +31 -0
  45. data/lib/traceview/api/profiling.rb +50 -0
  46. data/lib/traceview/api/tracing.rb +135 -0
  47. data/lib/traceview/api/util.rb +121 -0
  48. data/lib/traceview/base.rb +225 -0
  49. data/lib/traceview/config.rb +238 -0
  50. data/lib/traceview/frameworks/grape.rb +97 -0
  51. data/lib/traceview/frameworks/padrino.rb +64 -0
  52. data/lib/traceview/frameworks/padrino/templates.rb +58 -0
  53. data/lib/traceview/frameworks/rails.rb +145 -0
  54. data/lib/traceview/frameworks/rails/helpers/rum/rum_ajax_header.js.erb +5 -0
  55. data/lib/traceview/frameworks/rails/helpers/rum/rum_footer.js.erb +1 -0
  56. data/lib/traceview/frameworks/rails/helpers/rum/rum_header.js.erb +3 -0
  57. data/lib/traceview/frameworks/rails/inst/action_controller.rb +216 -0
  58. data/lib/traceview/frameworks/rails/inst/action_view.rb +56 -0
  59. data/lib/traceview/frameworks/rails/inst/action_view_2x.rb +54 -0
  60. data/lib/traceview/frameworks/rails/inst/action_view_30.rb +48 -0
  61. data/lib/traceview/frameworks/rails/inst/active_record.rb +24 -0
  62. data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  63. data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
  64. data/lib/traceview/frameworks/rails/inst/connection_adapters/oracle.rb +18 -0
  65. data/lib/traceview/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
  66. data/lib/traceview/frameworks/rails/inst/connection_adapters/utils.rb +117 -0
  67. data/lib/traceview/frameworks/sinatra.rb +95 -0
  68. data/lib/traceview/frameworks/sinatra/templates.rb +56 -0
  69. data/lib/traceview/inst/cassandra.rb +279 -0
  70. data/lib/traceview/inst/dalli.rb +86 -0
  71. data/lib/traceview/inst/em-http-request.rb +99 -0
  72. data/lib/traceview/inst/excon.rb +111 -0
  73. data/lib/traceview/inst/faraday.rb +73 -0
  74. data/lib/traceview/inst/http.rb +87 -0
  75. data/lib/traceview/inst/httpclient.rb +173 -0
  76. data/lib/traceview/inst/memcache.rb +102 -0
  77. data/lib/traceview/inst/memcached.rb +94 -0
  78. data/lib/traceview/inst/mongo.rb +238 -0
  79. data/lib/traceview/inst/moped.rb +474 -0
  80. data/lib/traceview/inst/rack.rb +122 -0
  81. data/lib/traceview/inst/redis.rb +271 -0
  82. data/lib/traceview/inst/resque.rb +192 -0
  83. data/lib/traceview/inst/rest-client.rb +38 -0
  84. data/lib/traceview/inst/sequel.rb +162 -0
  85. data/lib/traceview/inst/typhoeus.rb +102 -0
  86. data/lib/traceview/instrumentation.rb +21 -0
  87. data/lib/traceview/loading.rb +94 -0
  88. data/lib/traceview/logger.rb +41 -0
  89. data/lib/traceview/method_profiling.rb +84 -0
  90. data/lib/traceview/ruby.rb +36 -0
  91. data/lib/traceview/support.rb +113 -0
  92. data/lib/traceview/thread_local.rb +26 -0
  93. data/lib/traceview/util.rb +250 -0
  94. data/lib/traceview/version.rb +16 -0
  95. data/lib/traceview/xtrace.rb +90 -0
  96. data/test/frameworks/apps/grape_nested.rb +30 -0
  97. data/test/frameworks/apps/grape_simple.rb +24 -0
  98. data/test/frameworks/apps/padrino_simple.rb +45 -0
  99. data/test/frameworks/apps/sinatra_simple.rb +24 -0
  100. data/test/frameworks/grape_test.rb +142 -0
  101. data/test/frameworks/padrino_test.rb +30 -0
  102. data/test/frameworks/sinatra_test.rb +30 -0
  103. data/test/instrumentation/cassandra_test.rb +380 -0
  104. data/test/instrumentation/dalli_test.rb +171 -0
  105. data/test/instrumentation/em_http_request_test.rb +86 -0
  106. data/test/instrumentation/excon_test.rb +207 -0
  107. data/test/instrumentation/faraday_test.rb +235 -0
  108. data/test/instrumentation/http_test.rb +140 -0
  109. data/test/instrumentation/httpclient_test.rb +296 -0
  110. data/test/instrumentation/memcache_test.rb +251 -0
  111. data/test/instrumentation/memcached_test.rb +226 -0
  112. data/test/instrumentation/mongo_test.rb +462 -0
  113. data/test/instrumentation/moped_test.rb +496 -0
  114. data/test/instrumentation/rack_test.rb +116 -0
  115. data/test/instrumentation/redis_hashes_test.rb +265 -0
  116. data/test/instrumentation/redis_keys_test.rb +318 -0
  117. data/test/instrumentation/redis_lists_test.rb +310 -0
  118. data/test/instrumentation/redis_misc_test.rb +160 -0
  119. data/test/instrumentation/redis_sets_test.rb +293 -0
  120. data/test/instrumentation/redis_sortedsets_test.rb +325 -0
  121. data/test/instrumentation/redis_strings_test.rb +333 -0
  122. data/test/instrumentation/resque_test.rb +62 -0
  123. data/test/instrumentation/rest-client_test.rb +294 -0
  124. data/test/instrumentation/sequel_mysql2_test.rb +326 -0
  125. data/test/instrumentation/sequel_mysql_test.rb +326 -0
  126. data/test/instrumentation/sequel_pg_test.rb +330 -0
  127. data/test/instrumentation/typhoeus_test.rb +285 -0
  128. data/test/minitest_helper.rb +187 -0
  129. data/test/profiling/method_test.rb +198 -0
  130. data/test/servers/rackapp_8101.rb +22 -0
  131. data/test/support/backcompat_test.rb +269 -0
  132. data/test/support/config_test.rb +128 -0
  133. data/test/support/dnt_test.rb +73 -0
  134. data/test/support/liboboe_settings_test.rb +104 -0
  135. data/test/support/xtrace_test.rb +35 -0
  136. data/traceview.gemspec +29 -0
  137. 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
+