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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e9eab4fef33e50acc7b26498aacc90ecd3d63f8a
4
- data.tar.gz: 3f0ff725ff8193baa57e00c0f4ea4725ee9cb052
3
+ metadata.gz: ae7d78fd94912eaec8e882d9dd08f34361657778
4
+ data.tar.gz: dd3f522c3e0dce7cc21ad3513114998637b88803
5
5
  SHA512:
6
- metadata.gz: 497750052b7d92097a37e178e910dbfdd584df7cb7cde046c1d21cef1b617e08d371ad7ce71afe8178b2e354440a19e81f913fc53bf21c50960a5c3c1a57ebd9
7
- data.tar.gz: 004da2067917cde50fe846d9f068af919ab2c2b06a2f06ba49210f7362d8592457ee03cecf9dad82f5f0ad7adf4bb624930ade4e91f63da43994de9c5af281db
6
+ metadata.gz: 9cad401a7c37240c344406a860021073f3195c1d395cae42f9d3b1a85a1a88b430f5f739e3cc3853c6f871fcbc0b7e5d8f331a7fe53c3d50a0ca16ffb5550c6e
7
+ data.tar.gz: 8fbf28bf702a802e5a850069ba9d349cd12e12a2689d2cccbcca600e9a93f7d8d607df0d777244f7e2ef508aa98371e8d3ab53f8bf4bb039f118c796c2561f27
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ group :development, :test do
5
5
  gem 'minitest'
6
6
  gem 'minitest-reporters'
7
7
  gem 'minitest-debugger', :require => false
8
+ gem 'webmock'
8
9
  end
9
10
 
10
11
  group :development do
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 :last_entity_response
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
- # take_snapshot
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 take_snapshot
34
- data = {}
35
-
36
- data[:sensorVersion] = ::Instana::VERSION
37
- data[:pid] = Process.pid
38
- data[:ruby_version] = RUBY_VERSION
39
-
40
- process = ProcTable.ps(Process.pid)
41
- arguments = process.cmdline.split(' ')
42
- data[:name] = arguments.shift
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
- elsif defined?(::Sinatra::VERSION)
63
- data[:framework] = "Sinatra #{::Sinatra::VERSION}"
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
- data
67
- rescue => e
68
- ::Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
69
- ::Instana.logger.debug e.backtrace.join("\r\n")
70
- return data
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
- ::Instana.logger.debug "Announcing sensor to #{path} for pid #{Process.pid}: #{announce_payload.to_json}"
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. Every 10 minutes, this
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
- #Instana.logger.debug "Posting metrics to #{path}: #{payload.to_json}"
134
+ if response
135
+ last_entity_response = response.code.to_i
130
136
 
131
- response = nil
132
- Net::HTTP.start(uri.hostname, uri.port) do |http|
133
- response = http.request(req)
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
- # If snapshot data is in the payload and last response
137
- # was ok then delete the snapshot data. Otherwise let it
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
- Instana.logger.debug response.code unless response.code.to_i == 200
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
- if response.code.to_i != 200
171
- Instana.logger.debug "Host agent returned #{response.code}"
172
- false
173
- else
174
- true
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
- rescue Errno::ECONNREFUSED => e
177
- Instana.logger.debug "Agent not responding: #{e.inspect}"
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 false
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Instana
2
- VERSION = "0.8.1"
2
+ VERSION = "0.8.2"
3
3
  end
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
- Instana.agent = Instana::Agent.new
21
- Instana.collectors = []
22
- Instana.logger = Logger.new(STDOUT)
23
- Instana.logger.info "Stan is on the scene. Starting Instana instrumentation."
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
- Instana.pid = Process.pid
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
- if ::Instana.agent.host_agent_ready?
42
- ::Instana.agent.announce_sensor
43
- require "instana/collectors"
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.1
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-04 00:00:00.000000000 Z
11
+ date: 2016-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler