apminsight 1.0.1 → 1.8.6

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 +284 -41
  7. data/lib/agent/handler/custom_api_handler.rb +40 -0
  8. data/lib/agent/handler/sequence_book.rb +125 -0
  9. data/lib/agent/handler/tracker_handler.rb +58 -0
  10. data/lib/agent/logging/am_logger.rb +20 -11
  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 +46 -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 +62 -0
  27. data/lib/agent/trackers/root_tracker.rb +43 -0
  28. data/lib/agent/util/am_constants.rb +53 -8
  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,14 +1,15 @@
1
1
  require 'rubygems'
2
2
  require 'json'
3
3
  require 'socket'
4
- require 'rails'
4
+ require 'net/http'
5
5
  require 'agent/am_objectholder'
6
+ require 'agent/version'
6
7
 
7
8
  module ManageEngine
8
9
  class APMConfig
9
- attr_reader :agenthost,:agentport,:instance_id,:alreadyconnected,:apmhost,:apmport
10
- attr_reader :appname,:proxyneeded, :apdex_t, :trans_trace, :trans_trace_t, :sql_capture, :sql_capture_params, :sql_trace_t,:proxy_user,:proxy_pass
11
- attr_reader :proxy_host,:proxy_port ,:is_secured, :logs_dir ,:connection_retry,:agent_enabled,:connect_interval,:db_operations,:include_packages
10
+ attr_reader :agenthost,:agentport,:instance_id,:alreadyconnected,:apmhost,:apmport,:license_key,:site24x7, :site24x7url, :hostType
11
+ attr_reader :appname,:proxyneeded, :apdex_t, :trans_trace, :trans_trace_t, :sql_capture, :sql_capture_params, :sql_trace_t,:proxy_user,:proxy_pass, :metric_overflow_t, :trace_overflow_t, :dbmetric_overflow_t
12
+ attr_reader :proxy_host,:proxy_port ,:is_secured, :logs_dir ,:connection_retry,:agent_enabled,:connect_interval,:db_operations,:txn_skip_listen, :url_merge_pattern
12
13
  attr_accessor :app_db,:app_dispatcher,:lastupdatedtime
13
14
  def initialize
14
15
  @obj = ManageEngine::APMObjectHolder.instance
@@ -21,19 +22,31 @@ module ManageEngine
21
22
  @instance_id = 0
22
23
  @agent_enabled = false
23
24
  @alreadyconnected = checkAgentInfo
25
+ @site24x7 = checkLicenseFile
26
+ if (@site24x7)
27
+ @site24x7url = @license_key.start_with?('eu_') ? @obj.constants.site24x7EUurl : @license_key.start_with?('cn_') ? @obj.constants.site24x7CNurl : @license_key.start_with?('au_') ? @obj.constants.site24x7AUurl : @license_key.start_with?('in_') ? @obj.constants.site24x7INurl : @license_key.start_with?('gd_') ? @obj.constants.site24x7GDurl : @license_key.start_with?('jp_') ? @obj.constants.site24x7JPurl : @obj.constants.site24x7USurl
28
+ end
24
29
  @db_operations =["select","insert","update","delete"]
25
- @obj.log.debug "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
26
- @obj.log.debug "APP HOME #{File.absolute_path(".")} "
27
- @obj.log.debug "APP HOME #{Dir.pwd} "
28
- @obj.log.debug "Configuration : "
29
- @obj.log.debug "Agent Name : #{@agenthost}"
30
- @obj.log.debug "Agent Already Connected : #{@alreadyconnected}"
31
- @obj.log.debug "Agent Enabled : #{@agent_enabled}"
32
- @obj.log.debug "Allowed DB Operations : #{@db_operations}"
33
- @config.each do|key,val|
34
- @obj.log.info "#{key} => #{val}"
35
- end
36
- @obj.log.debug "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
30
+ urlMergePattern
31
+ @hostType = getHostType
32
+ @obj.log.info "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
33
+ @obj.log.info "APP HOME #{File.absolute_path(".")} "
34
+ @obj.log.info "APP HOME #{Dir.pwd} "
35
+ @obj.log.info "Agent Version : #{ManageEngine::APMInsight::VERSION}"
36
+ #@obj.log.info "Configuration : "
37
+ #@obj.log.info "Hostname : #{@agenthost}"
38
+ @obj.log.info "Host Type: #{@hostType}"
39
+ @obj.log.info "Agent Already Connected : #{@alreadyconnected}"
40
+ @obj.log.info "Agent Enabled : #{@agent_enabled}"
41
+ @obj.log.info "Allowed DB Operations : #{@db_operations}"
42
+ # @config.each do|key,val|
43
+ # @obj.log.info "#{key} => #{val}"
44
+ # end
45
+ @obj.log.info "URL Merge Patterns"
46
+ @url_merge_pattern.each do |key, val|
47
+ @obj.log.info "#{key} => #{val}"
48
+ end
49
+ @obj.log.info "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
37
50
  @app_db="dummydb"
