apminsight 1.0.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/VERSION +1 -1
  2. data/apm-agent.gemspec +64 -0
  3. data/conf/apminsight.conf +15 -24
  4. data/lib/agent/am_objectholder.rb +25 -19
  5. data/lib/agent/api/custom_tracker.rb +79 -0
  6. data/lib/agent/configuration/am_configuration.rb +249 -37
  7. data/lib/agent/handler/custom_api_handler.rb +40 -0
  8. data/lib/agent/handler/sequence_book.rb +118 -0
  9. data/lib/agent/handler/tracker_handler.rb +58 -0
  10. data/lib/agent/logging/am_logger.rb +13 -9
  11. data/lib/agent/metrics/am_metricsformatter.rb +117 -59
  12. data/lib/agent/metrics/am_metricsparser.rb +195 -468
  13. data/lib/agent/metrics/am_metricstore.rb +7 -6
  14. data/lib/agent/metrics/exception_record.rb +24 -0
  15. data/lib/agent/server/am_agent.rb +42 -17
  16. data/lib/agent/server/am_connector.rb +65 -21
  17. data/lib/agent/server/instrument/action_view.rb +64 -0
  18. data/lib/agent/server/instrument/active_record.rb +52 -0
  19. data/lib/agent/server/instrument/am_apm.rb +107 -97
  20. data/lib/agent/server/instrument/am_instrumenter.rb +54 -42
  21. data/lib/agent/server/instrument/environment.rb +42 -0
  22. data/lib/agent/server/instrument/rails.rb +56 -0
  23. data/lib/agent/server/instrument/sinatra.rb +97 -0
  24. data/lib/agent/server/worker/am_worker.rb +93 -49
  25. data/lib/agent/trackers/database_tracker.rb +107 -0
  26. data/lib/agent/trackers/default_tracker.rb +57 -0
  27. data/lib/agent/trackers/root_tracker.rb +43 -0
  28. data/lib/agent/util/am_constants.rb +46 -3
  29. data/lib/agent/util/am_util.rb +64 -1
  30. data/lib/agent/util/transaction_util.rb +35 -0
  31. data/lib/agent/version.rb +13 -0
  32. data/lib/apminsight.rb +4 -1
  33. metadata +114 -76
@@ -1,43 +1,55 @@
1
- require 'agent/am_objectholder'
2
- require 'socket'
3
- module ManageEngine
4
- class APMInstrumenter
5
- @t =nil;
6
- def initialize
7
- @obj=ManageEngine::APMObjectHolder.instance
8
- end
1
+ ##
2
+ ## This is Rails framework specific tracking mechanism, they are moved to multiple files for separate tracking
3
+ ##
9
4
 
10
- def doSubscribe
11
- @obj=ManageEngine::APMObjectHolder.instance
12
- @obj.log.debug "[ instrumenter ] [ Subscriber for Agent ]"
13
- @subscriber = ActiveSupport::Notifications.subscribe do |name, start, finish, id, payload|
14
- if(ManageEngine::APMObjectHolder.instance.config.agent_enabled)
15
- rt = (finish-start).to_i
16
- ManageEngine::APMWorker.getInstance.start
17
- ManageEngine::APMObjectHolder.instance.log.debug "[ Notifications for Agent ] #{Thread.current} #{id} #{name} - #{rt} - #{payload}"
18
- trace= caller;
19
- id = "#{Thread.current}"
20
- stats = Hash.new
21
- stats["name"] = name;
22
- stats["start"] = start.to_f * 1000;
23
- stats["end"] = finish.to_f * 1000;
24
- stats["id"] = id;
25
- stats["payload"] = payload;
26
- if (name=="sql.active_record" && (finish-start)>=(ManageEngine::APMObjectHolder.instance.config.sql_trace_t * 1000 ).to_i)
27
- stats["trace"] = trace;
28
- end
29
- stats["ctime"] =ManageEngine::APMObjectHolder.instance.util.currenttimemillis;
30
- ManageEngine::APMObjectHolder.instance.collector.updateTransaction(id,stats);
31
- else
32
- ActiveSupport::Notifications.unsubscribe @subscriber
33
- @obj.log.info "[ instrumenter ] [ RETURNING NO METRICS] "
34
- end
35
- end
36
- end
37
-
38
- def doUnSubscribe
39
- ActiveSupport::Notifications.unsubscribe @subscriber
40
- end
41
-
42
- end #class
43
- end#module
5
+ #require 'agent/am_objectholder'
6
+ #require 'socket'
7
+ #module ManageEngine
8
+ # class APMInstrumenter
9
+ # @t =nil;
10
+ # def initialize
11
+ # @obj=ManageEngine::APMObjectHolder.instance
12
+ # end
13
+ #
14
+ # def doSubscribe
15
+ # @obj=ManageEngine::APMObjectHolder.instance
16
+ # @obj.log.debug "[ instrumenter ] [ Subscriber for Agent ]"
17
+ # @subscriber = ActiveSupport::Notifications.subscribe do |name, start, finish, id, payload|
18
+ # if(ManageEngine::APMObjectHolder.instance.config.agent_enabled)
19
+ # #rt = (finish-start).to_i
20
+ # ManageEngine::APMWorker.getInstance.start
21
+ # ManageEngine::APMObjectHolder.instance.log.debug "[ Notifications for Agent ] #{Thread.current} #{id} #{name} - #{payload[:path]}"
22
+ # #trace= caller;
23
+ # #puts ">>> Threadlocal var : #{Thread.current[:apminsight]}"
24
+ # if name=="sql.active_record"
25
+ # #Thread.current[:apminsight] = "#{Thread.current[:apminsight]} + #{payload[:sql]}"
26
+ # if payload[:name] != "SCHEMA"
27
+ # @obj.log.debug ">>>>>>>> SQL: #{payload[:sql]}"
28
+ # end
29
+ # @obj.log.debug "~~~~~ SQL Payload: #{payload}"
30
+ # end
31
+ # id = "#{Thread.current}"
32
+ # stats = Hash.new
33
+ # stats["name"] = name;
34
+ # stats["start"] = start.to_f * 1000;
35
+ # stats["end"] = finish.to_f * 1000;
36
+ # stats["id"] = id;
37
+ # stats["payload"] = payload;
38
+ # if (name=="sql.active_record" && (finish.to_f - start.to_f)>=(ManageEngine::APMObjectHolder.instance.config.sql_trace_t).to_f)
39
+ # stats["trace"] = caller(20); # Taking stacktrace of depth 20
40
+ # end
41
+ # stats["ctime"] =ManageEngine::APMObjectHolder.instance.util.currenttimemillis;
42
+ # ManageEngine::APMObjectHolder.instance.collector.updateTransaction(id,stats);
43
+ # else
44
+ # ActiveSupport::Notifications.unsubscribe @subscriber
45
+ # @obj.log.info "[ instrumenter ] [ RETURNING NO METRICS] "
46
+ # end
47
+ # end
48
+ # end
49
+ #
50
+ # def doUnSubscribe
51
+ # ActiveSupport::Notifications.unsubscribe @subscriber
52
+ # end
53
+ #
54
+ #end #class
55
+ #end#module
@@ -0,0 +1,42 @@
1
+ require 'agent/server/instrument/rails'
2
+ require 'agent/server/instrument/sinatra'
3
+ require 'agent/server/instrument/active_record'
4
+ require 'agent/server/instrument/action_view'
5
+
6
+ module ManageEngine
7
+ class Environment
8
+
9
+ SUPPORTED_FRAMEWORKS = [
10
+ ManageEngine::Instrumentation::RailsFramework.new,
11
+ ManageEngine::Instrumentation::SinatraFramework.new
12
+ ]
13
+
14
+ DATABASE_INTERCEPTORS = [
15
+ ManageEngine::Instrumentation::ActiveRecordSQL.new
16
+ ]
17
+
18
+ OTHER_INTERCEPTORS = [
19
+ ManageEngine::Instrumentation::ActionView.new
20
+ ]
21
+
22
+ def detect_and_instrument
23
+ @framework ||= SUPPORTED_FRAMEWORKS.detect{ |framework| framework.present? }
24
+ if (@framework != nil)
25
+ @framework.instrument
26
+ end
27
+
28
+ DATABASE_INTERCEPTORS.each do |interceptor|
29
+ if (interceptor.present?)
30
+ interceptor.instrument
31
+ end
32
+ end
33
+
34
+ OTHER_INTERCEPTORS.each do |interceptor|
35
+ if (interceptor.present?)
36
+ interceptor.instrument
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,56 @@
1
+ require 'agent/handler/tracker_handler'
2
+
3
+ module ManageEngine
4
+ module Instrumentation
5
+ class RailsFramework
6
+
7
+ def present?
8
+ defined?(::Rails) && defined?(::ActionController)
9
+ end
10
+
11
+ def version
12
+ Rails::VERSION::STRING
13
+ end
14
+
15
+ def env
16
+ if Rails::VERSION::MAJOR >= 3
17
+ ::Rails.env
18
+ else
19
+ RAILS_ENV.dup
20
+ end
21
+ end
22
+
23
+ def name
24
+ 'Rails'
25
+ end
26
+
27
+ def instrument
28
+ @obj = ManageEngine::APMObjectHolder.instance
29
+ @obj.log.info "Instrumenting ActionController.. Rails Version: #{version}"
30
+ @railsTracker = nil
31
+
32
+ ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |name, start, finish, id, payload|
33
+ path = payload[:path].partition("?")[0]
34
+ @railsTracker = ManageEngine::Tracker::RootTracker.new("#{payload[:controller]}.#{payload[:action]}", start.to_f * 1000)
35
+ @railsTracker.url=(path)
36
+ @railsTracker = ManageEngine::Agent::TrackerHandler.invokeTracker(@railsTracker)
37
+ end # subscribe
38
+
39
+
40
+ ActiveSupport::Notifications.subscribe('process_action.action_controller') do |name, start, finish, id, payload|
41
+ if @railsTracker != nil
42
+ @railsTracker.finish(finish.to_f * 1000)
43
+ exception = payload[:exception_object]
44
+ if exception != nil
45
+ @railsTracker.setError(exception)
46
+ @railsTracker.setStatus(500) # By default, set 500 as status for error txns
47
+ end
48
+ ManageEngine::Agent::TrackerHandler.exitTracker(@railsTracker)
49
+ end
50
+ end
51
+
52
+ end # def instrument
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,97 @@
1
+ require 'agent/am_objectholder'
2
+ require 'agent/trackers/root_tracker'
3
+
4
+ module ManageEngine
5
+ module Instrumentation
6
+ class SinatraFramework
7
+
8
+ def present?
9
+ defined?(::Sinatra) && defined?(::Sinatra::Base)
10
+ end
11
+
12
+ def version
13
+ ::Sinatra::VERSION
14
+ end
15
+
16
+ def env
17
+ ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
18
+ end
19
+
20
+ def name
21
+ 'Sinatra'
22
+ end
23
+
24
+ def instrument
25
+ ManageEngine::APMObjectHolder.instance.log.info "Instrumenting Sinatra framework. Version: #{version}"
26
+ ::Sinatra::Base.class_eval do
27
+ include ManageEngine::Instrumentation::APMInsightSinatra
28
+
29
+ alias original_route_eval route_eval
30
+ alias route_eval apminsight_route_eval
31
+
32
+ # alias sinatra_exception_handler! handle_exception!
33
+ # alias handle_exception! apminsight_exception_handler!
34
+
35
+ end # class_eval
36
+ end # def instrument
37
+
38
+ end # class Sinatra
39
+
40
+ module APMInsightSinatra
41
+
42
+ def apminsight_route_eval(*args, &block)
43
+
44
+ # http://www.rubydoc.info/github/rack/rack/master/Rack/Request
45
+ url = (env.has_key?('sinatra.route') ? env['sinatra.route'] : @request.path).dup
46
+ @obj = ManageEngine::APMObjectHolder.instance
47
+
48
+ sinatraTracker = ManageEngine::Tracker::RootTracker.new(url)
49
+ sinatraTracker.url=(url)
50
+ sinatraTracker = ManageEngine::Agent::TrackerHandler.invokeTracker(sinatraTracker)
51
+
52
+ # TODO: capture all additional details @request.query_string @request.params
53
+
54
+ begin
55
+ original_route_eval(*args, &block)
56
+
57
+ rescue Exception => e # On application error, above method throws exception
58
+ if (sinatraTracker != nil)
59
+ sinatraTracker.setError(e)
60
+ sinatraTracker.setStatus(500) # By default, set 500 as status for error txns
61
+ end
62
+ raise e
63
+
64
+ ensure
65
+ if sinatraTracker != nil
66
+ sinatraTracker.finish
67
+ end
68
+ ManageEngine::Agent::TrackerHandler.exitTracker(sinatraTracker)
69
+ end
70
+
71
+ end
72
+
73
+ # def apminsight_exception_handler!(*args, &block)
74
+ # begin
75
+ # sinatra_exception_handler!(*args, &block)
76
+ # ensure
77
+ # tracker = Thread.current[:apminsight]
78
+ # puts "tracker is #{(tracker == nil)}"
79
+ # if tracker != nil
80
+ # tracker.error(args[0]) # Other way, env[sinatra.error]
81
+ # tracker.status(@response.status)
82
+ # finishTracker tracker
83
+ # end #if
84
+ # end#begin
85
+ # end#def
86
+ #
87
+ # def finishTracker(tracker)
88
+ # tracker.finish
89
+ # #ManageEngine::APMObjectHolder.instance.collector.updateTransaction(id,stats)
90
+ # puts tracker.to_s
91
+ # Thread.current[:apminsight] = nil
92
+ # end
93
+
94
+ end # module SinatraFramework
95
+
96
+ end # module Instrumentation
97
+ end
@@ -7,46 +7,63 @@ class APMWorker
7
7
  @status = 'not_init'
8
8
  @id = 0
9
9
  attr_accessor :id
10
- def initialize
11
- @status = "initialized"
12
- @id = Process.pid
13
- end
14
-
15
- def start
16
- @obj = ManageEngine::APMObjectHolder.instance
17
-
18
- if @status=="working"
19
- @obj.log.debug "woker thread already started"
20
- elsif @status == "initialized"
21
- @obj.log.info "start worker thread for - #{Process.pid} :: #{@status} "
22
- #@obj.log.info "Starting APMWorker Thread #{Process.pid} "
23
- @apm = Thread.new do
24
- @status = 'working'
25
- while !@obj.shutdown do
26
- checkforagentstatus
27
- updateConfig
28
- dc
29
- sleep (@obj.config.connect_interval).to_i
30
- end#w
31
- @status= "end"
32
- @obj.log.debug "Worker thread ends"
33
- end
34
- end
35
- end
10
+ def initialize
11
+ @status = "initialized"
12
+ @id = Process.pid
13
+ end
36
14
 
37
- def self.getInstance
38
- if(@work==nil || @work.id!=Process.pid)
39
- @work = ManageEngine::APMWorker.new
40
- end
41
- return @work
15
+ def start
16
+ @obj = ManageEngine::APMObjectHolder.instance
17
+
18
+ if @status=="working"
19
+ @obj.log.debug "woker thread already started"
20
+ elsif @status == "initialized"
21
+ @obj.log.info "start worker thread for - #{Process.pid} :: #{@status} "
22
+ #@obj.log.info "Starting APMWorker Thread #{Process.pid} "
23
+ @apm = Thread.new do
24
+ @status = 'working'
25
+ while !@obj.shutdown do
26
+ checkforagentstatus
27
+ updateConfig
28
+ dc
29
+ sleep (@obj.config.connect_interval).to_i
30
+ end#w
31
+ @status= "end"
32
+ @obj.log.debug "Worker thread ends"
42
33
  end
34
+ end
35
+ end
36
+
37
+ def self.getInstance
38
+ if(@work==nil || @work.id!=Process.pid)
39
+ @work = ManageEngine::APMWorker.new
40
+ end
41
+ return @work
42
+ end
43
43
 
44
44
  def updateConfig
45
45
  if(@obj.config.lastupdatedtime!=File.mtime(@obj.constants.apm_conf).to_i)
46
- @obj.log.info "Configuration File Changed... So Updating Configuration."
46
+ @obj.log.info "Configuration File Changed... So Updating Configuration."
47
+ agent_config_data = @obj.config.getAgentConfigData
47
48
  @obj.config.lastupdatedtime=File.mtime(@obj.constants.apm_conf).to_i
48
49
  @obj.config.configureFile
49
50
  @obj.config.assignConfig
51
+ new_agent_config_data = @obj.config.getAgentConfigData
52
+ sendUpdate = "false"
53
+ agent_config_data.each do|key,value|
54
+ if key != "last.modified.time"
55
+ newValue = new_agent_config_data[key]
56
+ if value != newValue
57
+ sendUpdate = "true"
58
+ end
59
+ end
60
+ end
61
+ if sendUpdate == "true"
62
+ @obj.log.info "sending update to server #{new_agent_config_data}"
63
+ data1 = Hash.new
64
+ data1["custom_config_info"]=new_agent_config_data
65
+ resp = @obj.connector.post @obj.constants.connect_config_update_uri+@obj.config.instance_id,data1
66
+ end
50
67
  end
51
68
  end
52
69
 
@@ -57,12 +74,12 @@ class APMWorker
57
74
  @obj.log.info "Agent in Disabled State."
58
75
  if prevState
59
76
  @obj.log.info "Agent in Disabled State. Going to unsubscribe"
60
- @obj.instrumenter.doUnSubscribe
77
+ # @obj.instrumenter.doUnSubscribe
61
78
  end
62
79
  else
63
80
  if !prevState
64
81
  @obj.log.info "Agent in Active State."
