instana 0.8.6 → 0.9.0.pre.slywolf3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 42faa21829471a9c09aef2c82c1bdf79cee9bb2a
4
- data.tar.gz: 7f4d358d30c2bb53b3dc97a39fbcdbfb950a0db5
3
+ metadata.gz: 23defe3c2a845b3d6b8c92da8a08989e0b65085a
4
+ data.tar.gz: 7f2064a969abebbf119a994981b3b0915f069e05
5
5
  SHA512:
6
- metadata.gz: d1b63875713111313dae3ba16b1374df8d3d70f78074c5617728eead258311d9a65cb537e03b44dc0f2e5a4b15fea7c7a0940f86957f78d91b29fd9bce81e5f9
7
- data.tar.gz: 602264940c06d3e4d8ebae06f56e91af16729ead2af6c3bf5add6b434d3b6bd6576d6388dbefdc8d963fe1f4f11e5310fdf4fa9c8a014a7dcd5d0840aadf61bc
6
+ metadata.gz: 6e31e6dd3ec166b5152519d53b65e6e5af003f22cbc28a5d56262eeb9cf15b11f288a25cd91714af94c2984c3aab644876844fdfc8be8d05332db18be376e4c4
7
+ data.tar.gz: f76e9995ff846378f4b95ee33677fe077395f3e705d37ec6a683749689363b110339a05193b51c71283a23eeed17413c40eebae8b9e7f139853bae443947915e
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .ruby-version
data/Gemfile CHANGED
@@ -5,7 +5,15 @@ group :development, :test do
5
5
  gem 'minitest'
6
6
  gem 'minitest-reporters'
7
7
  gem 'minitest-debugger', :require => false
8
+ gem 'rack-test'
8
9
  gem 'webmock'
10
+
11
+ gem "puma"
12
+
13
+ # Rack v2 dropped support for Ruby 2.2 and higher.
14
+ if RUBY_VERSION < '2.2'
15
+ gem 'rack', '< 2.0'
16
+ end
9
17
  end
10
18
 
11
19
  group :development do
@@ -0,0 +1,73 @@
1
+ #######################################
2
+ ## Block tracing
3
+ #######################################
4
+
5
+ #Instana::Tracer.start_or_continue_trace(name, kvs, incoming_id) - Initiates tracing
6
+ #Instana::Tracer.trace(name, kvs) - starts a new span in an existing Trace
7
+
8
+ # <start_or_continue_trace> will initiate a new trace. Often used at entry
9
+ # points in webservers, it will initialize tracing and instrument the passed
10
+ # block. <incoming_id> is for continuing remote traces (remote in terms
11
+ # of service calls, or message queues).
12
+ Instana::Tracer.start_or_continue_trace(:my_block_name, {}, incoming_id) do
13
+ # Code block
14
+ end
15
+
16
+ # <trace> will instrument a block of code in an already running trace.
17
+ # This is the most common use case (instead of initiating new
18
+ # traces).
19
+ Instana::Tracer.trace(:postgres, {:user => 'postgres'}) do
20
+ @postgres.select(1)
21
+ end
22
+
23
+ #######################################
24
+ ## Lower level logging
25
+ #######################################
26
+
27
+ # <log_start_or_continue_trace> will initiate a new trace. Often used at entry
28
+ # points in webservers, it will establish a new trace (for web request,
29
+ # background jobs etc.).
30
+ # <incoming_id> is for continuing remote traces (remote in terms
31
+ # of service calls, or message queues).
32
+ Instana::Tracer.log_start_or_continue(:rack, {}, incoming_id)
33
+
34
+ # <log_entry> will start a new span from the current span within
35
+ # a trace.
36
+ Instana::Tracer.log_entry(name, kvs)
37
+
38
+ # <log_exit> will close out the current span
39
+ Instana::Tracer.log_exit(name, kvs)
40
+
41
+ # <log_info> will append information to the current span in the
42
+ # trace. Examples could be redis options, contextual data, user
43
+ # login status etc...
44
+ Instana::Tracer.log_info({:some_key => 'some_value'})
45
+
46
+ # <log_error> will log an exception to the current span in the
47
+ # trace.
48
+ Instana::Tracer.log_error(Exception)
49
+
50
+ # <log_end> closes out the current span, finishes
51
+ # the trace and adds it to ::Instana.processor
52
+ # for reporting.
53
+ Instana::Tracer.log_end(:rack, {})
54
+
55
+ #######################################
56
+ # Lower level API Example
57
+ ######################################
58
+
59
+ # Full Tracing Lifecycle example
60
+ #
61
+ Instana::Tracer.log_start_or_continue(:mywebserver, {:user_id => @user_id})
62
+
63
+ begin
64
+ Instana::Tracer.log_entry(:redis_lookup, {:redisdb => @redisdb.url})
65
+ @redisdb.get(@user_id.session_id)
66
+ rescue => e
67
+ Instana::Tracer.log_error(e)
68
+ ensure
69
+ Instana::Tracer.log_exit(:redis_lookup)
70
+ end
71
+
72
+ Instana::Tracer.log_end(:mywebserver)
73
+
@@ -7,6 +7,8 @@ module Instana
7
7
  class << self
8
8
  attr_accessor :agent
9
9
  attr_accessor :collectors
10
+ attr_accessor :tracer
11
+ attr_accessor :processor
10
12
  attr_accessor :config
11
13
  attr_accessor :logger
12
14
  attr_accessor :pid
@@ -17,7 +19,9 @@ module Instana
17
19
  # Initialize the Instana language agent
18
20
  #
19
21
  def start
20
- @agent = Instana::Agent.new
22
+ @agent = ::Instana::Agent.new
23
+ @tracer = ::Instana::Tracer.new
24
+ @processor = ::Instana::Processor.new
21
25
  @collectors = []
22
26
 
23
27
  @logger = Logger.new(STDOUT)
@@ -30,11 +34,11 @@ module Instana
30
34
 
31
35
  # Store the current pid so we can detect a potential fork
32
36
  # later on
33
- @pid = Process.pid
37
+ @pid = ::Process.pid
34
38
  end
35
39
 
36
40
  def pid_change?
37
- @pid != Process.pid
41
+ @pid != ::Process.pid
38
42
  end
39
43
  end
40
44
  end
@@ -42,9 +46,12 @@ end
42
46
 
43
47
  require "instana/config"
44
48
  require "instana/agent"
49
+ require "instana/tracer"
50
+ require "instana/tracing/processor"
45
51
 
46
52
  ::Instana.start
47
53
 
48
54
  require "instana/collectors"
55
+ require "instana/instrumentation"
49
56
 
50
57
  ::Instana.agent.start
@@ -8,6 +8,7 @@ include Sys
8
8
  module Instana
9
9
  class Agent
10
10
  attr_accessor :state
11
+ attr_accessor :agent_uuid
11
12
 
12
13
  LOCALHOST = '127.0.0.1'.freeze
13
14
  MIME_JSON = 'application/json'.freeze
@@ -62,9 +63,7 @@ module Instana
62
63
  @process[:report_pid] = nil
63
64
  end
64
65
 
65
- ##
66
- # start
67
- #
66
+ # Sets up periodic timers and starts the agent in a background thread.
68
67
  #
69
68
  def start
70
69
  # The announce timer
@@ -90,6 +89,7 @@ module Instana
90
89
  transition_to(:unannounced)
91
90
  end
92
91
  end
92
+ ::Instana.processor.send
93
93
  end
94
94
  end
95
95
 
@@ -109,9 +109,22 @@ module Instana
109
109
  end
110
110
  end
111
111
 
112
- ##
113
- # announce_sensor
112
+ # Indicates if the agent is ready to send metrics
113
+ # or data.
114
+ #
115
+ def ready?
116
+ # In test, we're always ready :-)
117
+ return true if ENV['INSTANA_GEM_TEST']
118
+
119
+ @state == :announced
120
+ end
121
+
122
+ # Returns the PID that we are reporting to
114
123
  #
124
+ def report_pid
125
+ @process[:report_pid]
126
+ end
127
+
115
128
  # Collect process ID, name and arguments to notify
116
129
  # the host agent.
117
130
  #
@@ -142,11 +155,10 @@ module Instana
142
155
  return false
143
156
  end
144
157
 
145
- ##
146
- # report_entity_data
147
- #
148
158
  # Method to report metrics data to the host agent.
149
159
  #