38
51
  @app_dispatcher = getDispatcher
39
52
  @lastupdatedtime=File.mtime(@obj.constants.apm_conf).to_i
@@ -41,27 +54,33 @@ module ManageEngine
41
54
 
42
55
  def configureFile
43
56
  begin
44
- gem_conf = Gem.loaded_specs[@obj.constants.apm_gem].full_gem_path
45
- #gem_conf = File.join(gem_conf, 'lib')
46
- gem_conf = File.join(gem_conf, 'conf')
47
- gem_conf = File.join(gem_conf, 'apminsight.conf')
48
57
  if(FileTest.exists?(@obj.constants.apm_conf))
49
58
  #conf file exists in APPlication Home
50
- @obj.log.debug "Config File Read from #{@obj.constants.apm_conf}"
59
+ @obj.log.debug "Config File Exists. It is read from #{@obj.constants.apm_conf}"
51
60
  @config = @obj.util.readProperties(@obj.constants.apm_conf)
61
+ secureConfFile "#{@obj.constants.apm_conf}"
52
62
  else
63
+ gemSpecs = Gem.loaded_specs[@obj.constants.s247_apm_gem]
64
+ if (gemSpecs == nil)
65
+ gemSpecs = Gem.loaded_specs[@obj.constants.apm_gem]
66
+ end
67
+ gem_conf = gemSpecs.full_gem_path
68
+ #gem_conf = File.join(gem_conf, 'lib')
69
+ gem_conf = File.join(gem_conf, 'conf')
70
+ gem_conf = File.join(gem_conf, 'apminsight.conf')
53
71
  #conf file not exists in APPlications Home. So 1. copy it for gem locations
54
72
  if @obj.util.copyFiles gem_conf,@obj.constants.apm_conf
55
73
  #copied sucessfully
56
- @obj.log.debug "Config File Read from #{@obj.constants.apm_conf}"
74
+ @obj.log.info "Config File copied to application home directory. It is read from #{@obj.constants.apm_conf}"
57
75
  @config = @obj.util.readProperties(@obj.constants.apm_conf)
76
+ secureConfFile "#{@obj.constants.apm_conf}"
58
77
  else
59
78
  #Problem in copying, so reading props from Conf file in Gem Location
60
- @obj.log.debug "Config File Read from #{gem_conf}"
79
+ @obj.log.warn "Config File not copied. It is read from #{gem_conf}"
61
80
  @config = @obj.util.readProperties(gem_conf)
81
+ secureConfFile gem_conf
62
82
  end
63
83
  end
64
-
65
84
  rescue Exception=>e
66
85
  @obj.log.info "[Exception] Problem in Reading Configuration File : \n File : #{@obj.constants.apm_conf}"
67
86
  @obj.log.logException "#{e.message}",e
@@ -70,11 +89,29 @@ module ManageEngine
70
89
  end
71
90
  end
72
91
 
92
+ def secureConfFile(file)
93
+ begin
94
+ File.chmod(0600, file)
95
+ rescue Exception => e
96
+ @obj.log.warn "Unable to secure the conf file #{file}"
97
+ end
98
+ end
99
+
73
100
  def checkAgentInfo
74
101
  if FileTest.exists?(@obj.constants.agent_conf)
75
102
  @obj.log.debug "Status : Agent Already Connected"
76
103
  props = @obj.util.readProperties(@obj.constants.agent_conf)
