apminsight 1.0.1 → 1.8.6

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