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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +8 -0
- data/examples/tracing.rb +73 -0
- data/lib/instana.rb +10 -3
- data/lib/instana/agent.rb +55 -25
- data/lib/instana/instrumentation.rb +12 -0
- data/lib/instana/instrumentation/net-http.rb +50 -0
- data/lib/instana/instrumentation/rack.rb +50 -0
- data/lib/instana/rack.rb +10 -0
- data/lib/instana/thread_local.rb +15 -0
- data/lib/instana/tracer.rb +178 -0
- data/lib/instana/tracing/processor.rb +93 -0
- data/lib/instana/tracing/span.rb +37 -0
- data/lib/instana/tracing/trace.rb +169 -0
- data/lib/instana/version.rb +1 -1
- metadata +14 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23defe3c2a845b3d6b8c92da8a08989e0b65085a
|
4
|
+
data.tar.gz: 7f2064a969abebbf119a994981b3b0915f069e05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e31e6dd3ec166b5152519d53b65e6e5af003f22cbc28a5d56262eeb9cf15b11f288a25cd91714af94c2984c3aab644876844fdfc8be8d05332db18be376e4c4
|
7
|
+
data.tar.gz: f76e9995ff846378f4b95ee33677fe077395f3e705d37ec6a683749689363b110339a05193b51c71283a23eeed17413c40eebae8b9e7f139853bae443947915e
|
data/.gitignore
CHANGED
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
|
data/examples/tracing.rb
ADDED
@@ -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
|
+
|
data/lib/instana.rb
CHANGED
@@ -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
|
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
|
data/lib/instana/agent.rb
CHANGED
@@ -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
|
-
#
|
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
|
data/lib/instana/rack.rb
ADDED
@@ -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
|
data/lib/instana/version.rb
CHANGED
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.
|
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-
|
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:
|
161
|
+
version: 1.3.1
|
152
162
|
requirements: []
|
153
163
|
rubyforge_project:
|
154
164
|
rubygems_version: 2.5.1
|