77
- @instance_id = props["agent.id"]
104
+ instance_id = props["agent.id"]
105
+
106
+ if (instance_id == nil || instance_id == "")
107
+ # If instance id is not found or empty, it means the apminsight.info is being modified by user
108
+ # Ignore all its entry
109
+ @obj.log.warn "File: #{@obj.constants.agent_conf} is corrupted. Agent will continue ignoring these values."
110
+ return false
111
+ else
112
+ @instance_id = instance_id
113
+ end
114
+
78
115
  @agent_enabled= @obj.util.getBooleanValue props["agent.enabled"]
79
116
  true
80
117
  else
@@ -83,10 +120,35 @@ module ManageEngine
83
120
  end
84
121
  end
85
122
 
123
+ def checkLicenseFile
124
+ @obj.constants.setLicenseKey @license_key
125
+ if(@license_key.start_with?('APMI_'))
126
+ @obj.log.info "Connecting to App Manager"
127
+ false
128
+ else
129
+ @obj.log.info "Connecting to Site24x7"
130
+ true
131
+ end
132
+ end
133
+
134
+ def urlMergePattern
135
+ @url_merge_pattern = Hash.new
136
+ begin
137
+ if (FileTest.exists?(@obj.constants.mergepattern_conf))
138
+ @url_merge_pattern=@obj.util.readProperties(@obj.constants.mergepattern_conf)
139
+ end
140
+ rescue Exception => e
141
+ @obj.log.info "[Exception] Problem in Reading Configuration File : \n File : #{@obj.constants.mergepattern_conf}"
142
+ @obj.log.logException "#{e.message}",e
143
+ end
144
+ end
145
+
86
146
  def updateAgentInfoFile(props)
87
147
  @instance_id = props["agent.id"]
88
148
  @agent_enabled= @obj.util.getBooleanValue props["agent.enabled"]
89
149
  @obj.util.writeProperties(@obj.constants.agent_conf,props)
150
+
151
+ secureConfFile(@obj.constants.agent_conf)
90
152
  end
91
153
 
92
154
  def initValues
@@ -102,18 +164,30 @@ module ManageEngine
102
164
  @connection_retry = 0
103
165
  @connect_interval = 60
104
166
  @apdex_t=0.5
105
- @include_packages=Array.new
167
+ @txn_skip_listen=Array.new
106
168
  @trans_trace_t=2
107
169
  @sql_trace_t=3
170
+ @metric_overflow_t=250
171
+ @dbmetric_overflow_t=500
172
+ @trace_overflow_t=30
173
+ @site24x7url = @obj.constants.site24x7USurl #default agent communication URL
108
174
  end
109
175
 
110
176
  def assignConfig
111
177
  initValues
112
178
  @config.each do |key,value|
179
+ value = checkAndGetEnvValue(value)
113
180
  case key
114
181
  when "application.name" then @appname=value
182
+ if (ENV.has_key?('APM_APPLICATION_NAME'))
183
+ @appname = ENV['APM_APPLICATION_NAME']
184
+ end
115
185
  when "apm.host" then @apmhost=value
116
186
  when "apm.port" then @apmport=isInteger(@apmport,value)
187
+ when "license.key" then @license_key=value
188
+ if (@license_key.empty? && ENV.has_key?('S247_LICENSE_KEY'))
189
+ @license_key = ENV['S247_LICENSE_KEY']
190
+ end
117
191
  when "behind.proxy" then @proxyneeded=@obj.util.getBooleanValue value
118
192
  when "agent.server.port" then @agentport=isInteger(@agentport,value)
119
193
  when "apdex.threshold" then @apdex_t=isFloat(@apdex_t,value)
@@ -123,46 +197,175 @@ module ManageEngine
123
197
  when "transaction.trace.sql.parametrize" then @sql_capture_params=@obj.util.getBooleanValue value
124
198
  when "transaction.trace.sql.stacktrace.threshold" then @sql_trace_t=isFloat(@sql_trace_t,value)
125
199
  when "proxy.server.host" then @proxy_host=value
126
- when "proxy.server.port" then @proxy_port=value
200
+ when "proxy.server.port" then @proxy_port=isInteger(@proxy_port,value)
127
201
  when "proxy.auth.username" then @proxy_user=value
128
- when "proxy.auth.password" then @proxy_pass=value
129
- when "apminsight.secure" then @is_secured=@obj.util.getBooleanValue value
202
+ when "proxy.auth.password" then @proxy_pass=@obj.util.decrypt value, @license_key
203
+ if (@proxy_pass == nil)
204
+ @proxy_pass = value
205
+ end
206
+ when "apm.protocol.https" then @is_secured=@obj.util.getBooleanValue value
130
207
  when "apminsight.log.dir" then @logs_dir=value
