instana 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/instana/ruby-sensor.svg?branch=master)](https://travis-ci.org/instana/ruby-sensor)
|
10
|
+
[![Gem Version](https://badge.fury.io/rb/instana.svg)](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
|