instana 0.12.1 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eaaf893ffa35a79234320530e0d0d6d0631fb37e
4
- data.tar.gz: 47ef832a84ff8e5746eb459cd905400742a9d80e
3
+ metadata.gz: fff8c1b1ed4764301a7f1a45dec8973df279b2d8
4
+ data.tar.gz: 8947536ba2a71f9916712d55980f77f57af8fde7
5
5
  SHA512:
6
- metadata.gz: 77065dc8af02060b4196969bde7e7c05584d2a4544b32f3e177ccf49d97bc899145dc38d315c3a48b0fa8dd260930e95dfc1ecbf3b0ca9d0e262ec8a27069996
7
- data.tar.gz: cc288b3c9b9954b10bb4b1bf4d63dfb37c31b6d8e8f0847aa8ef9224b1e5022858bd0905bc00d61bca5c678edd6c9c5c752cf7df08f26611bb6e23230efd4578
6
+ metadata.gz: 1626e65166c81355c55105db86179e22c8cf9a1ae7ee7292e17574015f0acf2590eb0ed06b667d20d6d1294bd2d79eddaef584951119fc750880950401caa2ff
7
+ data.tar.gz: 18c6581dbaece4419c1fd6edb59e4fabd30efab3789c24bc0b006420c3bce83f774ae4a239a37dc47b7c8ea1d0e99bf2f981c59d692b39b0e4ac00870010820e
data/Configuration.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Configuration
2
2
 