208
+ when "apminsight.log.level" then @obj.log.setLevel value
131
209
  when "agent.connection.retry" then @connection_retry=value #Not in Conf - yet to come
132
- when "agent.connection.interval" then @connect_interval=value#Not in Conf - yet to come
133
- when "include.packages" then @include_packages=@obj.util.getArray value,","
210
+ when "agent.polling.interval" then @connect_interval=isInteger(@connect_interval, value)#Not in Conf - yet to come
211
+ when "transaction.skip.listening" then @txn_skip_listen=@obj.util.getArray value.gsub("\s", ""),","
212
+ when "metricstore.metric.bucket.size" then @metric_overflow_t = isInteger(@metric_overflow_t, value)
213
+ when "metricstore.dbmetric.bucket.size" then @dbmetric_overflow_t = isInteger(@dbmetric_overflow_t, value)
214
+ when "transaction.tracestore.size" then @trace_overflow_t = isInteger(@trace_overflow_t, value)
134
215
  end
135
216
  end
217
+ store_encrypted_data(@config)
218
+ end
219
+
220
+ #Checks whether the given value is Environment Variable
221
+ def checkAndGetEnvValue(data)
222
+ begin
223
+ value = "#{data}"[/\{(.*)\}/,1]
224
+ if (value != nil && ENV.has_key?(value))
225
+ return data.gsub(/\{.*\}/, ENV[value])
226
+ end
227
+ rescue Exception=>e
228
+ end
229
+ return data
136
230
  end
137
231
 
232
+ def store_encrypted_data config
233
+ data = config["proxy.auth.password"]
234
+ if (data != nil && @obj.util.decrypt(data, @license_key) == nil) # checking whether data is already encrypted
235
+ begin
236
+ file_contents = ""
237
+ conf_file = File.open(@obj.constants.apm_conf, 'r')
238
+ conf_file.read.each_line do |line|
239
+ if line.start_with?("proxy.auth.password")
240
+ file_contents += "proxy.auth.password=" + @obj.util.encrypt(config["proxy.auth.password"], @license_key)
241
+ else
242
+ file_contents += line
243
+ end
244
+ #file_contents += "\n"
245
+ end #end of do read loop
246
+ conf_file.close
247
+ conf_file = File.open(@obj.constants.apm_conf, "w+")
248
+ conf_file.puts file_contents
249
+ conf_file.close
250
+ file_contents = nil # clearing memory
251
+ rescue Exception=>e
252
+ @obj.log.logException "Error while encrypting file", e
253
+ end
254
+ end # if already encrypted
255
+ end
256
+
257
+ def getHostType
258
+ begin
259
+ # Check for AWS environment
260
+ url = URI.parse('http://169.254.169.254/latest/meta-data/') # AWS metadata url
261
+ request = Net::HTTP::Get.new(url.path)
262
+ response = Net::HTTP.start(url.host, url.port, :read_timeout => 2) {|http| http.request(request)}
263
+ if (response.kind_of? Net::HTTPOK)
264
+ @hostType = "AWS"
265
+ return @hostType
266
+ end
267
+ rescue Exception => e
268
+ end
269
+
270
+ begin
271
+ #Check for Azure environment
272
+ url = URI.parse('http://169.254.169.254/metadata/v1/maintenance') # Azure metadata url
273
+ request = Net::HTTP::Get.new(url.path)
274
+ response = Net::HTTP.start(url.host, url.port, :read_timeout => 2) {|http| http.request(request)}
275
+ if (response.kind_of? Net::HTTPOK)
276
+ @hostType = "AZURE"
277
+ return @hostType
278
+ end
279
+ rescue Exception => e
280
+ end
281
+
282
+ begin
283
+ # Check for Heroku env. In the backgroud it is using AWS EC2, hence sending as AWS
284
+ if (ENV.has_key?('DYNO') || ENV.has_key?('STACK'))
285
+ @hostType = "AWS"
286
+ return @hostType
287
+ end
288
+ rescue Exception => e
289
+ end
290
+
291
+ @hostType = nil
292
+ end
293
+
138
294
  def getAgentInfo