160
+ # @param paylod [Hash] The collection of metrics to report.
161
+ #
150
162
  def report_entity_data(payload)
151
163
  with_snapshot = false
152
164
  path = "com.instana.plugin.ruby.#{@process[:report_pid]}"
@@ -187,9 +199,36 @@ module Instana
187
199
  Instana.logger.debug e.backtrace.join("\r\n")
188
200
  end
189
201
 
190
- ##
191
- # host_agent_ready?
202
+ # Accept and report spans to the host agent.
192
203
  #
204
+ # @param traces [Array] An array of [Span]
205
+ # @return [Boolean]
206
+ #
207
+ def report_spans(spans)
208
+ return unless @state == :announced
209
+
210
+ path = "com.instana.plugin.ruby/traces.#{@process[:report_pid]}"
211
+ uri = URI.parse("http://#{@host}:#{@port}/#{path}")
212
+ req = Net::HTTP::Post.new(uri)
213
+
214
+ req.body = spans.to_json
215
+ response = make_host_agent_request(req)
216
+
217
+ if response
218
+ last_trace_response = response.code.to_i
219
+
220
+ #::Instana.logger.debug "traces response #{last_trace_response}: #{spans.to_json}"
221
+
222
+ if [200, 204].include?(last_trace_response)
223
+ return true
224
+ end
225
+ end
226
+ false
227
+ rescue => e
228
+ Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
229
+ Instana.logger.debug e.backtrace.join("\r\n")
230
+ end
231
+
193
232
  # Check that the host agent is available and can be contacted. This will
194
233
  # first check localhost and if not, then attempt on the default gateway
195
234
  # for docker in bridged mode. It will save where it found the host agent
@@ -229,12 +268,12 @@ module Instana
229
268
 
230
269
  private
231
270
 
232
- ##
233
- # transition_to
234
- #
235
271
  # Handles any/all steps required in the transtion
236
272
  # between states.
237
273
  #
274
+ # @param state [Symbol] Can be 1 of 2 possible states:
275
+ # `:announced`, `:unannounced`
276
+ #
238
277
  def transition_to(state)
239
278
  case state
240
279
  when :announced
@@ -254,13 +293,13 @@ module Instana
254
293
  end
255
294
  end
256
295
 
257
- ##
258
- # make host_agent_request
259
- #
260
296
  # Centralization of the net/http communications
261
297
  # with the host agent. Pass in a prepared <req>
262
298
  # of type Net::HTTP::Get|Put|Head
263
299
  #
300
+ # @param req [Net::HTTP::Req] A prepared Net::HTTP request object of the type
301
+ # you wish to make (Get, Put, Post etc.)
302
+ #
264
303
  def make_host_agent_request(req)
265
304
  req['Accept'] = MIME_JSON
266
305
  req['Content-Type'] = MIME_JSON
@@ -278,9 +317,6 @@ module Instana
278
317
  return nil
279
318
  end
280
319
 
281
- ##
282
- # pid_namespace?
283
- #
284
320
  # Indicates whether we are running in a pid namespace (such as
285
321
  # Docker).
286
322
  #
@@ -289,9 +325,6 @@ module Instana
289
325
  Process.pid != get_real_pid
290
326
  end
291
327
 
292
- ##
293
- # get_real_pid
294
- #
295
328
  # Attempts to determine the true process ID by querying the
296
329
  # /proc/<pid>/sched file. This works on linux currently.
297
330
  #
@@ -301,9 +334,6 @@ module Instana
301
334
  v.match(/\d+/).to_s.to_i
302
335
  end
303
336
 
304
- ##
305
- # take_snapshot
306
- #
307
337
  # Method to collect up process info for snapshots. This
308
338
  # is generally used once per process.
309
339
  #
