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