instana 0.8.1 → 0.8.2
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/Gemfile +1 -0
- data/README.md +7 -0
- data/lib/instana/agent.rb +164 -81
- data/lib/instana/collectors.rb +24 -29
- data/lib/instana/version.rb +1 -1
- data/lib/instana.rb +9 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae7d78fd94912eaec8e882d9dd08f34361657778
|
4
|
+
data.tar.gz: dd3f522c3e0dce7cc21ad3513114998637b88803
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cad401a7c37240c344406a860021073f3195c1d395cae42f9d3b1a85a1a88b430f5f739e3cc3853c6f871fcbc0b7e5d8f331a7fe53c3d50a0ca16ffb5550c6e
|
7
|
+
data.tar.gz: 8fbf28bf702a802e5a850069ba9d349cd12e12a2689d2cccbcca600e9a93f7d8d607df0d777244f7e2ef508aa98371e8d3ab53f8bf4bb039f118c796c2561f27
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
|
7
7
|
The Instana gem provides Ruby metrics for [Instana](https://www.instana.com/).
|
8
8
|
|
9
|
+
[](https://travis-ci.org/instana/ruby-sensor)
|
10
|
+
[](https://badge.fury.io/rb/instana)
|
11
|
+
|
9
12
|
## Note
|
10
13
|
|
11
14
|
This gem is currently in beta and supports Ruby versions 2.0 or greater.
|
@@ -43,6 +46,10 @@ To disable a single component in the gem, you can disable a single component wit
|
|
43
46
|
```
|
44
47
|
Current components are `:gc`, `:memory` and `:thread`.
|
45
48
|
|
49
|
+
## Documentation
|
50
|
+
|
51
|
+
You can find more documentation covering supported components and minimum versions in the Instana [documentation portal](https://instana.atlassian.net/wiki/display/DOCS/Ruby).
|
52
|
+
|
46
53
|
## Development
|
47
54
|
|
48
55
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/instana/agent.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'uri'
|
3
3
|
require 'json'
|
4
|
+
require 'timers'
|
4
5
|
require 'sys/proctable'
|
5
6
|
include Sys
|
6
7
|
|
7
8
|
module Instana
|
8
9
|
class Agent
|
9
|
-
attr_accessor :
|
10
|
+
attr_accessor :state
|
10
11
|
|
11
12
|
def initialize
|
12
13
|
# Host agent defaults. Can be configured via Instana.config
|
@@ -15,6 +16,9 @@ module Instana
|
|
15
16
|
@port = 42699
|
16
17
|
@server_header = 'Instana Agent'
|
17
18
|
|
19
|
+
# Supported two states (unannounced & announced)
|
20
|
+
@state = :unannounced
|
21
|
+
|
18
22
|
# Snapshot data is collected once per process but resent
|
19
23
|
# every 10 minutes along side process metrics.
|
20
24
|
@snapshot = take_snapshot
|
@@ -22,52 +26,62 @@ module Instana
|
|
22
26
|
# Set last snapshot to 10 minutes ago
|
23
27
|
# so we send a snapshot on first report
|
24
28
|
@last_snapshot = Time.now - 601
|
29
|
+
|
30
|
+
# Timestamp of the last successful response from
|
31
|
+
# entity data reporting.
|
32
|
+
@entity_last_seen = Time.now
|
33
|
+
|
34
|
+
# Two timers, one for each state (unannounced & announced)
|
35
|
+
@timers = ::Timers::Group.new
|
36
|
+
@announce_timer = nil
|
37
|
+
@collect_timer = nil
|
25
38
|
end
|
26
39
|
|
27
40
|
##
|
28
|
-
#
|
41
|
+
# start
|
29
42
|
#
|
30
|
-
# Method to collect up process info for snapshots. This
|
31
|
-
# is generally used once per process.
|
32
43
|
#
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
data[:exec_args] = arguments
|
44
|
-
|
45
|
-
# Since a snapshot is only taken on process boot,
|
46
|
-
# this is ok here.
|
47
|
-
data[:start_time] = Time.now.to_s
|
48
|
-
|
49
|
-
# Framework Detection
|
50
|
-
if defined?(::RailsLts::VERSION)
|
51
|
-
data[:framework] = "Rails on Rails LTS-#{::RailsLts::VERSION}"
|
52
|
-
|
53
|
-
elsif defined?(::Rails.version)
|
54
|
-
data[:framework] = "Ruby on Rails #{::Rails.version}"
|
55
|
-
|
56
|
-
elsif defined?(::Grape::VERSION)
|
57
|
-
data[:framework] = "Grape #{::Grape::VERSION}"
|
58
|
-
|
59
|
-
elsif defined?(::Padrino::VERSION)
|
60
|
-
data[:framework] = "Padrino #{::Padrino::VERSION}"
|
44
|
+
def start
|
45
|
+
# The announce timer
|
46
|
+
# We attempt to announce this ruby sensor to the host agent.
|
47
|
+
# In case of failure, we try again in 30 seconds.
|
48
|
+
@announce_timer = @timers.now_and_every(30) do
|
49
|
+
if host_agent_ready? && announce_sensor
|
50
|
+
::Instana.logger.debug "Announce successful. Switching to metrics collection."
|
51
|
+
transition_to(:announced)
|
52
|
+
end
|
53
|
+
end
|
61
54
|
|
62
|
-
|
63
|
-
|
55
|
+
# The collect timer
|
56
|
+
# If we are in announced state, send metric data (only delta reporting)
|
57
|
+
# every ::Instana::Collector.interval seconds.
|
58
|
+
@collect_timer = @timers.every(::Instana::Collector.interval) do
|
59
|
+
if @state == :announced
|
60
|
+
unless ::Instana::Collector.collect_and_report
|
61
|
+
# If report has been failing for more than 1 minute,
|
62
|
+
# fall back to unannounced state
|
63
|
+
if (Time.now - @entity_last_seen) > 60
|
64
|
+
::Instana.logger.debug "Metrics reporting failed for >1 min. Falling back to unannounced state."
|
65
|
+
transition_to(:unannounced)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
64
69
|
end
|
65
70
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
+
# Start the background ruby sensor thread. It works off of timers and
|
72
|
+
# is sleeping otherwise
|
73
|
+
Thread.new do
|
74
|
+
loop {
|
75
|
+
if @state == :unannounced
|
76
|
+
@collect_timer.pause
|
77
|
+
@announce_timer.resume
|
78
|
+
else
|
79
|
+
@announce_timer.pause
|
80
|
+
@collect_timer.resume
|
81
|
+
end
|
82
|
+
@timers.wait
|
83
|
+
}
|
84
|
+
end
|
71
85
|
end
|
72
86
|
|
73
87
|
##
|
@@ -88,18 +102,10 @@ module Instana
|
|
88
102
|
path = 'com.instana.plugin.ruby.discovery'
|
89
103
|
uri = URI.parse("http://#{@host}:#{@port}/#{path}")
|
90
104
|
req = Net::HTTP::Put.new(uri)
|
91
|
-
|
92
|
-
req['Accept'] = 'application/json'
|
93
|
-
req['Content-Type'] = 'application/json'
|
94
105
|
req.body = announce_payload.to_json
|
95
106
|
|
96
|
-
|
97
|
-
|
98
|
-
response = nil
|
99
|
-
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
100
|
-
response = http.request(req)
|
101
|
-
end
|
102
|
-
Instana.logger.debug response.code
|
107
|
+
response = make_host_agent_request(req)
|
108
|
+
response && (response.code.to_i == 200) ? true : false
|
103
109
|
rescue => e
|
104
110
|
Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
|
105
111
|
Instana.logger.debug e.backtrace.join("\r\n")
|
@@ -108,41 +114,36 @@ module Instana
|
|
108
114
|
##
|
109
115
|
# report_entity_data
|
110
116
|
#
|
111
|
-
# Method to report metrics data to the host agent.
|
112
|
-
# method will also send a process snapshot data.
|
117
|
+
# Method to report metrics data to the host agent.
|
113
118
|
#
|
114
119
|
def report_entity_data(payload)
|
120
|
+
with_snapshot = false
|
115
121
|
path = "com.instana.plugin.ruby.#{Process.pid}"
|
116
122
|
uri = URI.parse("http://#{@host}:#{@port}/#{path}")
|
117
123
|
req = Net::HTTP::Post.new(uri)
|
118
124
|
|
119
125
|
# Every 5 minutes, send snapshot data as well
|
120
126
|
if (Time.now - @last_snapshot) > 600
|
127
|
+
with_snapshot = true
|
121
128
|
payload.merge!(@snapshot)
|
122
|
-
@last_snapshot = Time.now
|
123
129
|
end
|
124
130
|
|
125
|
-
req['Accept'] = 'application/json'
|
126
|
-
req['Content-Type'] = 'application/json'
|
127
131
|
req.body = payload.to_json
|
132
|
+
response = make_host_agent_request(req)
|
128
133
|
|
129
|
-
|
134
|
+
if response
|
135
|
+
last_entity_response = response.code.to_i
|
130
136
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
137
|
+
if last_entity_response == 200
|
138
|
+
@entity_last_seen = Time.now
|
139
|
+
@last_snapshot = Time.now if with_snapshot
|
135
140
|
|
136
|
-
|
137
|
-
|
138
|
-
# ride for another run.
|
139
|
-
if response.code.to_i == 200
|
140
|
-
@snapshot.each do |k, v|
|
141
|
-
payload.delete(k)
|
141
|
+
#::Instana.logger.debug "entity response #{last_entity_response}: #{payload.to_json}"
|
142
|
+
return true
|
142
143
|
end
|
144
|
+
#::Instana.logger.debug "entity response #{last_entity_response}: #{payload.to_json}"
|
143
145
|
end
|
144
|
-
|
145
|
-
@last_entity_response = response.code.to_i
|
146
|
+
false
|
146
147
|
rescue => e
|
147
148
|
Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
|
148
149
|
Instana.logger.debug e.backtrace.join("\r\n")
|
@@ -157,29 +158,111 @@ module Instana
|
|
157
158
|
uri = URI.parse("http://#{@host}:#{@port}/")
|
158
159
|
req = Net::HTTP::Get.new(uri)
|
159
160
|
|
161
|
+
response = make_host_agent_request(req)
|
162
|
+
|
163
|
+
(response && response.code.to_i == 200) ? true : false
|
164
|
+
rescue => e
|
165
|
+
Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
|
166
|
+
Instana.logger.debug e.backtrace.join("\r\n")
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
##
|
173
|
+
# transition_to
|
174
|
+
#
|
175
|
+
# Handles any/all steps required in the transtion
|
176
|
+
# between states.
|
177
|
+
#
|
178
|
+
def transition_to(state)
|
179
|
+
case state
|
180
|
+
when :announced
|
181
|
+
# announce successful; set state
|
182
|
+
@state = :announced
|
183
|
+
|
184
|
+
# Reset the entity timer
|
185
|
+
@entity_last_seen = Time.now
|
186
|
+
|
187
|
+
# Set last snapshot to 10 minutes ago
|
188
|
+
# so we send a snapshot on first report
|
189
|
+
@last_snapshot = Time.now - 601
|
190
|
+
when :unannounced
|
191
|
+
@state = :unannounced
|
192
|
+
else
|
193
|
+
::Instana.logger.warn "Uknown agent state: #{state}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# make host_agent_request
|
199
|
+
#
|
200
|
+
# Centralization of the net/http communications
|
201
|
+
# with the host agent. Pass in a prepared <req>
|
202
|
+
# of type Net::HTTP::Get|Put|Head
|
203
|
+
#
|
204
|
+
def make_host_agent_request(req)
|
160
205
|
req['Accept'] = 'application/json'
|
161
206
|
req['Content-Type'] = 'application/json'
|
162
207
|
|
163
|
-
::Instana.logger.debug "Checking agent availability...."
|
164
|
-
|
165
208
|
response = nil
|
166
|
-
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
209
|
+
Net::HTTP.start(req.uri.hostname, req.uri.port, :open_timeout => 1, :read_timeout => 1) do |http|
|
167
210
|
response = http.request(req)
|
168
211
|
end
|
212
|
+
response
|
213
|
+
rescue Errno::ECONNREFUSED => e
|
214
|
+
Instana.logger.debug "Agent not responding. Connection refused."
|
215
|
+
return nil
|
216
|
+
rescue => e
|
217
|
+
Instana.logger.debug "Host agent request error: #{e.inspect}"
|
218
|
+
return nil
|
219
|
+
end
|
169
220
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
221
|
+
private
|
222
|
+
##
|
223
|
+
# take_snapshot
|
224
|
+
#
|
225
|
+
# Method to collect up process info for snapshots. This
|
226
|
+
# is generally used once per process.
|
227
|
+
#
|
228
|
+
def take_snapshot
|
229
|
+
data = {}
|
230
|
+
|
231
|
+
data[:sensorVersion] = ::Instana::VERSION
|
232
|
+
data[:pid] = ::Process.pid
|
233
|
+
data[:ruby_version] = RUBY_VERSION
|
234
|
+
|
235
|
+
process = ::ProcTable.ps(Process.pid)
|
236
|
+
arguments = process.cmdline.split(' ')
|
237
|
+
data[:name] = arguments.shift
|
238
|
+
data[:exec_args] = arguments
|
239
|
+
|
240
|
+
# Since a snapshot is only taken on process boot,
|
241
|
+
# this is ok here.
|
242
|
+
data[:start_time] = Time.now.to_s
|
243
|
+
|
244
|
+
# Framework Detection
|
245
|
+
if defined?(::RailsLts::VERSION)
|
246
|
+
data[:framework] = "Rails on Rails LTS-#{::RailsLts::VERSION}"
|
247
|
+
|
248
|
+
elsif defined?(::Rails.version)
|
249
|
+
data[:framework] = "Ruby on Rails #{::Rails.version}"
|
250
|
+
|
251
|
+
elsif defined?(::Grape::VERSION)
|
252
|
+
data[:framework] = "Grape #{::Grape::VERSION}"
|
253
|
+
|
254
|
+
elsif defined?(::Padrino::VERSION)
|
255
|
+
data[:framework] = "Padrino #{::Padrino::VERSION}"
|
256
|
+
|
257
|
+
elsif defined?(::Sinatra::VERSION)
|
258
|
+
data[:framework] = "Sinatra #{::Sinatra::VERSION}"
|
175
259
|
end
|
176
|
-
|
177
|
-
|
178
|
-
return false
|
260
|
+
|
261
|
+
data
|
179
262
|
rescue => e
|
180
|
-
Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
|
181
|
-
Instana.logger.debug e.backtrace.join("\r\n")
|
182
|
-
return
|
263
|
+
::Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
|
264
|
+
::Instana.logger.debug e.backtrace.join("\r\n")
|
265
|
+
return data
|
183
266
|
end
|
184
267
|
end
|
185
268
|
end
|
data/lib/instana/collectors.rb
CHANGED
@@ -7,6 +7,30 @@ module Instana
|
|
7
7
|
module Collector
|
8
8
|
class << self
|
9
9
|
attr_accessor :interval
|
10
|
+
attr_accessor :snapshot
|
11
|
+
|
12
|
+
##
|
13
|
+
# collect_and_report
|
14
|
+
#
|
15
|
+
# Run through each collector, let them collect up
|
16
|
+
# data and then report what we have via the agent
|
17
|
+
#
|
18
|
+
def collect_and_report
|
19
|
+
payload = {}
|
20
|
+
|
21
|
+
::Instana.collectors.each do |c|
|
22
|
+
metrics = c.collect
|
23
|
+
if metrics
|
24
|
+
payload[c.payload_key] = metrics
|
25
|
+
else
|
26
|
+
payload.delete(c.payload_key)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Report all the collected goodies
|
31
|
+
::Instana.agent.report_entity_data(payload)
|
32
|
+
end
|
33
|
+
|
10
34
|
end
|
11
35
|
end
|
12
36
|
end
|
@@ -16,32 +40,3 @@ if ENV.key?('INSTANA_GEM_DEV')
|
|
16
40
|
else
|
17
41
|
::Instana::Collector.interval = 1
|
18
42
|
end
|
19
|
-
|
20
|
-
::Thread.new do
|
21
|
-
timers = ::Timers::Group.new
|
22
|
-
payload = {}
|
23
|
-
|
24
|
-
timers.every(::Instana::Collector.interval) {
|
25
|
-
|
26
|
-
# Check if we forked (unicorn, puma) and
|
27
|
-
# if so, re-announce the process sensor
|
28
|
-
if ::Instana.pid_change?
|
29
|
-
::Instana.logger.debug "Detected a fork (old: #{::Instana.pid} new: #{::Process.pid}). Re-announcing sensor."
|
30
|
-
::Instana.pid = Process.pid
|
31
|
-
Instana.agent.announce_sensor
|
32
|
-
end
|
33
|
-
|
34
|
-
::Instana.collectors.each do |c|
|
35
|
-
metrics = c.collect
|
36
|
-
if metrics
|
37
|
-
payload[c.payload_key] = metrics
|
38
|
-
else
|
39
|
-
payload.delete(c.payload_key)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Report all the collected goodies
|
44
|
-
::Instana.agent.report_entity_data(payload)
|
45
|
-
}
|
46
|
-
loop { timers.wait }
|
47
|
-
end
|
data/lib/instana/version.rb
CHANGED
data/lib/instana.rb
CHANGED
@@ -17,14 +17,14 @@ module Instana
|
|
17
17
|
# Initialize the Instana language agent
|
18
18
|
#
|
19
19
|
def start
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
@agent = Instana::Agent.new
|
21
|
+
@collectors = []
|
22
|
+
@logger = Logger.new(STDOUT)
|
23
|
+
@logger.info "Stan is on the scene. Starting Instana instrumentation."
|
24
24
|
|
25
25
|
# Store the current pid so we can detect a potential fork
|
26
26
|
# later on
|
27
|
-
|
27
|
+
@pid = Process.pid
|
28
28
|
end
|
29
29
|
|
30
30
|
def pid_change?
|
@@ -33,14 +33,12 @@ module Instana
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
|
36
37
|
require "instana/config"
|
37
38
|
require "instana/agent"
|
38
39
|
|
39
40
|
::Instana.start
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
::Instana.logger.info "Instana host agent not available. Going to sit in a corner quietly."
|
46
|
-
end
|
42
|
+
require "instana/collectors"
|
43
|
+
|
44
|
+
::Instana.agent.start
|
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.
|
4
|
+
version: 0.8.2
|
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-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|