instana 0.8.6 → 0.9.0.pre.slywolf3

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: 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