139
295
  data = Hash.new
140
296
  agentdata = Hash.new
141
- agentdata = {"application.type"=>"RUBY","application.name"=>@appname,"hostname"=>@agenthost,"port"=>@agentport,"agent.version"=>"1.0"}
297
+ agentdata = {"application.type"=>"RUBY","application.name"=>@appname,"hostname"=>@agenthost,"port"=>@agentport,"agent.version"=>ManageEngine::APMInsight::MAJOR_VERSION}
298
+ if (@hostType != nil)
299
+ agentdata["host.type"]=@hostType
300
+ end
142
301
  data["agent_info"]=agentdata
143
302
  data["environment"]=getEnvData
303
+ data["custom_config_info"]=getAgentConfigData
144
304
  data
145
305
  end
146
306
 
147
307
  def getEnvData
148
308
  env = Hash.new
149
309
  begin
150
- ENV.to_hash.each do |key, value|
151
- env[key] = value
152
- end
153
- env["Application Path"]="#{Rails.root}"
310
+ env["OS"] = Gem::Platform.local.os
311
+ env["OS Version"] = Gem::Platform.local.version
312
+ env["OS Arch"] = Gem::Platform.local.cpu
313
+ env["Ruby Version"] = "#{RUBY_VERSION}"
314
+ gemSpecs = Gem.loaded_specs[@obj.constants.s247_apm_gem]
315
+ if (gemSpecs == nil)
316
+ gemSpecs = Gem.loaded_specs[@obj.constants.apm_gem]
317
+ end
318
+ if (gemSpecs != nil)
319
+ env["Agent Installed Path"] = gemSpecs.full_gem_path
320
+ end
321
+
322
+ # ENV.to_hash.each do |key, value|
323
+ # env[key] = value
324
+ # end
325
+ env["Application Path"] = "#{Dir.pwd}"
154
326
  rescue Exception=>e
327
+ @obj.log.warn "Error in capturing env data. #{e.message}"
155
328
  end
156
329
  env
157
330
  end
158
331
 
332
+ def getAgentConfigData
333
+ agentconfig = Hash.new
334
+ agentconfig["last.modified.time"]=@lastupdatedtime*1000
335
+ agentconfig["apdex.threshold"]=@apdex_t
336
+ agentconfig["sql.capture.enabled"]=0
337
+ if @sql_capture
338
+ agentconfig["sql.capture.enabled"]=1
339
+ end
340
+ agentconfig["transaction.trace.enabled"]=0
341
+ if @trans_trace
342
+ agentconfig["transaction.trace.enabled"]=1
343
+ end
344
+ agentconfig["transaction.trace.threshold"]=@trans_trace_t
345
+ agentconfig["transaction.trace.sql.parametrize"]=0
346
+ if @sql_capture_params
347
+ agentconfig["transaction.trace.sql.parametrize"]=1
348
+ end
349
+ agentconfig["transaction.trace.sql.stacktrace.threshold"]=@sql_trace_t
350
+ agentconfig["transaction.tracking.request.interval"]=1
351
+ agentconfig
352
+ end
159
353
 
160
354
  def getDispatcher
161
- dispatcher = "unknown"
162
- if defined?(PhusionPassenger) then
163
- dispatcher = "passenger"
164
- end
165
- dispatcher
355
+
356
+ @obj.log.info "Server: #{ENV['SERVER_SOFTWARE']}"
357
+
358
+ dispatcher = "unknown"
359
+ if defined?(PhusionPassenger) then
360
+ dispatcher = "passenger"
361
+ end
362
+ if defined?(Unicorn) then
363
+ dispatcher = "unicorn"
364
+ end
365
+ if defined?(Rainbows) then
366
+ dispatcher = "rainbows"
367
+ end
368
+ dispatcher
166
369
  end
167
370
 
168
371
  def isInteger default,value
@@ -184,6 +387,46 @@ module ManageEngine
184
387
  end
185
388
  end
186
389
 