65
- @obj.instrumenter.doSubscribe
82
+ # @obj.instrumenter.doSubscribe
66
83
  end
67
84
  end
68
85
  end
@@ -126,14 +143,18 @@ class APMWorker
126
143
  tdata.concat(val[0])
127
144
  when 2
128
145
  tdata.concat(val[0])
129
- trdata.concat(val[1])
146
+ if (trdata.size < @obj.config.trace_overflow_t)
147
+ trdata.concat(val[1])
148
+ end
130
149
  end
131
150
  end
132
151
  result.push(merge(tdata))
133
152
  resp = @obj.connector.post @obj.constants.connect_data_uri+@obj.config.instance_id,result
153
+ @obj.log.info "#{tdata.size} metric(s) dispatched."
134
154
  if trdata.size>0
135
155
  result[2]=trdata;
136
156
  resp = @obj.connector.post @obj.constants.connect_trace_uri+@obj.config.instance_id,result
157
+ @obj.log.info "#{trdata.size} trace(s) dispatched."
137
158
  end
138
159
  end
139
160
 
@@ -149,7 +170,7 @@ class APMWorker
149
170
  def send_save data
150
171
  begin
151
172
  if FileTest.exist?(@obj.constants.agent_lock)
152
- if Time.now.to_i - File.mtime(@obj.constants.agent_lock).to_i > (@obj.config.connect_interval).to_i
173
+ if Time.now.to_i - File.mtime(@obj.constants.agent_lock).to_i >= (@obj.config.connect_interval).to_i
153
174
  @obj.log.debug "worker send signal"
154
175
  senddata data
155
176
  else
@@ -172,7 +193,11 @@ class APMWorker
172
193
  f.flock(File::LOCK_EX)
173
194
  begin
174
195
  f.each_line do |line|
175
- data.push(JSON.parse(line))
196
+ begin
197
+ data.push(JSON.parse(line))
198
+ rescue Exception=>ex
199
+ @obj.log.logException "Error Parsing data, Skipping line #{line}", ex
200
+ end
176
201
  end
177
202
  f.truncate 0
178
203
  rescue Exception=>e
@@ -223,18 +248,36 @@ class APMWorker
223
248
 
224
249
 
225
250
  def mapdx res,dat
226
- res[0] = res[0]+dat[0];
227
- if dat[1]<res[1]
228
- res[1]=dat[1]
229
- end
230
- if dat[2]>res[2]
231
- res[2]=dat[2]
232
- end
233
- res[3] = res[3]+dat[3]
234
- res[5] = res[5]+dat[5]
235
- res[6] = res[6]+dat[6]
236
- res[7] = res[7]+dat[7]
237
- res[4] = (res[5].to_f + (res[6].to_f/2).to_f).to_f/res[3].to_f
251
+ begin
252
+ rtData = res[0];
253
+ rtData[0] = rtData[0]+dat[0][0];
254
+ if dat[0][1]<rtData[1]
255
+ rtData[1]=dat[0][1]
256
+ end
257
+ if dat[0][2]>rtData[2]
258
+ rtData[2]=dat[0][2]
259
+ end
260
+ rtData[3] = rtData[3]+dat[0][3]
261
+ rtData[5] = rtData[5]+dat[0][5]
262
+ rtData[6] = rtData[6]+dat[0][6]
263
+ rtData[7] = rtData[7]+dat[0][7]
264
+ rtData[4] = rtData[3] != 0 ? (rtData[5].to_f + (rtData[6].to_f/2).to_f).to_f/rtData[3].to_f : 0
265
+ res[0] = rtData
266
+
267
+ resExcepData = res[1][@obj.constants.mf_logmetric]
268
+ excepData = dat[1][@obj.constants.mf_logmetric]
269
+ if (resExcepData == nil)
270
+ resExcepData = excepData
271
+ else
272
+ if (excepData != nil)
273
+ resExcepData = resExcepData.merge(excepData){|key, oldval, newval| newval + oldval}
274
+ end
275
+ end
276
+
277
+ res[1][@obj.constants.mf_logmetric] = resExcepData != nil ? resExcepData : Hash.new
278
+ rescue Exception=>e
279
+ @obj.log.logException "Exception while merging data",e
280
+ end
238
281
  res
239
282
  end
240
283
 
@@ -247,6 +290,7 @@ class APMWorker
247
290
  res[2]=dat[2]
248
291
  end
249
292
  res[3] = res[3]+dat[3]
293
+ res[4] = res[4]+dat[4]
250
294
  res
251
295
  end
252
296