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