instana 0.13.1 → 0.14.0

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: fff8c1b1ed4764301a7f1a45dec8973df279b2d8
4
- data.tar.gz: 8947536ba2a71f9916712d55980f77f57af8fde7
3
+ metadata.gz: 72f8c1fa60c3d84f52025ce41ebb803ce83b36d8
4
+ data.tar.gz: cb85eb7203cc6d028712351d642dc153a28a6468
5
5
  SHA512:
6
- metadata.gz: 1626e65166c81355c55105db86179e22c8cf9a1ae7ee7292e17574015f0acf2590eb0ed06b667d20d6d1294bd2d79eddaef584951119fc750880950401caa2ff
7
- data.tar.gz: 18c6581dbaece4419c1fd6edb59e4fabd30efab3789c24bc0b006420c3bce83f774ae4a239a37dc47b7c8ea1d0e99bf2f981c59d692b39b0e4ac00870010820e
6
+ metadata.gz: 21b2723fc3ee132868586e0f4dd0ad218dafc0a6e39c0fd56ff18c508fe984c32f1431134d1f467a91c687b2b3eb1f34976b76629a675bbe0d829011415e70ad
7
+ data.tar.gz: af75b8240335a5c8484400c6d1dca6197b0121d7a0d20569af8bbd0911ab336b68b935b352809f841a571b36cbbe789767dd99d54eeabdacb6084467d6a6c59c
data/README.md CHANGED
@@ -48,6 +48,10 @@ The instana gem is a zero configuration tool that will automatically collect key
48
48
 
49
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).
50
50
 
51
+ ## Tracing
52
+
53
+ See [Tracing.md](https://github.com/instana/ruby-sensor/blob/master/Tracing.md)
54
+
51
55
  ## Documentation
52
56
 
53
57
  You can find more documentation covering supported components and minimum versions in the Instana [documentation portal](https://instana.atlassian.net/wiki/display/DOCS/Ruby).
data/Tracing.md ADDED
@@ -0,0 +1,109 @@
1
+ # Tracing
2
+
3
+ Tracing with Instana is automatic but if you want even more visibility into custom code or some in-house
4
+ component, you can use the following API to report additional trace data to Instana.
5
+
6
+ # The API
7
+
8
+ The Instana Ruby gem provides a simple to use API to trace any arbitrary part of your application.
9
+
10
+ To instrument a section of code, it can be simply done with:
11
+
12
+ ```Ruby
13
+ begin
14
+ ::Instana.tracer.log_entry(:mywork, { :helpful_kvs => @user.id })
15
+ # The code to be instrumented
16
+ @id = User.find_by_name('john.smith')
17
+ rescue => e
18
+ ::Instana.tracer.log_error(e)
19
+ ensure
20
+ ::Instana.tracer.log_exit(:mywork, { :found_id => @id })
21
+ end
22
+ ```
23
+
24
+ The above is a simple example but shows how easy it is to instrument any code you like. Instana will
25
+ take care of the rest.
26
+
27
+ See the [examples directory](https://github.com/instana/ruby-sensor/blob/master/examples/tracing.rb) for
28
+ an expanded view and quick cheat sheet on tracing.
29
+
30
+ # Asynchronous Tracing
31
+
32
+ Some operations that you want to trace might be asynchronous meaning that they may return immediately
33
+ but will still continue to work out of band. To do this, you can use the `log_async_*` related
34
+ tracing methods:
35
+
36
+ ```Ruby
37
+ ::Instana.tracer.log_entry(:prep_job, { :helpful_kvs => @job.name })
38
+
39
+ http_ops = {:get => "/", :post => "/post_data"}
40
+
41
+ cb_block = Proc.new do |response, payload|
42
+ # The callback block that is invoked on HTTP response (payload == t_context)
43
+ #
44
+ # process response
45
+ #
46
+ ::Instana.tracer.log_async_exit(:http_op, :status => response.status, payload)
47
+ end
48
+
49
+ http_ops.each do |op|
50
+ t_context = ::Instana.tracer.log_async_entry(:http_op)
51
+
52
+ # Example op that returns immediately
53
+ request_id = connection.async_request(op, cb_block, t_context)
54
+
55
+ ::Instana.tracer.log_async_info({:request_id => request_id}, t_context)
56
+ end
57
+ ```
58
+
59
+ # Carrying Context into New Threads
60
+
61
+ Tracing is thread local. If you spawn a new thread the context must be carried to that new thread and then picked up.
62
+
63
+ ```Ruby
64
+ # Get the tracing context
65
+ t_context = ::Instana.tracer.context
66
+
67
+ # Spawn new thread
68
+ Thread.new do
69
+ # Pickup context in this thread with `t_context`
70
+ ::Instana.tracer.log_start_or_continue(:async_thread, { :async_start => 1 }, t_context)
71
+
72
+ # Continue tracing work as usual
73
+ begin
74
+ ::Instana.tracer.log_entry(:mywork, { :helpful_kvs => @user.id })
75
+ # The code to be instrumented
76
+ @id = User.find_by_name('john.smith')
77
+ rescue => e
78
+ ::Instana.tracer.log_error(e)
79
+ ensure
80
+ ::Instana.tracer.log_exit(:mywork, { :found_id => @id })
81
+ end
82
+ end
83
+ ```
84
+ # Tracing Jobs Scheduled for Later
85
+
86
+ Jobs that are queued to be run later can be instrumented as such:
87
+
88
+ ```Ruby
89
+ ::Instana.tracer.log_entry(:prep_job, { :job_name => @job.name })
90
+
91
+ # Get the current tracing context
92
+ t_context = ::Instana.tracer.context
93
+
94
+ # The Async proc (job) that will be executed out of band.
95
+ block = Proc.new do
96
+ # This will pickup context and link the two traces (root + job)
97
+ t_context = ::Instana.tracer.log_start_or_continue_trace(:my_async_op, { :helpful_kvs => true }, t_context)
98
+ #
99
+ # Some Asynchronous work to be done
100
+ #
101
+ ::Instana.tracer.log_info({:job_name => Job.get(id).name})
102
+ # More Asynchronous work
103
+ ::Instana.tracer.log_end(:my_async_op, { :job_success => true })
104
+ end
105
+
106
+ MyClass.run_in_5_minutes(block)
107
+
108
+ ::Instana.tracer.log_exit(:prep_job, { :prep_successful => true })
109
+ ```
data/lib/instana/agent.rb CHANGED
@@ -27,7 +27,7 @@ module Instana
27
27
 
28
28
  # Snapshot data is collected once per process but resent
29
29
  # every 10 minutes along side process metrics.
30
- @snapshot = take_snapshot
30
+ @snapshot = ::Instana::Util.take_snapshot
31
31
 
32
32
  # Set last snapshot to just under 10 minutes ago
33
33
  # so we send a snapshot sooner than later
@@ -82,14 +82,6 @@ module Instana
82
82
  @process[:report_pid] = nil
83
83
  end
84
84
 
85
- # Determine whether the pid has changed since Agent start.
86
- #
87
- # @ return [Boolean] true or false to indicate if forked
88
- #
89
- def forked?
90
- @pid != Process.pid
91
- end
92
-
93
85
  # Used post fork to re-initialize state and restart communications with
94
86
  # the host agent.
95
87
  #
@@ -137,7 +129,7 @@ module Instana
137
129
  # In case of failure, we try again in 30 seconds.
138
130
  @announce_timer = @timers.now_and_every(30) do
139
131
  if host_agent_ready? && announce_sensor
140
- ::Instana.logger.debug "Announce successful. Switching to metrics collection."
132
+ ::Instana.logger.warn "Host agent available. We're in business."
141
133
  transition_to(:announced)
142
134
  end
143
135
  end
@@ -147,11 +139,11 @@ module Instana
147
139
  # every ::Instana::Collector.interval seconds.
148
140
  @collect_timer = @timers.every(::Instana::Collector.interval) do
149
141
  if @state == :announced
150
- unless ::Instana::Collector.collect_and_report
142
+ if !::Instana::Collector.collect_and_report
151
143
  # If report has been failing for more than 1 minute,
152
144
  # fall back to unannounced state
153
145
  if (Time.now - @entity_last_seen) > 60
154
- ::Instana.logger.debug "Metrics reporting failed for >1 min. Falling back to unannounced state."
146
+ ::Instana.logger.warn "Host agent offline for >1 min. Going to sit in a corner..."
155
147
  transition_to(:unannounced)
156
148
  end
157
149
  end
@@ -188,31 +180,6 @@ module Instana
188
180
  end
189
181
  end
190
182
 
191
- # Indicates if the agent is ready to send metrics
192
- # and/or data.
193
- #
194
- def ready?
195
- # In test, we're always ready :-)
196
- return true if ENV['INSTANA_GEM_TEST']
197
-
198
- if forked?
199
- ::Instana.logger.agent "Instana: detected fork. Calling after_fork"
200
- after_fork
201
- end
202
-
203
- @state == :announced
204
- rescue => e
205
- Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
206
- Instana.logger.debug e.backtrace.join("\r\n")
207
- return false
208
- end
209
-
210
- # Returns the PID that we are reporting to
211
- #
212
- def report_pid
213
- @process[:report_pid]
214
- end
215
-
216
183
  # Collect process ID, name and arguments to notify
217
184
  # the host agent.
218
185
  #
@@ -270,14 +237,18 @@ module Instana
270
237
  response = make_host_agent_request(req)
271
238
 
272
239
  if response
273
- last_entity_response = response.code.to_i
240
+ if response.body.length > 2
241
+ # The host agent returned something indicating that is has a request for us that we
242
+ # need to process.
243
+ handle_response(response.body)
244
+ end
274
245
 
275
- if last_entity_response == 200
246
+ if response.code.to_i == 200
276
247
  @entity_last_seen = Time.now
277
248
  @last_snapshot = Time.now if with_snapshot
278
-
279
249
  return true
280
250
  end
251
+
281
252
  end
282
253
  false
283
254
  rescue => e
@@ -285,6 +256,32 @@ module Instana
285
256
  Instana.logger.debug e.backtrace.join("\r\n")
286
257
  end
287
258
 
259
+ # When a request is received by the host agent, it is sent here
260
+ # from processing and response.
261
+ #
262
+ # @param json_string [String] the request from the host agent
263
+ #
264
+ def handle_response(json_string)
265
+ their_request = JSON.parse(json_string).first
266
+
267
+ if their_request.key?("action")
268
+ if their_request["action"] == "ruby.source"
269
+ payload = ::Instana::Util.get_rb_source(their_request["args"]["file"])
270
+ else
271
+ payload = { :error => "Unrecognized action: #{their_request["action"]}. An newer Instana gem may be required for this. Current version: #{::Instana::VERSION}" }
272
+ end
273
+ else
274
+ payload = { :error => "Instana Ruby: No action specified in request." }
275
+ end
276
+
277
+ path = "com.instana.plugin.ruby/response.#{@process[:report_pid]}?messageId=#{URI.encode(their_request['messageId'])}"
278
+ uri = URI.parse("http://#{@host}:#{@port}/#{path}")
279
+ req = Net::HTTP::Post.new(uri)
280
+ req.body = payload.to_json
281
+ ::Instana.logger.agent_response "Responding to agent: #{req.inspect}"
282
+ make_host_agent_request(req)
283
+ end
284
+
288
285
  # Accept and report spans to the host agent.
289
286
  #
290
287
  # @param traces [Array] An array of [Span]
@@ -350,6 +347,31 @@ module Instana
350
347
  return false
351
348
  end
352
349
 
350
+ # Returns the PID that we are reporting to
351
+ #
352
+ def report_pid
353
+ @process[:report_pid]
354
+ end
355
+
356
+ # Indicates if the agent is ready to send metrics
357
+ # and/or data.
358
+ #
359
+ def ready?
360
+ # In test, we're always ready :-)
361
+ return true if ENV['INSTANA_GEM_TEST']
362
+
363
+ if forked?
364
+ ::Instana.logger.agent "Instana: detected fork. Calling after_fork"
365
+ after_fork
366
+ end
367
+
368
+ @state == :announced
369
+ rescue => e
370
+ Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
371
+ Instana.logger.debug e.backtrace.join("\r\n")
372
+ return false
373
+ end
374
+
353
375
  private
354
376
 
355
377
  # Handles any/all steps required in the transtion
@@ -424,41 +446,12 @@ module Instana
424
446
  v.match(/\d+/).to_s.to_i
425
447
  end
426
448
 
427
- # Method to collect up process info for snapshots. This
428
- # is generally used once per process.
449
+ # Determine whether the pid has changed since Agent start.
429
450
  #
430
- def take_snapshot
431
- data = {}
432
-
433
- data[:sensorVersion] = ::Instana::VERSION
434
- data[:ruby_version] = RUBY_VERSION
435
-
436
- # Since a snapshot is only taken on process boot,
437
- # this is ok here.
438
- data[:start_time] = Time.now.to_s
439
-
440
- # Framework Detection
441
- if defined?(::RailsLts::VERSION)
442
- data[:framework] = "Rails on Rails LTS-#{::RailsLts::VERSION}"
443
-
444
- elsif defined?(::Rails.version)
445
- data[:framework] = "Ruby on Rails #{::Rails.version}"
446
-
447
- elsif defined?(::Grape::VERSION)
448
- data[:framework] = "Grape #{::Grape::VERSION}"
449
-
450
- elsif defined?(::Padrino::VERSION)
451
- data[:framework] = "Padrino #{::Padrino::VERSION}"
452
-
453
- elsif defined?(::Sinatra::VERSION)
454
- data[:framework] = "Sinatra #{::Sinatra::VERSION}"
455
- end
456
-
457
- data
458
- rescue => e
459
- ::Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
460
- ::Instana.logger.debug e.backtrace.join("\r\n")
461
- return data
451
+ # @ return [Boolean] true or false to indicate if forked
452
+ #
453
+ def forked?
454
+ @pid != Process.pid
462
455
  end
463
456
  end
464
457
  end
@@ -15,6 +15,8 @@ module Instana
15
15
  # Run through each collector, let them collect up
16
16
  # data and then report what we have via the agent
17
17
  #
18
+ # @return Boolean true on success
19
+ #
18
20
  def collect_and_report
19
21
  payload = {}
20
22
 
@@ -27,10 +29,13 @@ module Instana
27
29
  end
28
30
  end
29
31
 
30
- # Report all the collected goodies
31
- ::Instana.agent.report_entity_data(payload) unless ENV['INSTANA_GEM_TEST']
32
+ if ENV['INSTANA_GEM_TEST']
33
+ true
34
+ else
35
+ # Report all the collected goodies
36
+ ::Instana.agent.report_entity_data(payload)
37
+ end
32
38
  end
33
-
34
39
  end
35
40
  end
36
41
  end
@@ -58,7 +58,7 @@ if defined?(::Excon) && ::Instana.config[:excon][:enabled]
58
58
  end
59
59
  end
60
60
 
61
- ::Instana.logger.warn "Instrumenting excon"
61
+ ::Instana.logger.warn "Instrumenting Excon"
62
62
  ::Excon.defaults[:middlewares].unshift(::Instana::Instrumentation::Excon)
63
63
  end
64
64
 
@@ -43,7 +43,7 @@ Net::HTTP.class_eval {
43
43
  ::Instana.tracer.log_exit(:'net-http')
44
44
  end
45
45
 
46
- Instana.logger.info "Instrumenting net/http"
46
+ Instana.logger.warn "Instrumenting Net::HTTP"
47
47
 
48
48
  alias request_without_instana request
49
49
  alias request request_with_instana
@@ -2,7 +2,7 @@ require "logger"
2
2
 
3
3
  module Instana
4
4
  class XLogger < Logger
5
- LEVELS = [:agent, :agent_comm, :trace].freeze
5
+ LEVELS = [:agent, :agent_comm, :trace, :agent_response].freeze
6
6
  STAMP = "Instana: ".freeze
7
7
 
8
8
  def initialize(*args)
@@ -13,7 +13,12 @@ module Instana
13
13
  end
14
14
 
15
15
  # Sets the debug level for this logger. The debug level is broken up into various
16
- # sub-levels as defined in LEVELS.
16
+ # sub-levels as defined in LEVELS:
17
+ #
18
+ # :agent - All agent related messages such as state & announcements
19
+ # :agent_comm - Output all payload comm sent between this Ruby gem and the host agent
20
+ # :trace - Output all traces reported to the host agent
21
+ # :agent_response - Outputs messages related to handling requests received by the host agent
17
22
  #
18
23
  # To use:
19
24
  # ::Instana.logger.debug_level = [:agent_comm, :trace]
@@ -45,6 +50,11 @@ module Instana
45
50
  self.debug(msg)
46
51
  end
47
52
 
53
+ def agent_response(msg)
54
+ return unless @level_agent_response
55
+ self.debug(msg)
56
+ end
57
+
48
58
  def error(msg)
49
59
  super(STAMP + msg)
50
60
  end
@@ -249,6 +249,8 @@ module Instana
249
249
  # Retrieve the current context of the tracer.
250
250
  #
251
251
  def context
252
+ return nil unless tracing?
253
+
252
254
  { :trace_id => self.current_trace.id,
253
255
  :span_id => self.current_trace.current_span_id }
254
256
  end
@@ -1,6 +1,8 @@
1
1
  module Instana
2
2
  class Trace
3
3
  REGISTERED_SPANS = [ :rack, :'net-http', :excon ]
4
+ ENTRY_SPANS = [ :rack ]
5
+ EXIT_SPANS = [ :'net-http', :excon ]
4
6
 
5
7
  # @return [Integer] the ID for this trace
6
8
  attr_reader :id
@@ -24,9 +26,6 @@ module Instana
24
26
  # up this trace.
25
27
  @spans = Set.new
26
28
 
27
- # The current active span
28
- @current_span = nil
29
-
30
29
  # Generate a random 64bit ID for this trace
31
30
  @id = generate_id
32
31
 
@@ -46,6 +45,9 @@ module Instana
46
45
  :f => { :e => ::Instana.agent.report_pid, :h => ::Instana.agent.agent_uuid } # Entity Source
47
46
  })
48
47
 
48
+ # For entry spans, add a backtrace fingerprint
49
+ add_stack(2) if ENTRY_SPANS.include?(name)
50
+
49
51
  # Check for custom tracing
50
52
  if !REGISTERED_SPANS.include?(name.to_sym)
51
53
  configure_custom_span(nil, name, kvs)
@@ -73,7 +75,7 @@ module Instana
73
75
  # @param name [String] the name of the span to start
74
76
  # @param kvs [Hash] list of key values to be reported in the span
75
77
  #
76
- def new_span(name, kvs)
78
+ def new_span(name, kvs = {})
77
79
  return unless @current_span
78
80
 
79
81
  new_span = Span.new({
@@ -96,6 +98,9 @@ module Instana
96
98
  @current_span[:n] = name.to_sym
97
99
  @current_span[:data] = kvs
98
100
  end
101
+
102
+ # Attach a backtrace to all exit spans
103
+ add_stack if EXIT_SPANS.include?(name)
99
104
  end
100
105
 
101
106
  # Add KVs to the current span
@@ -139,6 +144,10 @@ module Instana
139
144
  else
140
145
  span[:ec] = 1
141
146
  end
147
+
148
+ add_info(:log => {
149
+ :message => e.message,
150
+ :parameters => e.class })
142
151
  end
143
152
 
144
153
  # Close out the current span and set the parent as
@@ -198,6 +207,9 @@ module Instana
198
207
  new_span[:data] = kvs
199
208
  end
200
209
 
210
+ # Attach a backtrace to all exit spans
211
+ add_stack(nil, new_span) if EXIT_SPANS.include?(name)
212
+
201
213
  # Add the new span to the span collection
202
214
  @spans.add(new_span)
203
215
 
@@ -386,6 +398,40 @@ module Instana
386
398
  end
387
399
  end
388
400
 
401
+ # Adds a backtrace to the passed in span or on
402
+ # @current_span if not.
403
+ #
404
+ # @param limit [Integer] Limit the backtrace to the top <limit> frames
405
+ # @param span [Span] the span to add the backtrace to or if unspecified
406
+ # the current span
407
+ #
408
+ def add_stack(limit = nil, span = nil)
409
+ span ||= @current_span
410
+ span[:stack] = []
411
+ frame_count = 0
412
+
413
+ bt = Kernel.caller
414
+
415
+ bt.each do |i|
416
+ # If the stack has the full instana gem version in it's path
417
+ # then don't include that frame. Also don't exclude the Rack module.
418
+ if !i.match(/instana\/instrumentation\/rack.rb/).nil? ||
419
+ (i.match(::Instana::VERSION_FULL).nil? && i.match('lib/instana/').nil?)
420
+
421
+ break if limit && frame_count >= limit
422
+
423
+ x = i.split(':')
424
+
425
+ span[:stack] << {
426
+ :f => x[0],
427
+ :n => x[1],
428
+ :m => x[2]
429
+ }
430
+ frame_count = frame_count + 1 if limit
431
+ end
432
+ end
433
+ end
434
+
389
435
  # Get the current time in milliseconds
390
436
  #
391
437
  # @return [Integer] the current time in milliseconds
data/lib/instana/util.rb CHANGED
@@ -65,6 +65,58 @@ module Instana
65
65
  require 'ruby-debug'; debugger
66
66
  end
67
67
  end
68
+
69
+ # Retrieves and returns the source code for any ruby
70
+ # files requested by the UI via the host agent
71
+ #
72
+ # @param file [String] The fully qualified path to a file
73
+ #
74
+ def get_rb_source(file)
75
+ if (file =~ /.rb$/).nil?
76
+ { :error => "Only Ruby source files are allowed. (*.rb)" }
77
+ else
78
+ { :data => File.read(file) }
79
+ end
80
+ rescue => e
81
+ return { :error => e.inspect }
82
+ end
83
+
84
+ # Method to collect up process info for snapshots. This
85
+ # is generally used once per process.
86
+ #
87
+ def take_snapshot
88
+ data = {}
89
+
90
+ data[:sensorVersion] = ::Instana::VERSION
91
+ data[:ruby_version] = RUBY_VERSION
92
+
93
+ # Since a snapshot is only taken on process boot,
94
+ # this is ok here.
95
+ data[:start_time] = Time.now.to_s
96
+
97
+ # Framework Detection
98
+ if defined?(::RailsLts::VERSION)
99
+ data[:framework] = "Rails on Rails LTS-#{::RailsLts::VERSION}"
100
+
101
+ elsif defined?(::Rails.version)
102
+ data[:framework] = "Ruby on Rails #{::Rails.version}"
103
+
104
+ elsif defined?(::Grape::VERSION)
105
+ data[:framework] = "Grape #{::Grape::VERSION}"
106
+
107
+ elsif defined?(::Padrino::VERSION)
108
+ data[:framework] = "Padrino #{::Padrino::VERSION}"
109
+
110
+ elsif defined?(::Sinatra::VERSION)
111
+ data[:framework] = "Sinatra #{::Sinatra::VERSION}"
112
+ end
113
+
114
+ data
115
+ rescue => e
116
+ ::Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
117
+ ::Instana.logger.debug e.backtrace.join("\r\n")
118
+ return data
119
+ end
68
120
  end
69
121
  end
70
122
  end
@@ -1,3 +1,4 @@
1
1
  module Instana
2
- VERSION = "0.13.1"
2
+ VERSION = "0.14.0"
3
+ VERSION_FULL = "instana-#{VERSION}"
3
4
  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.13.1
4
+ version: 0.14.0
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-09 00:00:00.000000000 Z
11
+ date: 2016-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,6 +123,7 @@ files:
123
123
  - Gemfile
124
124
  - README.md
125
125
  - Rakefile
126
+ - Tracing.md
126
127
  - bin/console
127
128
  - bin/setup
128
129
  - examples/tracing.rb