apminsight 1.0.1 → 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/apm-agent.gemspec +64 -0
- data/conf/apminsight.conf +15 -24
- data/lib/agent/am_objectholder.rb +25 -19
- data/lib/agent/api/custom_tracker.rb +79 -0
- data/lib/agent/configuration/am_configuration.rb +249 -37
- data/lib/agent/handler/custom_api_handler.rb +40 -0
- data/lib/agent/handler/sequence_book.rb +118 -0
- data/lib/agent/handler/tracker_handler.rb +58 -0
- data/lib/agent/logging/am_logger.rb +13 -9
- data/lib/agent/metrics/am_metricsformatter.rb +117 -59
- data/lib/agent/metrics/am_metricsparser.rb +195 -468
- data/lib/agent/metrics/am_metricstore.rb +7 -6
- data/lib/agent/metrics/exception_record.rb +24 -0
- data/lib/agent/server/am_agent.rb +42 -17
- data/lib/agent/server/am_connector.rb +65 -21
- data/lib/agent/server/instrument/action_view.rb +64 -0
- data/lib/agent/server/instrument/active_record.rb +52 -0
- data/lib/agent/server/instrument/am_apm.rb +107 -97
- data/lib/agent/server/instrument/am_instrumenter.rb +54 -42
- data/lib/agent/server/instrument/environment.rb +42 -0
- data/lib/agent/server/instrument/rails.rb +56 -0
- data/lib/agent/server/instrument/sinatra.rb +97 -0
- data/lib/agent/server/worker/am_worker.rb +93 -49
- data/lib/agent/trackers/database_tracker.rb +107 -0
- data/lib/agent/trackers/default_tracker.rb +57 -0
- data/lib/agent/trackers/root_tracker.rb +43 -0
- data/lib/agent/util/am_constants.rb +46 -3
- data/lib/agent/util/am_util.rb +64 -1
- data/lib/agent/util/transaction_util.rb +35 -0
- data/lib/agent/version.rb +13 -0
- data/lib/apminsight.rb +4 -1
- metadata +114 -76
@@ -1,43 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
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.
|
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
|
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
|
-
|
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
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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
|
|