@@ -0,0 +1,12 @@
1
+ #
2
+ # Load all of the files in the instrumentation subdirectory
3
+ #
4
+ pattern = File.join(File.dirname(__FILE__), 'instrumentation', '*.rb')
5
+ Dir.glob(pattern) do |f|
6
+ begin
7
+ require f
8
+ rescue => e
9
+ Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
10
+ Instana.logger.debug e.backtrace.join("\r\n")
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ require 'net/http'
2
+
3
+ Net::HTTP.class_eval {
4
+
5
+ def request_with_instana(*args, &block)
6
+ if !Instana.tracer.tracing? || !started?
7
+ return request_without_instana(*args, &block)
8
+ end
9
+
10
+ ::Instana.tracer.log_entry(:'net-http')
11
+
12
+ # Send out the tracing context with the request
13
+ request = args[0]
14
+ our_trace_id = ::Instana.tracer.trace_id
15
+ our_span_id = ::Instana.tracer.span_id
16
+
17
+ # Set request headers; encode IDs as hexadecimal strings
18
+ request['X-Instana-T'] = ::Instana.tracer.id_to_header(our_trace_id)
19
+ request['X-Instana-S'] = ::Instana.tracer.id_to_header(our_span_id)
20
+
21
+ response = request_without_instana(*args, &block)
22
+
23
+ # Pickup response headers; convert back to base 10 integer
24
+ if response.key?('X-Instana-T')
25
+ their_trace_id = ::Instana.tracer.header_to_id(response.header['X-Instana-T'])
26
+
27
+ if our_trace_id != their_trace_id
28
+ ::Instana.logger.debug "#{Thread.current}: Trace ID mismatch on net/http response! ours: #{our_trace_id} theirs: #{their_trace_id}"
29
+ end
30
+ end
31
+
32
+ kv_payload = { :http => {} }
33
+ kv_payload[:http][:status] = response.code
34
+ kv_payload[:http][:url] = request.uri.to_s
35
+ kv_payload[:http][:method] = request.method
36
+ ::Instana.tracer.log_info(kv_payload)
37
+
38
+ response
39
+ rescue => e
40
+ ::Instana.tracer.log_error(e)
41
+ raise
42
+ ensure
43
+ ::Instana.tracer.log_exit(:'net-http')
44
+ end
45
+
46
+ Instana.logger.info "Instrumenting net/http"
47
+
48
+ alias request_without_instana request
49
+ alias request request_with_instana
50
+ }
@@ -0,0 +1,50 @@
1
+ module Instana
2
+ class Rack
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ kvs = { :http => {} }
9
+ kvs[:http][:method] = env['REQUEST_METHOD']
10
+ kvs[:http][:url] = ::CGI.unescape(env['PATH_INFO'])
11
+
12
+ if env.key?('HTTP_HOST')
13
+ kvs[:http][:host] = env['HTTP_HOST']
14
+ elsif env.key?('SERVER_NAME')
15
+ kvs[:http][:host] = env['SERVER_NAME']
16
+ end
17
+
18
+ # Check incoming context
19
+ incoming_context = {}
20
+ if env.key?('HTTP_X_INSTANA_T')
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')
23
+ incoming_context[:level] = env['HTTP_X_INSTANA_L'] if env.key?('HTTP_X_INSTANA_L')
24
+ end
25
+
26
+ ::Instana.tracer.log_start_or_continue(:rack, {}, incoming_context)
27
+
28
+ status, headers, response = @app.call(env)
29
+
30
+ kvs[:http][:status] = status
31
+
32
+ # Save the IDs before the trace ends so we can place
33
+ # them in the response headers in the ensure block
34
+ trace_id = ::Instana.tracer.trace_id
35
+ span_id = ::Instana.tracer.span_id
36
+
37
+ [status, headers, response]
38
+ rescue Exception => e
39
+ ::Instana.tracer.log_error(e)
40
+ raise
41
+ ensure
42
+ if headers
43
+ # Set reponse headers; encode as hex string
44
+ headers['X-Instana-T'] = ::Instana.tracer.id_to_header(trace_id)
45
+ headers['X-Instana-S'] = ::Instana.tracer.id_to_header(span_id)
46
+ end
47
+ ::Instana.tracer.log_end(:rack, kvs)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ # This file exists to just make the Instana::Rack require calls a bit more
2
+ # user friendly.
3
+ #
4
+ # The real file is in the instrumentation subdirectory:
5
+ # lib/instana/instrumentation/rack.rb
6
+ #
7
+ # require 'instana/rack'
8
+ # config.middleware.use ::Instana::Rack
9
+ #
10
+ require 'instana/instrumentation/rack'
@@ -0,0 +1,15 @@
1
+ module Instana
2
+ module ThreadLocal
3
+ def thread_local(name)
4
+ key = "__#{self}_#{name}__".intern
5
+
6
+ define_method(name) do
7
+ Thread.current[key]
8
+ end
9
+
10
+ define_method(name.to_s + '=') do |value|
11
+ Thread.current[key] = value
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,178 @@
1
+ require "instana/thread_local"
2
+ require "instana/tracing/trace"
3
+ require "instana/tracing/span"
4
+
5
+ module Instana
6
+ class Tracer
7
+ extend ::Instana::ThreadLocal
8
+
9
+ thread_local :current_trace
10
+
11
+ #######################################
12
+ # Tracing blocks helper methods
13
+ #######################################
14
+
15
+ # Will start a new trace or continue an on-going one (such as
16
+ # from incoming remote requests with context headers).
17
+ #
18
+ # @param name [String] the name of the span to start
19
+ # @param kvs [Hash] list of key values to be reported in the span
20
+ # @param incoming_context [Hash] specifies the incoming context. At a
21
+ # minimum, it should specify :trace_id and :parent_id from the following:
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)
24
+ # :level specifies data collection level (optional)
25
+ #
26
+ def start_or_continue_trace(name, kvs = {}, incoming_context = {}, &block)
27
+ log_start_or_continue(name, kvs, incoming_context)
28
+ block.call
29
+ rescue Exception => e
30
+ log_error(e)
31
+ raise
32
+ ensure
33
+ log_end(name)
34
+ end
35
+
36
+ # Trace a block of code within the context of the exiting trace
37
+ #
38
+ # @param name [String] the name of the span to start
39
+ # @param kvs [Hash] list of key values to be reported in this new span
40
+ #
41
+ def trace(name, kvs = {}, &block)
42
+ log_entry(name, kvs)
43
+ result = block.call
44
+ result
45
+ rescue Exception => e
46
+ log_error(e)
47
+ raise
48
+ ensure
49
+ log_exit(name)
50
+ end
51
+
52
+ #######################################
53
+ # Lower level tracing methods
54
+ #######################################
55
+
56
+ # Will start a new trace or continue an on-going one (such as
57
+ # from incoming remote requests with context headers).
58
+ #
59
+ # @param name [String] the name of the span to start
60
+ # @param kvs [Hash] list of key values to be reported in the span
61
+ # @param incoming_context [Hash] specifies the incoming context. At a
62
+ # minimum, it should specify :trace_id and :parent_id from the following:
63
+ # :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)
65
+ # :level specifies data collection level (optional)
66
+ #
67
+ def log_start_or_continue(name, kvs = {}, incoming_context = {})
68
+ return unless ::Instana.agent.ready?
69
+ self.current_trace = ::Instana::Trace.new(name, kvs, incoming_context)
70
+ end
71
+
72
+ # Will establish a new span as a child of the current span
73
+ # in an existing trace
74
+ #
75
+ # @param name [String] the name of the span to create
76
+ # @param kvs [Hash] list of key values to be reported in the span
77
+ #
78
+ def log_entry(name, kvs = {})
79
+ return unless tracing?
80
+ self.current_trace.new_span(name, kvs)
81
+ end
82
+
83
+ # Add info to the current span
84
+ #
85
+ # @param kvs [Hash] list of key values to be reported in the span
86
+ #
87
+ def log_info(kvs)
88
+ return unless tracing?
89
+ self.current_trace.add_info(kvs)
90
+ end
91
+
92
+ # Add an error to the current span
93
+ #
94
+ # @param e [Exception] Add exception to the current span
95
+ #
96
+ def log_error(e)
97
+ return unless tracing?
98
+ self.current_trace.add_error(e)
99
+ end
100
+
101
+ # Will close out the current span
102
+ #
103
+ # @note `name` isn't really required but helps keep sanity that
104
+ # we're closing out the span that we really want to close out.
105
+ #
106
+ # @param name [String] the name of the span to exit (close out)
107
+ # @param kvs [Hash] list of key values to be reported in the span
108
+ #
109
+ def log_exit(name, kvs = {})
110
+ return unless tracing?
111
+ self.current_trace.end_span(kvs)
112
+ end
113
+
114
+ # Closes out the current span in the current trace
115
+ # and queues the trace for reporting
116
+ #
117
+ # @note `name` isn't really required but helps keep sanity that
118
+ # we're ending the span that we really want to close out.
119
+ #
120
+ # @param name [String] the name of the span to end
121
+ # @param kvs [Hash] list of key values to be reported in the span
122
+ #
123
+ def log_end(name, kvs = {})
124
+ return unless tracing?
125
+
126
+ self.current_trace.finish(kvs)
127
+ Instana.processor.add(self.current_trace)
128
+ self.current_trace = nil
129
+ end
130
+
131
+ # Indicates if we're are currently in the process of
132
+ # collecting a trace. This is false when the host agent isn
133
+ # available.
134
+ #
135
+ # @return [Boolean] true or false on whether we are currently tracing or not
136
+ #
137
+ def tracing?
138
+ # The non-nil value of this instance variable
139
+ # indicates if we are currently tracing
140
+ # in this thread or not.
141
+ self.current_trace ? true : false
142
+ end
143
+
144
+ # Convert an ID to a value appropriate to pass in a header.
145
+ #
146
+ # @param id [Integer] the id to be converted
147
+ #
148
+ # @return [String]
149
+ #
150
+ def id_to_header(id)
151
+ id.to_s(16)
152
+ end
153
+
154
+ # Convert a received header value into a valid ID
155
+ #
156
+ # @param header_id [String] the header value to be converted
157
+ #
158
+ # @return [Integer]
159
+ #
160
+ def header_to_id(header_id)
161
+ header_id.to_i(16)
162
+ end
163
+
164
+ # Returns the trace ID for the active trace (if there is one),
165
+ # otherwise nil.
166
+ #
167
+ def trace_id
168
+ self.current_trace ? self.current_trace.id : nil
169
+ end
170
+
171
+ # Returns the current [Span] ID for the active trace (if there is one),
172
+ # otherwise nil.
173
+ #
174
+ def span_id
175
+ self.current_trace ? current_trace.current_span_id : nil
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,93 @@
1
+ require 'thread'
2
+
3
+ module Instana
4
+ class Processor
5
+
6
+ def initialize
7
+ @queue = Queue.new
8
+ end
9
+
10
+ # Adds a trace to the queue to be processed and
11
+ # sent to the host agent
12
+ #
13
+ # @param [Trace] the trace to be added to the queue
14
+ def add(trace)
15
+ @queue.push(trace)
16
+ end
17
+
18
+ ##
19
+ # send
20
+ #
21
+ # Sends all traces in @queue to the host
22
+ # agent
23
+ #
24
+ # FIXME: Add limits checking here in regards to:
25
+ # - Max HTTP Post size
26
+ # - Out of control/growing queue
27
+ # - Prevent another run of the timer while this is running
28
+ #
29
+ def send
30
+ return if @queue.empty?
31
+
32
+ size = @queue.size
33
+ if size > 10
34
+ Instana.logger.debug "Trace queue is #{size}"
35
+ end
36
+
37
+ ::Instana.agent.report_spans(queued_spans)
38
+ end
39
+
40
+ # Get the number traces currently in the queue
41
+ #
42
+ def queue_count
43
+ @queue.size
44
+ end
45
+
46
+ # Retrieves all of the traces in @queue and returns
47
+ # the sum of their raw spans.
48
+ # This is used by Processor::send and in the test suite.
49
+ # Note that traces retrieved with this method are removed
50
+ # entirely from the queue.
51
+ #
52
+ def queued_spans
53
+ return [] if @queue.empty?
54
+
55
+ spans = []
56
+ until @queue.empty? do
57
+ # Non-blocking pop; ignore exception
58
+ trace = @queue.pop(true) rescue nil
59
+ trace.spans.each do |s|
60
+ spans << s.raw
61
+ end
62
+ end
63
+ spans
64
+ end
65
+
66
+ # Retrieves all of the traces that are in @queue.
67
+ # Note that traces retrieved with this method are removed
68
+ # entirely from the queue.
69
+ #
70
+ def queued_traces
71
+ return [] if @queue.empty?
72
+
73
+ traces = []
74
+ until @queue.empty? do
75
+ # Non-blocking pop; ignore exception
76
+ traces << @queue.pop(true) rescue nil
77
+ end
78
+ traces
79
+ end
80
+
81
+ # Removes all traces from the @queue. Used in the
82
+ # test suite.
83
+ #
84
+ def clear!
85
+ return [] if @queue.empty?
86
+
87
+ until @queue.empty? do
88
+ # Non-blocking pop; ignore exception
89
+ @queue.pop(true) rescue nil
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,37 @@
1
+ module Instana
2
+ class Span
3
+ attr_accessor :parent
4
+
5
+ def initialize(data)
6
+ @data = data
7
+ end
8
+
9
+ def id
10
+ @data[:s]
11
+ end
12
+
13
+ def parent_id
14
+ @data[:p]
15
+ end
16
+
17
+ def is_root?
18
+ @data[:s] == @data[:t]
19
+ end
20
+
21
+ def [](key)
22
+ @data[key.to_sym]
23
+ end
24
+
25
+ def []=(key, value)
26
+ @data[key.to_sym] = value
27
+ end
28
+
29
+ def key?(k)
30
+ @data.key?(k.to_sym)
31
+ end
32
+
33
+ def raw
34
+ @data
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,169 @@
1
+ module Instana
2
+ class Trace
3
+ # @return [Integer] the ID for this trace
4
+ attr_reader :id
5
+
6
+ # The collection of `Span` for this trace
7
+ # @return [Set] the collection of spans for this trace
8
+ attr_reader :spans
9
+
10
+ # Initializes a new instance of Trace
11
+ #
12
+ # @param name [String] the name of the span to start
13
+ # @param kvs [Hash] list of key values to be reported in the span
14
+ # @param incoming_context [Hash] specifies the incoming context. At a
15
+ # minimum, it should specify :trace_id and :parent_id from the following:
16
+ # :trace_id the trace ID (must be an unsigned hex-string)
17
+ # :parent_id the ID of the parent span (must be an unsigned hex-string)
18
+ # :level specifies data collection level (optional)
19
+ #
20
+ def initialize(name, kvs = {}, incoming_context = {})
21
+ # The collection of spans that make
22
+ # up this trace.
23
+ @spans = Set.new
24
+
25
+ # The current active span
26
+ @current_span = nil
27
+
28
+ # Generate a random 64bit ID for this trace
29
+ @id = generate_id
30
+
31
+ # This is a new trace so open the first span with the proper
32
+ # root span IDs.
33
+ @current_span = Span.new({
34
+ :s => @id, # Span ID
35
+ :n => name, # Span name
36
+ :ts => ts_now, # Timestamp
37
+ :ta => :ruby, # Agent
38
+ :data => kvs, # Data
39
+ :f => { :e => ::Instana.agent.report_pid, :h => ::Instana.agent.agent_uuid } # Entity Source
40
+ })
41
+
42
+ # Handle potential incoming context
43
+ if incoming_context.empty?
44
+ # No incoming context. Set trace ID the same
45
+ # as this first span.
46
+ @current_span[:t] = @id
47
+ else
48
+ @id = incoming_context[:trace_id]
49
+ @current_span[:t] = incoming_context[:trace_id]
50
+ @current_span[:p] = incoming_context[:parent_id]
51
+ end
52
+
53
+ @spans.add(@current_span)
54
+ end
55
+
56
+ # Start a new span as a child of @current_span
57
+ #
58
+ # @param name [String] the name of the span to start
59
+ # @param kvs [Hash] list of key values to be reported in the span
60
+ #
61
+ def new_span(name, kvs)
62
+ return unless @current_span
63
+
64
+ new_span = Span.new({
65
+ :s => generate_id, # Span ID
66
+ :t => @id, # Trace ID (same as :s for root span)
67
+ :p => @current_span[:s], # Parent ID
68
+ :n => name, # Span name
69
+ :ts => ts_now, # Timestamp
70
+ :ta => :ruby, # Agent
71
+ :data => kvs, # Data
72
+ :f => { :e => Process.pid, :h => :agent_id } # Entity Source
73
+ })
74
+ new_span.parent = @current_span
75
+ @spans.add(new_span)
76
+ @current_span = new_span
77
+ end
78
+
79
+ # Add KVs to the current span
80
+ #
81
+ # @param kvs [Hash] list of key values to be reported in the span
82
+ #
83
+ def add_info(kvs)
84
+ @current_span[:data].merge!(kvs)
85
+ end
86
+
87
+ # Log an error into the current span
88
+ #
89
+ # @param e [Exception] Add exception to the current span
90
+ #
91
+ def add_error(e)
92
+ @current_span[:error] = true
93
+ end
94
+
95
+ # Close out the current span and set the parent as
96
+ # the current span
97
+ #
98
+ # @param kvs [Hash] list of key values to be reported in the span
99
+ #
100
+ def end_span(kvs = {})
101
+ @current_span[:d] = ts_now - @current_span[:ts]
102
+ add_info(kvs) unless kvs.empty?
103
+ @current_span = @current_span.parent unless @current_span.is_root?
104
+ end
105
+
106
+ # Closes out the final span in this trace and runs any finalizer
107
+ # steps required.
108
+ # This should be called only on the root span to end the trace.
109
+ #
110
+ # @param kvs [Hash] list of key values to be reported in the span
111
+ #
112
+ def finish(kvs = {})
113
+ end_span(kvs)
114
+ end
115
+
116
+ # Indicates whether all seems ok with this
117
+ # trace in it's current state. Should be only
118
+ # called on finished traces.
119
+ #
120
+ # @return [Boolean] true or false on whether this trace is valid
121
+ #
122
+ def valid?
123
+ # TODO
124
+ true
125
+ end
126
+
127
+ # Searches the set of spans and indicates if there
128
+ # is an error logged in one of them.
129
+ #
130
+ # @return [Boolean] true or false indicating the presence
131
+ # of an error
132
+ #
133
+ def has_error?
134
+ @spans.each do |s|
135
+ if s.key?(:error)
136
+ return s[:error]
137
+ end
138
+ end
139
+ false
140
+ end
141
+
142
+ # Get the ID of the current span for this trace.
143
+ # Used often to place in HTTP response headers.
144
+ #
145
+ # @return [Integer] a random 64bit integer
146
+ #
147
+ def current_span_id
148
+ @current_span.id
149
+ end
150
+
151
+ private
152
+
153
+ # Get the current time in milliseconds
154
+ #
155
+ # @return [Integer] the current time in milliseconds
156
+ #
157
+ def ts_now
158
+ (Time.now.to_f * 1000).floor
159
+ end
160
+
161
+ # Generate a random 64bit ID
162
+ #
163
+ # @return [Integer] a random 64bit integer
164
+ #
165
+ def generate_id
166
+ rand(2**32..2**64-1)
167
+ end
168
+ end
169
+ end
@@ -1,3 +1,3 @@
1
1
  module Instana