390
+ def update_config configInfo
391
+ filepath = @obj.constants.apm_conf
392
+ f = "apminsight.conf.new"
393
+ begin
394
+ propsFile=File.open(filepath, 'r')
395
+ file = File.new(f,"w+")
396
+ propsFile.read.each_line do |line|
397
+ line.strip!
398
+ if (line[0] != ?# and line[0] != ?=)
399
+ i = line.index('=')
400
+ if (i)
401
+ key1 = line[0..i - 1].strip
402
+ if configInfo.has_key?(key1)
403
+ file.puts "#{key1}=#{configInfo[key1]}\n"
404
+ else
405
+ file.puts "#{line}\n"
406
+ end
407
+ else
408
+ file.puts "#{line}\n"
409
+ end
410
+ else
411
+ file.puts "#{line}\n"
412
+ end
413
+ end
414
+ rescue Exception=>e
415
+ @obj.log.info "Problem in Reading / Writing Property File : #{e.message} "
416
+ @obj.log.error "#{e.backtrace}"
417
+ ensure
418
+ propsFile.close
419
+ file.close
420
+ end
421
+ res = @obj.util.copyFiles f, filepath
422
+ if res
423
+ @obj.log.info "copyFiles result = #{res}"
424
+ #delete apminsight.conf.new has to be done
425
+ end
426
+ configureFile
427
+ assignConfig
428
+ end
429
+
187
430
 
188
431
  end#c
189
432
  end#m
@@ -0,0 +1,40 @@
1
+ require 'agent/handler/sequence_book'
2
+
3
+ module APMInsight
4
+ module API
5
+ class CustomAPIHandler
6
+
7
+ ## Create tracker for custom instrumented methods and send them to tracker handler
8
+ def self.invokeTracker name
9
+ begin
10
+ # @obj = ManageEngine::APMObjectHolder.instance
11
+
12
+ if Thread.current[:apminsight] != nil
13
+ tracker = ManageEngine::Tracker::DefaultTracker.new(name)
14
+ tracker = ManageEngine::Agent::TrackerHandler.invokeTracker(tracker)
15
+ return tracker
16
+ end
17
+
18
+ return nil
19
+ rescue Exception=>e
20
+ return nil
21
+ end
22
+ end
23
+
24
+ def self.exitTracker tracker
25
+ if tracker != nil
26
+ tracker.finish
27
+ ManageEngine::Agent::TrackerHandler.exitTracker(tracker)
28
+ end
29
+ end
30
+
31
+ def self.track_exception exception
32
+ seqBook = Thread.current[:apminsight]
33
+ if seqBook != nil
34
+ seqBook.addExceptionInfo exception
35
+ end
36
+ end
37
+
38
+ end #class CustomAPIhandler
39
+ end #module API
40
+ end
@@ -0,0 +1,125 @@
1
+ require 'agent/metrics/exception_record'
2
+
3
+ module APMInsight
4
+ module Agent
5
+ class SequenceBook
6
+ attr_reader :openTracker, :closedTracker, :rootTracker, :trackerCount, :closedTrackerCount, :exceptionBag, :listenFlag
7
+
8
+ def initialize
9
+ @rootTracker = createDummyTracker()
10
+ @closedTracker = @rootTracker
11
+ @openTracker = nil
12
+
13
+ @trackerCount = 0
14
+ @closedTrackerCount = 0
15
+ @listenFlag = -1
16
+ # @exceptionBag = Array.new
17
+ end
18
+
19
+ def attachTracker tracker
20
+ if tracker == nil
21
+ return nil
22
+ end
23
+
24
+ # If RootTracker is not set, check type and set
25
+ if @rootTracker == @closedTracker
26
+ if !tracker.is_a?(ManageEngine::Tracker::RootTracker)
27
+ closeSequence()
28
+ return nil
29
+ end
30
+ @rootTracker = tracker
31
+
32
+ updateListenFlag()
33
+ end
34
+
35
+
36
+ # Attach tracker as Sibling or Child and set nominee
37
+ if @closedTracker != nil
38
+ tracker.sibling = @closedTracker
39
+ @closedTracker.sibling = tracker # Nominee - if dropped/corrupted, defaults to this tracker
40
+ @openTracker = tracker
41
+ @closedTracker = nil
42
+ else
43
+ if tracker.equal?(@openTracker)
44
+ return nil
45
+ end
46
+
47
+ @openTracker.child = tracker
48
+ tracker.sibling = @openTracker
49
+ @openTracker = tracker
50
+ end
51
+
52
+ checkAndArrestSequence()
53
+
54
+ return tracker
55
+ end
56
+
57
+ def closeTracker tracker
58
+ @closedTrackerCount += 1
59
+ @closedTracker = tracker
60
+ tracker.sibling = nil
61
+ @openTracker = nil
62
+
63
+ # Marks end of transaction
64
+ if @rootTracker == tracker
65
+ if @listenFlag < 1 || (@listenFlag >= 1 && @trackerCount > 1)
66
+ # if some trackers are not closed, while processing the metrics, it may go into infinite loop
67
+ if (@closedTrackerCount - @trackerCount) == 0
68
+ sequenceBag = Hash.new
69
+ sequenceBag["roottracker"] = @rootTracker
70
+ sequenceBag["exceptions"] = @exceptionBag
71
+
72
+ ManageEngine::APMObjectHolder.instance.collector.updateTransaction(@rootTracker.url, sequenceBag)
73
+ else
74
+ ManageEngine::APMObjectHolder.instance.log.warn "Some trackers are not closed, dropping the metrics for #{@rootTracker.url}"
75
+ end
76
+ end
77
+ closeSequence()
78
+ end
79
+ end
80
+
81
+ def closeSequence
82
+ @rootTracker = nil
83
+ @openTracker = @closedTracker = nil
84
+ @trackerCount = 0
85
+ @closedTrackerCount = 0
86
+ Thread.current[:apminsight] = nil
87
+ end
88
+
89
+ def addExceptionInfo(exception)
90
+ begin
91
+ if @exceptionBag == nil
92
+ @exceptionBag = Set.new
93
+ end
94
+ if (@exceptionBag.size() < 10)
95
+ exceptionRecord = ::APMInsight::Errors::ExceptionRecord.new(exception)
96
+ @exceptionBag.add(exceptionRecord)
97
+ end
98
+ rescue Exception=>e
99
+ end
100
+ end
101
+
102
+ def updateListenFlag
103
+ if !ManageEngine::APMObjectHolder.instance.txn_util.listen?(@rootTracker.url())
104
+ @listenFlag = 1
105
+ end
106
+
107
+ ## Check for sampling factor & for bg txn chk if enabled
108
+ end
109
+
110
+ def checkAndArrestSequence
111
+ @trackerCount += 1
112
+ if @trackerCount == 1000
113
+ @listenFlag = 1
114
+ end
115
+
116
+ ## Can check for timeout
117
+ end
118
+
119
+ def createDummyTracker
120
+ return ManageEngine::Tracker::DefaultTracker.new("dummy")
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,58 @@
1
+ require 'agent/handler/sequence_book'
2
+
3
+ module ManageEngine
4
+ module Agent
5
+ class TrackerHandler
6
+
7
+ def self.invokeTracker tracker
8
+ begin
9
+ @obj = ManageEngine::APMObjectHolder.instance
10
+
11
+ if !@obj.config.agent_enabled || tracker == nil
12
+ return nil
13
+ end
14
+
15
+ seqBook = Thread.current[:apminsight]
16
+ if seqBook != nil
17
+ if seqBook.listenFlag == 1
18
+ return nil
19
+ end
20
+ else
21
+ seqBook = ::APMInsight::Agent::SequenceBook.new
22
+ Thread.current[:apminsight] = seqBook
23
+ end
24
+
25
+ tracker = seqBook.attachTracker(tracker)
26
+
27
+ return tracker
28
+ rescue Exception=>ex
29
+ # Logging to be done here, Not sure whether its safe to do
30
+ if (@obj != nil)
31
+ @obj.log.logException "[TrackerHandler] Exception occurred at invoketracker.", ex
32
+ end
33
+ return nil
34
+ end
35
+ end
36
+
37
+
38
+ # Closes tracker properly and set everything ready to process next tracker
39
+ # If roottracker closes, sequence book is cleaned and data are push to store
40
+ def self.exitTracker tracker
41
+ begin
42
+ if tracker != nil
43
+ seqBook = Thread.current[:apminsight]
44
+ if seqBook != nil
45
+ seqBook.closeTracker tracker
46
+ end
47
+ end
48
+ rescue Exception=>ex
49
+ if (@obj != nil)
50
+ @obj.log.logException "[TrackerHandler] Exception occurred at exittracker.", ex
51
+ end
52
+ end
53
+ end
54
+
55
+ end # Class TrackerHandler
56
+
57
+ end # module Agent
58
+ end