3
+ ## Enabling/Disabling Components
4
+
5
+ Individual components can be enabled and disabled with a local config.
6
+
7
+ To disable a single component in the gem, you can disable a single component with the following code:
8
+
9
+ ```Ruby
10
+ ::Instana.config[:metrics][:gc][:enabled] = false
11
+ ```
12
+ Current metric components are `:gc`, `:memory` and `:thread`.
13
+
14
+ Instrumentation can be disabled as:
15
+
16
+ ```Ruby
17
+ ::Instana.config[:excon][:enabled] = false
18
+ ::Instana.config[:rack][:enabled] = false
19
+ ```
20
+
21
+ ## Rack Middleware
22
+
23
+ This gem will detect and automagically insert the Instana Rack middleware into the middleware stack when a [supported framework](https://instana.atlassian.net/wiki/display/DOCS/Ruby) is present. We are currently adding support for more frameworks. If you are using a yet to be instrumented framework, you can insert the Instana Rack middleware with the following:
24
+
25
+ ```Ruby
26
+ require "instana/rack"
27
+ config.middleware.use ::Instana::Rack
28
+ ```
29
+
30
+ ...or whatever specific middleware call is appropriate for your framework.
31
+
32
+
33
+ ## Managing the Agent Background Thread
34
+
35
+ This agent spawns a lightweight background thread to periodically collect and report metrics and traces. Be default, this uses a standard Ruby thread. If you wish to have greater control and potentially boot the agent reporting manually in an alternative thread system (such as actor based threads), you can do so with the following:
36
+
37
+ ```Ruby
38
+ gem "instana", :require => "instana/setup"
39
+ ```
40
+
41
+ ...then in the background thread of your choice simply call:
42
+
43
+ ```Ruby
44
+ ::Instana.agent.start
45
+ ```
46
+
47
+ Note that this call is blocking. It kicks off a loop of timers that periodically collects and reports metrics and trace data. This should only be called from inside an already initialized background thread:
48
+
49
+ ```Ruby
50
+ Thread.new do
51
+ ::Instana.agent.start
52
+ end
53
+ ```
54
+
55
+ ### Caveat
56
+
57
+ In the case of forking webservers such as Unicorn or Puma in clustered mode, the agent detects the pid change and re-spawns the background thread. If you are managing the background thread yourself with the steps above _and_ you are using a forking webserver (or anything else that may fork the original process), you should also do the following.
58
+
59
+ When a fork is detected, the agent handles the re-initialization and then calls `::Agent.instana.spawn_background_thread`. This by default uses the standard `Thread.new`. If you wish to control this, you should override this method by re-defining that method. For example:
60
+
61
+ ```ruby
62
+ # This method can be overridden with the following:
63
+ #
64
+ module Instana
65
+ class Agent
66
+ def spawn_background_thread
67
+ # start/identify custom thread
68
+ ::Instana.agent.start
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
3
74
  ## Logging
4
75
 
5
76
  The Instana logger is a standard Ruby logger that logs debug, warn, info
@@ -16,6 +87,7 @@ what extra debug info it reports. It allows for:
16
87
  * `:agent` - shows all agent state related debug messages
17
88
  * `:agent_comm` - outputs all request/response pairs to and from the
18
89
  host agent
90
+ * `:trace` - outputs debug messages related to tracing and trace management
19
91
 
20
92
  Log messages can be generated for these channels using:
21
93
 
@@ -27,7 +99,7 @@ Log messages can be generated for these channels using:
27
99
  To set the debug log level:
28
100
 
29
101
  ```Ruby
30
- ::Instana.logger.debug_level = [:agent, :agent_comm]
102
+ ::Instana.logger.debug_level = [:agent, :agent_comm, :trace]
31
103
  # or
32
104
  ::Instana.logger.debug_level = [:agent_comm]
33
105
  # or
data/Gemfile CHANGED
@@ -13,9 +13,15 @@ group :development, :test do
13
13
  gem "cuba"
14
14
  gem "roda"
15
15
 
16
+ # HTTP Clients
17
+ gem 'rest-client'
18
+
16
19
  # Webservers
17
20
  gem "puma"
18
21
 
22
+ # HTTP Clients
23
+ gem 'excon'
24
+
19
25
  # Rack v2 dropped support for Ruby 2.2 and higher.
20
26
  if RUBY_VERSION < '2.2'
21
27
  gem 'rack', '< 2.0'
data/README.md CHANGED
@@ -17,12 +17,12 @@ This gem supports Ruby versions 2.0 or greater.
17
17
 
18
18
  Any and all feedback is welcome. Happy Ruby visibility.
19
19
 
20
- ![rails](https://s3.amazonaws.com/instana/rails-logo.jpg?1)
21
- ![roda](https://s3.amazonaws.com/instana/roda-logo.png?1)
22
- ![cuba](https://s3.amazonaws.com/instana/cuba-logo.png?1)
23
- ![sinatra](https://s3.amazonaws.com/instana/sinatra-logo.png?1)
24
- ![padrino](https://s3.amazonaws.com/instana/padrino-logo.png?1)
25
- ![rack](https://s3.amazonaws.com/instana/rack-logo.png?1)
20
+ [![rails](https://s3.amazonaws.com/instana/rails-logo.jpg?1)](http://rubyonrails.org/)
21
+ [![roda](https://s3.amazonaws.com/instana/roda-logo.png?1)](http://roda.jeremyevans.net/)
22
+ [![cuba](https://s3.amazonaws.com/instana/cuba-logo.png?1)](http://cuba.is/)
23
+ [![sinatra](https://s3.amazonaws.com/instana/sinatra-logo.png?1)](http://www.sinatrarb.com/)
24
+ [![padrino](https://s3.amazonaws.com/instana/padrino-logo.png?1)](http://padrinorb.com/)
25
+ [![rack](https://s3.amazonaws.com/instana/rack-logo.png?1)](https://rack.github.io/)
26
26
 
27
27
  ## Installation
28
28
 
@@ -46,70 +46,7 @@ The instana gem is a zero configuration tool that will automatically collect key
46
46
 
47
47
  ## Configuration
48
48
 
49
- Although the gem has no configuration required for out of the box metrics and tracing, components can be configured if needed.
50
-
51
- ### Agent Reporting
52
-
53
- This agent spawns a lightweight background thread to periodically collect and report metrics and traces. Be default, this uses a standard Ruby thread. If you wish to have greater control and potentially boot the agent reporting manually in an alternative thread system (such as actor based threads), you can do so with the following:
54
-
55
- ```Ruby
56
- gem "instana", :require => "instana/setup"
57
- ```
58
-
59
- ...then in the background thread of your choice simply call:
60
-
61
- ```Ruby
62
- ::Instana.agent.start
63
- ```
64
-
65
- Note that this call is blocking. It kicks off a loop of timers that periodically collects and reports metrics and trace data. This should only be called from inside an already initialized background thread:
66
-
67
- ```Ruby
68
- Thread.new do
69
- ::Instana.agent.start
70
- end
71
- ```
72
-
73
- #### Caveat
74
-
75
- In the case of forking webservers such as Unicorn or Puma in clustered mode, the agent detects the pid change and re-spawns the background thread. If you are managing the background thread yourself with the steps above _and_ you are using a forking webserver (or anything else that may fork the original process), you should also do the following.
76
-
77
- When a fork is detected, the agent handles the re-initialization and then calls `::Agent.instana.spawn_background_thread`. This by default uses the standard `Thread.new`. If you wish to control this, you should override this method by re-defining that method. For example:
78
-
79
- ```ruby
80
- # This method can be overridden with the following:
81
- #
82
- module Instana
83
- class Agent
84
- def spawn_background_thread
85
- # start/identify custom thread
86
- ::Instana.agent.start
87
- end
88
- end
89
- end
90
- ```
91
-
92
- ### Components
93
-
94
- Individual components can be disabled with a local config.
95
-
96
- To disable a single component in the gem, you can disable a single component with the following code:
97
-
98
- ```Ruby
99
- ::Instana.config[:metrics][:gc][:enabled] = false
100
- ```
101
- Current components are `:gc`, `:memory` and `:thread`.
102
-
103
- ### Rack Middleware
104
-
105
- This gem will detect and automagically insert the Instana Rack middleware into the middleware stack when a [supported framework](https://instana.atlassian.net/wiki/display/DOCS/Ruby) is present. We are currently adding support for more frameworks. If you are using a yet to be instrumented framework, you can insert the Instana Rack middleware with the following:
106
-
107
- ```Ruby
108
- require "instana/rack"
109
- config.middleware.use ::Instana::Rack
110
- ```
111
-
112
- ...or whatever specific middleware call is appropriate for your framework.
49
+ Although the gem has no configuration required for out of the box metrics and tracing, components can be configured if needed. See [Configuration.md](https://github.com/instana/ruby-sensor/blob/master/Configuration.md).
113
50
 
114
51
  ## Documentation
115
52
 
data/lib/instana.rb CHANGED
@@ -3,7 +3,7 @@ require "instana/setup"
3
3
  # Boot the instana agent background thread. If you wish to have greater
4
4
  # control on the where and which thread this is run in, instead use
5
5
  #
6
- # gem "instana", :require "instana/setup"
6
+ # gem "instana", :require => "instana/setup"
7
7
  #
8
8
  # ...and manually call ::Instana.agent.start in the thread
9
9
  # of your choice
@@ -28,7 +28,7 @@ module Instana
28
28
  end
29
29
 
30
30
  # Report all the collected goodies
31
- ::Instana.agent.report_entity_data(payload)
31
+ ::Instana.agent.report_entity_data(payload) unless ENV['INSTANA_GEM_TEST']
32
32
  end
33
33
 
34
34
  end
@@ -9,6 +9,9 @@ module Instana
9
9
  @config[:metrics][:gc] = { :enabled => true }
10
10
  @config[:metrics][:memory] = { :enabled => true }
11
11
  @config[:metrics][:thread] = { :enabled => true }
12
+
13
+ # HTTP Clients
14
+ @config[:excon] = { :enabled => true }
12
15
  end
13
16
 
14
17
  def [](key)
@@ -0,0 +1,64 @@
1
+ if defined?(::Excon) && ::Instana.config[:excon][:enabled]
2
+ module Instana
3
+ module Instrumentation
4
+ class Excon < ::Excon::Middleware::Base
5
+ def request_call(datum)
6
+ return @stack.request_call(datum) unless ::Instana.tracer.tracing?
7
+
8
+ payload = { :http => {} }
9
+ path = datum[:path].split('?').first
10
+ payload[:http][:url] = "#{datum[:connection].instance_variable_get(:@socket_key)}#{path}"
11
+ payload[:http][:method] = datum[:method] if datum.key?(:method)
12
+
13
+ if datum[:pipeline] == true
14
+ # Pass the context along in the datum so we get back on response
15
+ # and can close out the async span
16
+ datum[:instana_context] = ::Instana.tracer.log_async_entry(:excon, payload)
17
+ else
18
+ ::Instana.tracer.log_entry(:excon, payload)
19
+ end
20
+
21
+ # Set request headers; encode IDs as hexadecimal strings
22
+ datum[:headers]['X-Instana-T'] = ::Instana.tracer.trace_id_header
23
+ datum[:headers]['X-Instana-S'] = ::Instana.tracer.span_id_header
24
+
25
+ @stack.request_call(datum)
26
+ end
27
+
28
+ def error_call(datum)
29
+ return @stack.error_call(datum) unless ::Instana.tracer.tracing?
30
+
31
+ if datum[:pipeline] == true
32
+ ::Instana.tracer.log_async_error(datum[:error], datum[:instana_context])
33
+ else
34
+ ::Instana.tracer.log_error(datum[:error])
35
+ end
36
+ @stack.error_call(datum)
37
+ end
38
+
39
+ def response_call(datum)
40
+ return @stack.response_call(datum) unless ::Instana.tracer.tracing?
41
+
42
+ result = @stack.response_call(datum)
43
+
44
+ status = datum[:status]
45
+ if !status && datum.key?(:response) && datum[:response].is_a?(Hash)
46
+ status = datum[:response][:status]
47
+ end
48
+
49
+ if datum[:pipeline] == true
50
+ # Pickup context of this async span from datum[:instana_id]
51
+ ::Instana.tracer.log_async_exit(:excon, { :http => {:status => status } }, datum[:instana_context])
52
+ else
53
+ ::Instana.tracer.log_exit(:excon, { :http => {:status => status } })
54
+ end
55
+ result
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ ::Instana.logger.warn "Instrumenting excon"
62
+ ::Excon.defaults[:middlewares].unshift(::Instana::Instrumentation::Excon)
63
+ end
64
+
@@ -19,7 +19,7 @@ module Instana
19
19
  incoming_context = {}
20
20
  if env.key?('HTTP_X_INSTANA_T')
21
21
  incoming_context[:trace_id] = ::Instana.tracer.header_to_id(env['HTTP_X_INSTANA_T'])
22
- incoming_context[:parent_id] = ::Instana.tracer.header_to_id(env['HTTP_X_INSTANA_S']) if env.key?('HTTP_X_INSTANA_S')
22
+ incoming_context[:span_id] = ::Instana.tracer.header_to_id(env['HTTP_X_INSTANA_S']) if env.key?('HTTP_X_INSTANA_S')
23
23
  incoming_context[:level] = env['HTTP_X_INSTANA_L'] if env.key?('HTTP_X_INSTANA_L')
24
24
  end
25
25
 
@@ -0,0 +1,34 @@
1
+ module Instana
2
+ module Instrumentation
3
+ module RestClientRequest
4
+ def self.included(klass)
5
+ if klass.method_defined?(:execute)
6
+ klass.class_eval do
7
+ alias execute_without_instana execute
8
+ alias execute execute_with_instana
9
+ end
10
+ end
11
+ end
12
+
13
+ def execute_with_instana & block
14
+ # Since RestClient uses net/http under the covers, we just
15
+ # provide span visibility here. HTTP related KVs are reported
16
+ # in the Net::HTTP instrumentation
17
+ ::Instana.tracer.log_entry(:'rest-client')
18
+
19
+ execute_without_instana(&block)
20
+ rescue => e
21
+ ::Instana.tracer.log_error(e)
22
+ raise
23
+ ensure
24
+ ::Instana.tracer.log_exit(:'rest-client')
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+
31
+ if defined?(::RestClient::Request)
32
+ ::Instana.logger.warn "Instrumenting RestClient"
33
+ ::RestClient::Request.send(:include, ::Instana::Instrumentation::RestClientRequest)
34
+ end
@@ -2,7 +2,7 @@ require "logger"
2
2
 
3
3
  module Instana
4
4
  class XLogger < Logger
5
- LEVELS = [:agent, :agent_comm].freeze
5
+ LEVELS = [:agent, :agent_comm, :trace].freeze
6
6
  STAMP = "Instana: ".freeze
7
7
 
8
8
  def initialize(*args)
@@ -12,6 +12,12 @@ module Instana
12
12
  super(*args)
13
13
  end
14
14
 
15
+ # Sets the debug level for this logger. The debug level is broken up into various
16
+ # sub-levels as defined in LEVELS.
17
+ #
18
+ # To use:
19
+ # ::Instana.logger.debug_level = [:agent_comm, :trace]
20
+ #
15
21
  def debug_level=(levels)
16
22
  LEVELS.each do |l|
17
23
  instance_variable_set("@level_#{l}", false)
@@ -34,6 +40,11 @@ module Instana
34
40
  self.debug(msg)
35
41
  end
36
42
 
43
+ def trace(msg)
44
+ return unless @level_trace
45
+ self.debug(msg)
46
+ end
47
+
37
48
  def error(msg)
38
49
  super(STAMP + msg)
39
50
  end
@@ -9,7 +9,7 @@ module Instana
9
9
  thread_local :current_trace
10
10
 
11
11
  #######################################
12
- # Tracing blocks helper methods
12
+ # Tracing blocks API methods
13
13
  #######################################
14
14
 
15
15
  # Will start a new trace or continue an on-going one (such as
@@ -18,9 +18,9 @@ module Instana
18
18
  # @param name [String] the name of the span to start
19
19
  # @param kvs [Hash] list of key values to be reported in the span
20
20
  # @param incoming_context [Hash] specifies the incoming context. At a
21
- # minimum, it should specify :trace_id and :parent_id from the following:
21
+ # minimum, it should specify :trace_id and :span_id from the following:
22
22
  # @:trace_id the trace ID (must be an unsigned hex-string)
23
- # :parent_id the ID of the parent span (must be an unsigned hex-string)
23
+ # :span_id the ID of the parent span (must be an unsigned hex-string)
24
24
  # :level specifies data collection level (optional)
25
25
  #
26
26
  def start_or_continue_trace(name, kvs = {}, incoming_context = {}, &block)
@@ -35,6 +35,12 @@ module Instana
35
35
 
36
36
  # Trace a block of code within the context of the exiting trace
37
37
  #
38
+ # Example usage:
39
+ #
40
+ # ::Instana.tracer.trace(:dbwork, { :db_name => @db.name }) do
41
+ # @db.select(1)
42
+ # end
43
+ #
38
44
  # @param name [String] the name of the span to start
39
45
  # @param kvs [Hash] list of key values to be reported in this new span
40
46
  #
@@ -50,7 +56,7 @@ module Instana
50
56
  end
51
57
 
52
58
  #######################################
53
- # Lower level tracing methods
59
+ # Lower level tracing API methods
54
60
  #######################################
55
61
 
56
62
  # Will start a new trace or continue an on-going one (such as
@@ -59,9 +65,9 @@ module Instana
59
65
  # @param name [String] the name of the span to start
60
66
  # @param kvs [Hash] list of key values to be reported in the span
61
67
  # @param incoming_context [Hash] specifies the incoming context. At a
62
- # minimum, it should specify :trace_id and :parent_id from the following:
68
+ # minimum, it should specify :trace_id and :span_id from the following:
63
69
  # :trace_id the trace ID (must be an unsigned hex-string)
64
- # :parent_id the ID of the parent span (must be an unsigned hex-string)
70
+ # :span_id the ID of the parent span (must be an unsigned hex-string)
65
71
  # :level specifies data collection level (optional)
66
72
  #
67
73
  def log_start_or_continue(name, kvs = {}, incoming_context = {})
@@ -98,7 +104,7 @@ module Instana
98
104
  self.current_trace.add_error(e)
99
105
  end
100
106
 
101
- # Will close out the current span
107
+ # Closes out the current span
102
108
  #
103
109
  # @note `name` isn't really required but helps keep sanity that
104
110
  # we're closing out the span that we really want to close out.
@@ -124,10 +130,109 @@ module Instana
124
130
  return unless tracing?
125
131
 
126
132
  self.current_trace.finish(kvs)
127
- Instana.processor.add(self.current_trace)
133
+
134
+ if !self.current_trace.has_async? ||
135
+ (self.current_trace.has_async? && self.current_trace.complete?)
136
+ Instana.processor.add(self.current_trace)
137
+ else
138
+ # This trace still has outstanding/uncompleted asynchronous spans.
139
+ # Put it in the staging queue until the async span closes out or
140
+ # 5 minutes has passed. Whichever comes first.
141
+ Instana.processor.stage(self.current_trace)
142
+ end
128
143
  self.current_trace = nil
129
144
  end
130
145
 
146
+ ###########################################################################
147
+ # Asynchronous API methods
148
+ ###########################################################################
149
+
150
+ # Starts a new asynchronous span on the current trace.
151
+ #
152
+ # @param name [String] the name of the span to create
153
+ # @param kvs [Hash] list of key values to be reported in the span
154
+ #
155
+ # @return [Hash] the context: Trace ID and Span ID in the form of
156
+ # :trace_id => 12345
157
+ # :span_id => 12345
158
+ #
159
+ def log_async_entry(name, kvs, incoming_context = nil)
160
+ return unless tracing?
161
+ self.current_trace.new_async_span(name, kvs)
162
+ end
163
+
164
+ # Add info to an asynchronous span
165
+ #
166
+ # @param kvs [Hash] list of key values to be reported in the span
167
+ # @param t_context [Hash] the Trace ID and Span ID in the form of
168
+ # :trace_id => 12345
169
+ # :span_id => 12345
170
+ # This can be retrieved by using ::Instana.tracer.context
171
+ #
172
+ def log_async_info(kvs, ids)
173
+ # Asynchronous spans can persist longer than the parent
174
+ # trace. With the trace ID, we check the current trace
175
+ # but otherwise, we search staged traces.
176
+
177
+ if tracing? && self.current_trace.id == ids[:trace_id]
178
+ self.current_trace.add_async_info(kvs, ids)
179
+ else
180
+ trace = ::Instana.processor.staged_trace(ids)
181
+ trace.add_async_info(kvs, ids)
182
+ end
183
+ end
184
+
185
+ # Add an error to an asynchronous span
186
+ #
187
+ # @param e [Exception] Add exception to the current span
188
+ # @param ids [Hash] the Trace ID and Span ID in the form of
189
+ # :trace_id => 12345
190
+ # :span_id => 12345
191
+ #
192
+ def log_async_error(e, ids)
193
+ # Asynchronous spans can persist longer than the parent
194
+ # trace. With the trace ID, we check the current trace
195
+ # but otherwise, we search staged traces.
196
+
197
+ if tracing? && self.current_trace.id == ids[:trace_id]
198
+ self.current_trace.add_async_error(e, ids)
199
+ else
200
+ trace = ::Instana.processor.staged_trace(ids)
201
+ trace.add_async_error(e, ids)
202
+ end
203
+ end
204
+
205
+ # Closes out an asynchronous span
206
+ #
207
+ # @param name [String] the name of the async span to exit (close out)
208
+ # @param kvs [Hash] list of key values to be reported in the span
209
+ # @param ids [Hash] the Trace ID and Span ID in the form of
210
+ # :trace_id => 12345
211
+ # :span_id => 12345
212
+ #
213
+ def log_async_exit(name, kvs, ids)
214
+ # An asynchronous span can end after the current trace has
215
+ # already completed so we make sure that we end the span
216
+ # on the right trace.
217
+
218
+ if tracing? && (self.current_trace.id == ids[:trace_id])
219
+ self.current_trace.end_async_span(kvs, ids)
220
+ else
221
+ # Different trace from current so find the staged trace
222
+ # and close out the span on it.
223
+ trace = ::Instana.processor.staged_trace(ids)
224
+ if trace
225
+ trace.end_async_span(kvs, ids)
226
+ else
227
+ ::Instana.logger.debug "log_async_exit: Couldn't find staged trace. #{ids.inspect}"
228
+ end
229
+ end
230
+ end
231
+
232
+ ###########################################################################
233
+ # Helper methods
234
+ ###########################################################################
235
+
131
236
  # Indicates if we're are currently in the process of
132
237
  # collecting a trace. This is false when the host agent isn
133
238
  # available.
@@ -141,6 +246,31 @@ module Instana
141
246
  self.current_trace ? true : false
142
247
  end
143
248
 
249
+ # Retrieve the current context of the tracer.
250
+ #
251
+ def context
252
+ { :trace_id => self.current_trace.id,
253
+ :span_id => self.current_trace.current_span_id }
254
+ end
255
+
256
+ # Take the current trace_id and convert it to a header compatible
257
+ # format.
258
+ #
259
+ # @return [String] a hexadecimal representation of the current trace ID
260
+ #
261
+ def trace_id_header
262
+ id_to_header(trace_id)
263
+ end
264
+
265
+ # Take the current span_id and convert it to a header compatible
266
+ # formate.
267
+ #
268
+ # @return [String] a hexadecimal representation of the current span ID
269
+ #
270
+ def span_id_header
271
+ id_to_header(span_id)
272
+ end
273
+
144
274
  # Convert an ID to a value appropriate to pass in a header.
145
275
  #
146
276
  # @param id [Integer] the id to be converted
@@ -4,7 +4,20 @@ module Instana
4
4
  class Processor
5
5
 
6
6
  def initialize
7
+ # The main queue before being reported to the
8
+ # host agent. Traces in this queue are complete
9
+ # and ready to be sent.
7
10
  @queue = Queue.new
11
+
12
+ # The staging queue that holds traces that have completed
13
+ # but still have outstanding async spans.
14
+ # Traces that have been in this queue for more than
15
+ # 5 minutes are discarded.
16
+ @staging_queue = Set.new
17
+
18
+ # No access to the @staging_queue until this lock
19
+ # is taken.
20
+ @staging_lock = Mutex.new
8
21
  end
9
22
 
10
23
  # Adds a trace to the queue to be processed and
@@ -12,9 +25,41 @@ module Instana
12
25
  #
13
26
  # @param [Trace] the trace to be added to the queue
14
27
  def add(trace)
28
+ ::Instana.logger.trace("Queuing completed trace id: #{trace.id}")
15
29
  @queue.push(trace)
16
30
  end
17
31
 
32
+ # Adds a trace to the staging queue.
33
+ #
34
+ # @param [Trace] the trace to be added to the queue
35
+ def stage(trace)
36
+ ::Instana.logger.trace("Staging incomplete trace id: #{trace.id}")
37
+ @staging_queue.add(trace)
38
+ end
39
+
40
+ # This will run through the staged traces (if any) to find
41
+ # completed or timed out incompleted traces. Completed traces will
42
+ # be added to the main @queue. Timed out traces will be discarded
43
+ #
44
+ def process_staged
45
+ @staging_lock.synchronize {
46
+ if @staging_queue.size > 0
47
+ @staging_queue.delete_if do |t|
48
+ if t.complete?
49
+ ::Instana.logger.trace("Moving staged complete trace to main queue: #{t.id}")
50
+ add(t)
51
+ true
52
+ elsif t.discard?
53
+ ::Instana.logger.debug("Discarding trace with uncompleted async spans over 5 mins old. id: #{t.id}")
54
+ true
55
+ else
56
+ false
57
+ end
58
+ end
59
+ end
60
+ }
61
+ end
62
+
18
63
  ##
19
64
  # send
20
65
  #
@@ -27,20 +72,21 @@ module Instana
27
72
  # - Prevent another run of the timer while this is running
28
73
  #
29
74
  def send
30
- return if @queue.empty?
75
+ return if @queue.empty? || ENV['INSTANA_GEM_TEST']
31
76
 
32
77
  size = @queue.size
33
78
  if size > 100
34
79
  Instana.logger.debug "Trace queue is #{size}"
35
80
  end
36
81
 
37
- ::Instana.agent.report_spans(queued_spans)
38
- end
82
+ # Scan for any staged but incomplete traces that have now
83
+ # completed.
84
+ process_staged
39
85
 
40
- # Get the number traces currently in the queue
41
- #
42
- def queue_count
43
- @queue.size
86
+ # Retrieve all spans for queued traces
87
+ spans = queued_spans
88
+
89
+ ::Instana.agent.report_spans(spans)
44
90
  end
45
91
 
46
92
  # Retrieves all of the traces in @queue and returns
@@ -49,6 +95,8 @@ module Instana
49
95
  # Note that traces retrieved with this method are removed
50
96
  # entirely from the queue.
51
97
  #
98
+ # @return [Array] An array of [Span] or empty
99
+ #
52
100
  def queued_spans
53
101
  return [] if @queue.empty?
54
102
 
@@ -67,6 +115,8 @@ module Instana
67
115
  # Note that traces retrieved with this method are removed
68
116
  # entirely from the queue.
69
117
  #
118
+ # @return [Array] An array of [Trace] or empty
119
+ #
70
120
  def queued_traces
71
121
  return [] if @queue.empty?
72
122
 
@@ -78,16 +128,69 @@ module Instana
78
128
  traces
79
129
  end
80
130
 
81
- # Removes all traces from the @queue. Used in the
82
- # test suite.
131
+ # Retrieves a all staged traces from the staging queue. Staged traces
132
+ # are traces that have completed but may have outstanding
133
+ # asynchronous spans.
83
134
  #
84
- def clear!
85
- return [] if @queue.empty?
135
+ # @return [Array]
136
+ #
137
+ def staged_traces
138
+ traces = nil
139
+ @staging_lock.synchronize {
140
+ traces = @staging_queue.to_a
141
+ @staging_queue.clear
142
+ }
143
+ traces
144
+ end
86
145
 
146
+ # Retrieves a single staged trace from the staging queue. Staged traces
147
+ # are traces that have completed but may have outstanding
148
+ # asynchronous spans.
149
+ #
150
+ # @param ids [Hash] the Trace ID and Span ID in the form of
151
+ # :trace_id => 12345
152
+ # :span_id => 12345
153
+ #
154
+ def staged_trace(ids)
155
+ candidate = nil
156
+ @staging_lock.synchronize {
157
+ @staging_queue.each do |trace|
158
+ if trace.id == ids[:trace_id]
159
+ candidate = trace
160
+ end
161
+ end
162
+ }
163
+ unless candidate
164
+ ::Instana.logger.trace("Couldn't find staged trace with trace_id: #{ids[:trace_id]}")
165
+ end
166
+ candidate
167
+ end
168
+
169
+ # Get the number traces currently in the queue
170
+ #
171
+ # @return [Integer] the queue size
172
+ #
173
+ def queue_count
174
+ @queue.size
175
+ end
176
+
177
+ # Get the number traces currently in the staging queue
178
+ #
179
+ # @return [Integer] the queue size
180
+ #
181
+ def staged_count
182
+ @staging_queue.size
183
+ end
184
+
185
+ # Removes all traces from the @queue and @staging_queue. Used in the
186
+ # test suite to reset state.
187
+ #
188
+ def clear!
87
189
  until @queue.empty? do
88
190
  # Non-blocking pop; ignore exception
89
191
  @queue.pop(true) rescue nil
90
192
  end
193
+ @staging_queue.clear
91
194
  end
92
195
  end
93
196
  end
@@ -14,6 +14,18 @@ module Instana
14
14
  @data[:p]
15
15
  end
16
16
 
17
+ def name
18
+ if custom?
19
+ @data[:data][:sdk][:name]
20
+ else
21
+ @data[:n]
22
+ end
23
+ end
24
+
25
+ def duration
26
+ @data[:d]
27
+ end
28
+
17
29
  def is_root?
18
30
  @data[:s] == @data[:t]
19
31
  end
@@ -1,6 +1,6 @@
1
1
  module Instana
2
2
  class Trace
3
- REGISTERED_SPANS = [ :rack, :'net-http' ]
3
+ REGISTERED_SPANS = [ :rack, :'net-http', :excon ]
4
4
 
5
5
  # @return [Integer] the ID for this trace
6
6
  attr_reader :id
@@ -14,9 +14,9 @@ module Instana
14
14
  # @param name [String] the name of the span to start
15
15
  # @param kvs [Hash] list of key values to be reported in the span
16
16
  # @param incoming_context [Hash] specifies the incoming context. At a
17
- # minimum, it should specify :trace_id and :parent_id from the following:
17
+ # minimum, it should specify :trace_id and :span_id from the following:
18
18
  # :trace_id the trace ID (must be an unsigned hex-string)
19
- # :parent_id the ID of the parent span (must be an unsigned hex-string)
19
+ # :span_id the ID of the parent span (must be an unsigned hex-string)
20
20
  # :level specifies data collection level (optional)
21
21
  #
22
22
  def initialize(name, kvs = {}, incoming_context = {})
@@ -30,6 +30,13 @@ module Instana
30
30
  # Generate a random 64bit ID for this trace
31
31
  @id = generate_id
32
32
 
33
+ # Indicates the time when this trace was started. Used to timeout
34
+ # traces that have asynchronous spans that never close out.
35
+ @started_at = Time.now
36
+
37
+ # Indicates if this trace has any asynchronous spans within it
38
+ @has_async = false
39
+
33
40
  # This is a new trace so open the first span with the proper
34
41
  # root span IDs.
35
42
  @current_span = Span.new({
@@ -41,7 +48,7 @@ module Instana
41
48
 
42
49
  # Check for custom tracing
43
50
  if !REGISTERED_SPANS.include?(name.to_sym)
44
- configure_custom_span(name, kvs)
51
+ configure_custom_span(nil, name, kvs)
45
52
  else
46
53
  @current_span[:n] = name.to_sym
47
54
  @current_span[:data] = kvs
@@ -55,7 +62,7 @@ module Instana
55
62
  else
56
63
  @id = incoming_context[:trace_id]
57
64
  @current_span[:t] = incoming_context[:trace_id]
58
- @current_span[:p] = incoming_context[:parent_id]
65
+ @current_span[:p] = incoming_context[:span_id]
59
66
  end
60
67
 
61
68
  @spans.add(@current_span)
@@ -72,7 +79,7 @@ module Instana
72
79
  new_span = Span.new({
73
80
  :s => generate_id, # Span ID
74
81
  :t => @id, # Trace ID (same as :s for root span)
75
- :p => @current_span[:s], # Parent ID
82
+ :p => @current_span.id, # Parent ID
76
83
  :ts => ts_now, # Timestamp
77
84
  :ta => :ruby, # Agent
78
85
  :f => { :e => Process.pid, :h => :agent_id } # Entity Source
@@ -84,7 +91,7 @@ module Instana
84
91
 
85
92
  # Check for custom tracing
86
93
  if !REGISTERED_SPANS.include?(name.to_sym)
87
- configure_custom_span(name, kvs)
94
+ configure_custom_span(nil, name, kvs)
88
95
  else
89
96
  @current_span[:n] = name.to_sym
90
97
  @current_span[:data] = kvs
@@ -93,17 +100,28 @@ module Instana
93
100
 
94
101
  # Add KVs to the current span
95
102
  #
103
+ # @param span [Span] the span to add kvs to or otherwise the current span
96
104
  # @param kvs [Hash] list of key values to be reported in the span
97
105
  #
98
- def add_info(kvs)
99
- if @current_span.custom?
100
- if @current_span[:data][:sdk].key?(:custom)
101
- @current_span[:data][:sdk][:custom].merge!(kvs)
106
+ def add_info(kvs, span = nil)
107
+ span ||= @current_span
108
+
109
+ if span.custom?
110
+ if span[:data][:sdk].key?(:custom)
111
+ span[:data][:sdk][:custom].merge!(kvs)
102
112
  else
103
- @current_span[:data][:sdk][:custom] = kvs
113
+ span[:data][:sdk][:custom] = kvs
104
114
  end
105
115
  else
106
- @current_span[:data].merge!(kvs)
116
+ kvs.each_pair do |k,v|
117
+ if !span[:data].key?(k)
118
+ span[:data][k] = v
119
+ elsif v.is_a?(Hash) && span[:data][k].is_a?(Hash)
120
+ span[:data][k].merge!(v)
121
+ else
122
+ span[:data][k] = v
123
+ end
124
+ end
107
125
  end
108
126
  end
109
127
 
@@ -111,26 +129,16 @@ module Instana
111
129
  #
112
130
  # @param e [Exception] Add exception to the current span
113
131
  #
114
- def add_error(e)
115
- @current_span[:error] = true
132
+ def add_error(e, span = nil)
133
+ span ||= @current_span
134
+
135
+ span[:error] = true
116
136
 
117
- if @current_span.key?(:ec)
118
- @current_span[:ec] = @current_span[:ec] + 1
137
+ if span.key?(:ec)
138
+ span[:ec] = span[:ec] + 1
119
139
  else
120
- @current_span[:ec] = 1
140
+ span[:ec] = 1
121
141
  end
122
-
123
- #if e.backtrace && e.backtrace.is_a?(Array)
124
- # @current_span[:stack] = []
125
- # e.backtrace.each do |x|
126
- # file, line, method = x.split(':')
127
- # @current_span[:stack] << {
128
- # :f => file,
129
- # :n => line
130
- # #:m => method
131
- # }
132
- # end
133
- #end
134
142
  end
135
143
 
136
144
  # Close out the current span and set the parent as
@@ -154,17 +162,124 @@ module Instana
154
162
  end_span(kvs)
155
163
  end
156
164
 
157
- # Indicates whether all seems ok with this
158
- # trace in it's current state. Should be only
159
- # called on finished traces.
165
+ ###########################################################################
166
+ # Asynchronous Methods
167
+ ###########################################################################
168
+
169
+ # Start a new asynchronous span
170
+ #
171
+ # The major differentiator between this method and simple new_span is that
172
+ # this method doesn't affect @current_trace and instead returns an
173
+ # ID pair that can be used later to close out the created async span.
174
+ #
175
+ # @param name [String] the name of the span to start
176
+ # @param kvs [Hash] list of key values to be reported in the span
177
+ #
178
+ def new_async_span(name, kvs)
179
+
180
+ new_span = Span.new({
181
+ :s => generate_id, # Span ID
182
+ :t => @id, # Trace ID (same as :s for root span)
183
+ :p => @current_span.id, # Parent ID
184
+ :ts => ts_now, # Timestamp
185
+ :ta => :ruby, # Agent
186
+ :async => true, # Asynchonous
187
+ :f => { :e => Process.pid, :h => :agent_id } # Entity Source
188
+ })
189
+
190
+ new_span.parent = @current_span
191
+ @has_async = true
192
+
193
+ # Check for custom tracing
194
+ if !REGISTERED_SPANS.include?(name.to_sym)
195
+ configure_custom_span(new_span, name, kvs)
196
+ else
197
+ new_span[:n] = name.to_sym
198
+ new_span[:data] = kvs
199
+ end
200
+
201
+ # Add the new span to the span collection
202
+ @spans.add(new_span)
203
+
204
+ { :trace_id => new_span[:t], :span_id => new_span.id }
205
+ end
206
+
207
+ # Log info into an asynchronous span
208
+ #
209
+ # @param kvs [Hash] list of key values to be reported in the span
210
+ # @param span [Span] the span to configure
211
+ #
212
+ def add_async_info(kvs, ids)
213
+ @spans.each do |span|
214
+ if span.id == ids[:span_id]
215
+ add_info(kvs, span)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Log an error into an asynchronous span
221
+ #
222
+ # @param span [Span] the span to configure
223
+ # @param e [Exception] Add exception to the current span
224
+ #
225
+ def add_async_error(e, ids)
226
+ @spans.each do |span|
227
+ add_error(e, span) if span.id == ids[:span_id]
228
+ end
229
+ end
230
+
231
+ # End an asynchronous span
232
+ #
233
+ # @param name [Symbol] the name of the span
234
+ # @param kvs [Hash] list of key values to be reported in the span
235
+ # @param ids [Hash] the Trace ID and Span ID in the form of
236
+ # :trace_id => 12345
237
+ # :span_id => 12345
238
+ #
239
+ def end_async_span(kvs = {}, ids)
240
+ @spans.each do |span|
241
+ if span.id == ids[:span_id]
242
+ span[:d] = ts_now - span[:ts]
243
+ add_info(kvs, span) unless kvs.empty?
244
+ end
245
+ end
246
+ end
247
+
248
+ ###########################################################################
249
+ # Validator and Helper Methods
250
+ ###########################################################################
251
+
252
+ # Indicates whether all seems ok with this trace in it's current state.
253
+ # Should be only called on finished traces.
160
254
  #
161
255
  # @return [Boolean] true or false on whether this trace is valid
162
256
  #
163
257
  def valid?
164
- # TODO
258
+ @spans.each do |span|
259
+ unless span.key?(:d)
260
+ return false
261
+ end
262
+ end
263
+ end
264
+
265
+ # Indicates if every span of this trace has completed. Useful when
266
+ # asynchronous spans potentially could run longer than the parent trace.
267
+ #
268
+ def complete?
269
+ @spans.each do |span|
270
+ if !span.duration
271
+ return false
272
+ end
273
+ end
165
274
  true
166
275
  end
167
276
 
277
+ # Indicates whether this trace has any asynchronous spans.
278
+ #
279
+ def has_async?
280
+ @has_async
281
+ end
282
+
168
283
  # Searches the set of spans and indicates if there
169
284
  # is an error logged in one of them.
170
285
  #
@@ -191,26 +306,84 @@ module Instana
191
306
  @current_span.id
192
307
  end
193
308
 
309
+ # Get the name of the current span. Supports both registered spans
310
+ # and custom sdk spans.
311
+ #
312
+ def current_span_name
313
+ @current_span.name
314
+ end
315
+
316
+ # Check if the current span has the name value of <name>
317
+ #
318
+ # @param name [Symbol] The name to be checked against.
319
+ #
320
+ # @return [Boolean]
321
+ #
322
+ def current_span_name?(name)
323
+ @current_span.name == name
324
+ end
325
+
326
+ # For traces that have asynchronous spans, this method indicates
327
+ # whether we have hit the timeout on waiting for those async
328
+ # spans to close out.
329
+ #
330
+ # @return [Boolean]
331
+ #
332
+ def discard?
333
+ # If this trace has async spans that have not closed
334
+ # out in 5 minutes, then it's discarded.
335
+ if has_async? && (Time.now.to_i - @started_at.to_i) > 601
336
+ return true
337
+ end
338
+ false
339
+ end
340
+
194
341
  private
195
342
 
196
343
  # Configure @current_span to be a custom span per the
197
344
  # SDK generic span type.
198
345
  #
199
- def configure_custom_span(name, kvs = {})
200
- @current_span[:n] = :sdk
201
- @current_span[:data] = { :sdk => { :name => name.to_sym } }
202
- @current_span[:data][:sdk][:type] = kvs.key?(:type) ? kvs[:type] : :local
346
+ # @param span [Span] the span to configure or nil
347
+ # @param name [String] name of the span
348
+ # @param kvs [Hash] list of key values to be reported in the span
349
+ #
350
+ def configure_custom_span(span, name, kvs = {})
351
+ span ||= @current_span
352
+
353
+ span[:n] = :sdk
354
+ span[:data] = { :sdk => { :name => name.to_sym } }
355
+ span[:data][:sdk][:type] = kvs.key?(:type) ? kvs[:type] : :local
203
356
 
204
357
  if kvs.key?(:arguments)
205
- @current_span[:data][:sdk][:arguments] = kvs[:arguments]
358
+ span[:data][:sdk][:arguments] = kvs[:arguments]
206
359
  end
207
360
 
208
361
  if kvs.key?(:return)
209
- @current_span[:data][:sdk][:return] = kvs[:return]
362
+ span[:data][:sdk][:return] = kvs[:return]
363
+ end
364
+ span[:data][:sdk][:custom] = kvs unless kvs.empty?
365
+ #span[:data][:sdk][:custom][:tags] = {}
366
+ #span[:data][:sdk][:custom][:logs] = {}
367
+ end
368
+
369
+ # Locates the span in the current_trace or
370
+ # in the staging queue. This is generally used by async
371
+ # operations.
372
+ #
373
+ # @param ids [Hash] the Trace ID and Span ID in the form of
374
+ # :trace_id => 12345
375
+ # :span_id => 12345
376
+ #
377
+ # @return [Span]
378
+ #
379
+ def find_span(ids)
380
+ if ids[:trace_id] == @id
381
+ @spans.each do |s|
382
+ return s if s[:s] == ids[:span_id]
383
+ end
384
+ else
385
+ #::Instana.processor.staged_trace(
210
386
  end
211
- @current_span[:data][:sdk][:custom] = kvs unless kvs.empty?
212
- #@current_span[:data][:sdk][:custom][:tags] = {}
213
- #@current_span[:data][:sdk][:custom][:logs] = {}
214
387
  end
215
388
 
216
389
  # Get the current time in milliseconds
data/lib/instana/util.rb CHANGED
@@ -1,52 +1,70 @@
1
1
  module Instana
2
2
  module Util
3
- ##
4
- # enforce_deltas
5
- #
6
- # Take two hashes, and make sure candidate does not have
7
- # any of the same values as `last`. We only report
8
- # when values change.
9
- #
10
- # Note this is not recursive, so only pass in the single
11
- # hashes that you want delta reporting with.
12
- #
13
- def self.enforce_deltas(candidate, last)
14
- return unless last.is_a?(Hash)
15
-
16
- candidate.each do |k,v|
17
- if candidate[k] == last[k]
18
- candidate.delete(k)
3
+ class << self
4
+ # An agnostic approach to method aliasing.
5
+ #
6
+ # @param klass [Object] The class or module that holds the method to be alias'd.
7
+ # @param method [Symbol] The name of the method to be aliased.
8
+ #
9
+ def method_alias(klass, method)
10
+ if klass.method_defined?(method.to_sym)
11
+
12
+ with = "#{method}_with_instana"
13
+ without = "#{method}_without_instana"
14
+
15
+ klass.class_eval do
16
+ alias_method without, method.to_s
17
+ alias_method method.to_s, with
18
+ end
19
+ else
20
+ ::Instana.logger.debug "No such method (#{method}) to alias on #{klass}"
19
21
  end
20
22
  end
21
- candidate
22
- end
23
- end
24
23
 
25
- ##
26
- # Debugging helper method
27
- #
28
- def self.pry!
29
- # Only valid for development or test environments
30
- #env = ENV['RACK_ENV'] || ENV['RAILS_ENV']
31
- #return unless %w(development, test).include? env
32
-
33
- if RUBY_VERSION > '1.8.7'
34
- require 'pry-byebug'
35
-
36
- if defined?(PryByebug)
37
- Pry.commands.alias_command 'c', 'continue'
38
- Pry.commands.alias_command 's', 'step'
39
- Pry.commands.alias_command 'n', 'next'
40
- Pry.commands.alias_command 'f', 'finish'
41
-
42
- Pry::Commands.command(/^$/, 'repeat last command') do
43
- _pry_.run_command Pry.history.to_a.last
24
+ # Take two hashes, and make sure candidate does not have
25
+ # any of the same values as `last`. We only report
26
+ # when values change.
27
+ #
28
+ # Note this is not recursive, so only pass in the single
29
+ # hashes that you want delta reporting with.
30
+ #
31
+ def enforce_deltas(candidate, last)
32
+ return unless last.is_a?(Hash)
33
+
34
+ candidate.each do |k,v|
35
+ if candidate[k] == last[k]
36
+ candidate.delete(k)
37
+ end
44
38
  end
39
+ candidate
45
40
  end
46
41
 
47
- binding.pry
48
- else
49
- require 'ruby-debug'; debugger
42
+ # Debugging helper method
43
+ #
44
+ def pry!
45
+ # Only valid for development or test environments
46
+ #env = ENV['RACK_ENV'] || ENV['RAILS_ENV']
47
+ #return unless %w(development, test).include? env
48
+
49
+ if RUBY_VERSION > '1.8.7'
50
+ require 'pry-byebug'
51
+
52
+ if defined?(PryByebug)
53
+ Pry.commands.alias_command 'c', 'continue'
54
+ Pry.commands.alias_command 's', 'step'
55
+ Pry.commands.alias_command 'n', 'next'
56
+ Pry.commands.alias_command 'f', 'finish'
57
+
58
+ Pry::Commands.command(/^$/, 'repeat last command') do
59
+ _pry_.run_command Pry.history.to_a.last
60
+ end
61
+ end
62
+
63
+ binding.pry
64
+ else
65
+ require 'ruby-debug'; debugger
66
+ end
67
+ end
50
68
  end
51
69
  end
52
70
  end
@@ -1,3 +1,3 @@
1
1
  module Instana
2
- VERSION = "0.12.1"
2
+ VERSION = "0.13.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instana
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Giacomo Lombardo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-02 00:00:00.000000000 Z
11
+ date: 2016-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -140,8 +140,10 @@ files:
140
140
  - lib/instana/frameworks/roda.rb
141
141
  - lib/instana/frameworks/sinatra.rb
142
142
  - lib/instana/instrumentation.rb
143
+ - lib/instana/instrumentation/excon.rb
143
144
  - lib/instana/instrumentation/net-http.rb
144
145
  - lib/instana/instrumentation/rack.rb
146
+ - lib/instana/instrumentation/rest-client.rb
145
147
  - lib/instana/logger.rb
146
148
  - lib/instana/rack.rb
147
149
  - lib/instana/setup.rb