2
- VERSION = "0.8.6"
2
+ VERSION = "0.9.0-slywolf3"
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.8.6
4
+ version: 0.9.0.pre.slywolf3
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-11-18 00:00:00.000000000 Z
11
+ date: 2016-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,6 +122,7 @@ files:
122
122
  - Rakefile
123
123
  - bin/console
124
124
  - bin/setup
125
+ - examples/tracing.rb
125
126
  - instana.gemspec
126
127
  - lib/instana.rb
127
128
  - lib/instana/agent.rb
@@ -130,6 +131,15 @@ files:
130
131
  - lib/instana/collectors/memory.rb
131
132
  - lib/instana/collectors/thread.rb
132
133
  - lib/instana/config.rb
134
+ - lib/instana/instrumentation.rb
135
+ - lib/instana/instrumentation/net-http.rb
136
+ - lib/instana/instrumentation/rack.rb
137
+ - lib/instana/rack.rb
138
+ - lib/instana/thread_local.rb
139
+ - lib/instana/tracer.rb
140
+ - lib/instana/tracing/processor.rb
141
+ - lib/instana/tracing/span.rb
142
+ - lib/instana/tracing/trace.rb
133
143
  - lib/instana/util.rb
134
144
  - lib/instana/version.rb
135
145
  homepage: https://www.instana.com/
@@ -146,9 +156,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
156
  version: '2.0'
147
157
  required_rubygems_version: !ruby/object:Gem::Requirement
148
158
  requirements:
149
- - - ">="
159
+ - - ">"
150
160
  - !ruby/object:Gem::Version
151
- version: '0'
161
+ version: 1.3.1
152
162
  requirements: []
153
163
  rubyforge_project:
154
164
  rubygems_version: 2.5.1