apminsight 1.0.1 → 1.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.
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