stackify-ruby-apm 1.10.4 → 1.14.0
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.
- checksums.yaml +4 -4
- data/lib/stackify_apm/agent.rb +0 -2
- data/lib/stackify_apm/config.rb +36 -6
- data/lib/stackify_apm/context.rb +4 -1
- data/lib/stackify_apm/context/prefix.rb +30 -0
- data/lib/stackify_apm/context/request/headers.rb +30 -0
- data/lib/stackify_apm/context_builder.rb +1 -0
- data/lib/stackify_apm/helper/database_helper.rb +39 -0
- data/lib/stackify_apm/instrumenter_helper.rb +87 -0
- data/lib/stackify_apm/logger/logger_high_version.rb +5 -1
- data/lib/stackify_apm/logger/logger_lower_version.rb +5 -1
- data/lib/stackify_apm/middleware.rb +21 -3
- data/lib/stackify_apm/normalizers/active_record.rb +27 -8
- data/lib/stackify_apm/root_info.rb +6 -0
- data/lib/stackify_apm/serializers/transactions.rb +5 -0
- data/lib/stackify_apm/span/context.rb +42 -1
- data/lib/stackify_apm/spies.rb +4 -2
- data/lib/stackify_apm/spies/action_dispatch.rb +6 -1
- data/lib/stackify_apm/spies/curb.rb +41 -20
- data/lib/stackify_apm/spies/curb/easy.rb +220 -92
- data/lib/stackify_apm/spies/curb/multi.rb +26 -12
- data/lib/stackify_apm/spies/custom_instrumenter.rb +25 -4
- data/lib/stackify_apm/spies/dynamo_db.rb +51 -0
- data/lib/stackify_apm/spies/faraday.rb +87 -0
- data/lib/stackify_apm/spies/httparty.rb +45 -24
- data/lib/stackify_apm/spies/httpclient.rb +41 -20
- data/lib/stackify_apm/spies/httprb.rb +39 -18
- data/lib/stackify_apm/spies/log4r.rb +60 -0
- data/lib/stackify_apm/spies/logger.rb +117 -0
- data/lib/stackify_apm/spies/logging.rb +66 -0
- data/lib/stackify_apm/spies/mongo.rb +3 -1
- data/lib/stackify_apm/spies/net_http.rb +38 -20
- data/lib/stackify_apm/spies/redis.rb +39 -30
- data/lib/stackify_apm/spies/sequel.rb +28 -11
- data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +48 -25
- data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +35 -24
- data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +18 -8
- data/lib/stackify_apm/spies/stackify_logger.rb +28 -16
- data/lib/stackify_apm/spies/sucker_punch.rb +39 -0
- data/lib/stackify_apm/spies/yell.rb +65 -0
- data/lib/stackify_apm/util.rb +10 -9
- data/lib/stackify_apm/version.rb +1 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76953d4e093b082edd18b9b64c80f282cef005ca138ded983b5bc88b1545db96
|
4
|
+
data.tar.gz: 3c1fba2fa393369d38de568f91dbec9d1708a963c5f3802719f285c2e99edefa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1b9b23f4c1b473fc45850f996701cc33703de8a6937297638d84603a4ce816b2e00cb318d73eca407e073f1f41403d82b9d1bc7305cf4623b47a53965d8b576
|
7
|
+
data.tar.gz: 517edbe2b9f8ff7e1c5c8ecb381f93fc39a28af406de5c6ec45b5ff3c99a113bd4833482f38f7944f1c542e2a027e2c581c5d36df45d8b1749aefcdc752050df
|
data/lib/stackify_apm/agent.rb
CHANGED
@@ -101,8 +101,6 @@ module StackifyRubyAPM
|
|
101
101
|
info '[Agent] start()'
|
102
102
|
info '[Agent] transport type: ' + @config.transport
|
103
103
|
spies_name = ''
|
104
|
-
# If the rake task is detected as being ran then we don't load the spies
|
105
|
-
StackifyRubyAPM::Util.apm_disabled_in_rake
|
106
104
|
return false unless @config.instrument
|
107
105
|
config.enabled_spies.each do |lib|
|
108
106
|
spies_name = spies_name + ', ' + lib.inspect.to_s
|
data/lib/stackify_apm/config.rb
CHANGED
@@ -26,7 +26,7 @@ module StackifyRubyAPM
|
|
26
26
|
environment_name: ENV['RAILS_ENV'] || ENV['RACK_ENV'],
|
27
27
|
already_instrumented_flag: false,
|
28
28
|
rum_auto_injection: false,
|
29
|
-
rum_enabled:
|
29
|
+
rum_enabled: true,
|
30
30
|
rum_cookie_path: '/',
|
31
31
|
rum_cookie_name: '.Stackify.Rum',
|
32
32
|
transport: StackifyRubyAPM::TRACE_LOG,
|
@@ -70,7 +70,8 @@ module StackifyRubyAPM
|
|
70
70
|
transport_http_endpoint: 'https://localhost:10601',
|
71
71
|
|
72
72
|
queue: true,
|
73
|
-
lambda_handler: ''
|
73
|
+
lambda_handler: '',
|
74
|
+
prefix_enabled: false
|
74
75
|
}.freeze
|
75
76
|
|
76
77
|
ENV_TO_KEY = {
|
@@ -97,7 +98,8 @@ module StackifyRubyAPM
|
|
97
98
|
'STACKIFY_FLUSH_INTERVAL' => 'flush_interval_seconds',
|
98
99
|
'STACKIFY_DISABLED_SPIES' => [:list, 'disabled_spies'],
|
99
100
|
'STACKIFY_QUEUE' => [:bool, 'queue'],
|
100
|
-
'STACKIFY_LAMBDA_HANDLER' => 'lambda_handler'
|
101
|
+
'STACKIFY_LAMBDA_HANDLER' => 'lambda_handler',
|
102
|
+
'STACKIFY_PREFIX_ENABLED' => [:bool, 'prefix_enabled']
|
101
103
|
}.freeze
|
102
104
|
|
103
105
|
def initialize(options = {})
|
@@ -105,6 +107,7 @@ module StackifyRubyAPM
|
|
105
107
|
set_from_args(options)
|
106
108
|
set_from_config_file
|
107
109
|
set_from_env
|
110
|
+
set_prefix_paths if @prefix_enabled
|
108
111
|
yield self if block_given?
|
109
112
|
debug_logger
|
110
113
|
StackifyRubyAPM::Util.host_os == 'WINDOWS' ? load_stackify_props_windows : load_stackify_props_linux
|
@@ -170,6 +173,7 @@ module StackifyRubyAPM
|
|
170
173
|
|
171
174
|
attr_accessor :queue
|
172
175
|
attr_accessor :lambda_handler
|
176
|
+
attr_accessor :prefix_enabled
|
173
177
|
|
174
178
|
attr_reader :client_id
|
175
179
|
attr_reader :device_id
|
@@ -210,7 +214,20 @@ module StackifyRubyAPM
|
|
210
214
|
httparty
|
211
215
|
stackify_logger
|
212
216
|
sidekiq
|
213
|
-
delayed_job
|
217
|
+
delayed_job
|
218
|
+
faraday
|
219
|
+
sucker_punch
|
220
|
+
dynamo_db
|
221
|
+
]
|
222
|
+
end
|
223
|
+
|
224
|
+
def prefix_spies
|
225
|
+
return [] unless @prefix_enabled
|
226
|
+
%w[
|
227
|
+
logger
|
228
|
+
logging
|
229
|
+
log4r
|
230
|
+
yell
|
214
231
|
]
|
215
232
|
end
|
216
233
|
|
@@ -233,7 +250,7 @@ module StackifyRubyAPM
|
|
233
250
|
available_spies
|
234
251
|
end
|
235
252
|
|
236
|
-
new_available_spies - disabled_spies
|
253
|
+
new_available_spies + prefix_spies - disabled_spies
|
237
254
|
end
|
238
255
|
|
239
256
|
# Default Transport
|
@@ -254,7 +271,11 @@ module StackifyRubyAPM
|
|
254
271
|
|
255
272
|
def assign(options)
|
256
273
|
options.each do |key, value|
|
257
|
-
|
274
|
+
begin
|
275
|
+
send("#{key}=", value)
|
276
|
+
rescue Exception => e
|
277
|
+
info "[Config] Key: '#{key}' doesn't exist."
|
278
|
+
end
|
258
279
|
end
|
259
280
|
end
|
260
281
|
|
@@ -295,6 +316,14 @@ module StackifyRubyAPM
|
|
295
316
|
assign(YAML.load_file(config_file) || {})
|
296
317
|
end
|
297
318
|
|
319
|
+
# set log paths to prefix path if prefix_enabled is true
|
320
|
+
def set_prefix_paths
|
321
|
+
all_user_profile = ENV['ALLUSERSPROFILE'] || "C:\\ProgramData"
|
322
|
+
|
323
|
+
@log_path = StackifyRubyAPM::Util.host_os == 'WINDOWS' ? "#{all_user_profile}\\Stackify\\Agent\\debug\\stackify-ruby-apm-1.log" : "/usr/local/prefix/debug/stackify-ruby-apm-1.log"
|
324
|
+
@log_trace_path = StackifyRubyAPM::Util.host_os == 'WINDOWS' ? "#{all_user_profile}\\Stackify\\Agent\\log\\" : '/usr/local/prefix/log/'
|
325
|
+
end
|
326
|
+
|
298
327
|
# rubocop:disable Naming/AccessorMethodName
|
299
328
|
def set_rails(app)
|
300
329
|
self.application_name ||= format_name(application_name || app.class.parent_name).strip
|
@@ -382,6 +411,7 @@ module StackifyRubyAPM
|
|
382
411
|
info '[Config] environment_name must be String type.' unless @environment_name.is_a?(String) && defined?(@environment_name)
|
383
412
|
info '[Config] transport must be String type.' unless @transport.is_a?(String) && defined?(@transport)
|
384
413
|
info '[Config] Transport should be one of these values: [agent_socket, default, agent_http]. Should be a String.' if defined?(@transport) && !%w[agent_socket default agent_http].include?(@transport.downcase)
|
414
|
+
info '[Config] prefix_enabled must be Boolean type: true/false.' unless [TrueClass, FalseClass].include?(@prefix_enabled.class) && defined?(@prefix_enabled)
|
385
415
|
end
|
386
416
|
# rubocop:enable Metrics/CyclomaticComplexity
|
387
417
|
# rubocop:enable Metrics/PerceivedComplexity
|
data/lib/stackify_apm/context.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
# This class initializes the parameters and variables for the context of Transaction/Span
|
4
4
|
|
5
|
+
require 'stackify_apm/context/prefix'
|
5
6
|
require 'stackify_apm/context/request'
|
7
|
+
require 'stackify_apm/context/request/headers'
|
6
8
|
require 'stackify_apm/context/request/socket'
|
7
9
|
require 'stackify_apm/context/request/url'
|
8
10
|
require 'stackify_apm/context/response'
|
@@ -12,12 +14,13 @@ module StackifyRubyAPM
|
|
12
14
|
class Context
|
13
15
|
include NaivelyHashable
|
14
16
|
|
15
|
-
attr_accessor :request, :response, :aws, :category
|
17
|
+
attr_accessor :request, :response, :aws, :category, :prefix
|
16
18
|
attr_reader :custom, :tags
|
17
19
|
|
18
20
|
def initialize
|
19
21
|
@custom = {}
|
20
22
|
@tags = {}
|
23
|
+
@prefix = Context::Prefix.new
|
21
24
|
end
|
22
25
|
|
23
26
|
# add aws context to context instance
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StackifyRubyAPM
|
4
|
+
class Context
|
5
|
+
# @api private
|
6
|
+
class Prefix
|
7
|
+
include NaivelyHashable
|
8
|
+
|
9
|
+
attr_accessor :response_body, :request_body
|
10
|
+
attr_reader :response_headers, :request_headers
|
11
|
+
|
12
|
+
def request_headers=(headers)
|
13
|
+
@request_headers = to_json_list(headers)
|
14
|
+
end
|
15
|
+
|
16
|
+
def response_headers=(headers)
|
17
|
+
@response_headers = to_json_list(headers)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def to_json_list(header)
|
22
|
+
list_header = []
|
23
|
+
header.each do |key, value|
|
24
|
+
list_header << {key => value}
|
25
|
+
end
|
26
|
+
list_header.to_json
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StackifyRubyAPM
|
4
|
+
# @api private
|
5
|
+
class Context
|
6
|
+
# @api private
|
7
|
+
class Request
|
8
|
+
# @api private
|
9
|
+
class Headers
|
10
|
+
include NaivelyHashable
|
11
|
+
|
12
|
+
def initialize(req)
|
13
|
+
@values = build_headers req
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :values
|
17
|
+
|
18
|
+
def build_headers req
|
19
|
+
env = req.env
|
20
|
+
headers = Hash[*env.select {|k,v| k.start_with? 'HTTP_'}
|
21
|
+
.collect {|k,v| [k.sub(/^HTTP_/, ''), v]}
|
22
|
+
.collect {|k,v| [k.split('_').collect(&:capitalize).join('-'), v]}
|
23
|
+
.sort
|
24
|
+
.flatten]
|
25
|
+
return headers
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -23,5 +23,44 @@ module DatabaseHelper
|
|
23
23
|
end
|
24
24
|
# rubocop:enable Metrics/CyclomaticComplexity
|
25
25
|
# rubocop:enable Metrics/PerceivedComplexity
|
26
|
+
|
27
|
+
# Check the prepared statement by placeholder if its valid
|
28
|
+
# placeholder - contains the prepared statement pattern.
|
29
|
+
# Placeholder for mysql, sqlite, jdbc oracle, db2 is ?, example "SELECT * FROM posts WHERE author = ? and id = ?"
|
30
|
+
# Placeholder for postgres is $1, $2,...$n, example "SELECT * FROM posts WHERE author = $1 and id = $2"
|
31
|
+
# statement - contains the db properties such as SQL, PROVIDER, etc.
|
32
|
+
# So if there's payload value we append PREFIX_SQL_PARAMETERS to the existing object properties.
|
33
|
+
# payload - contains the payload[:binds] which is the payload value/data of the placeholder.
|
34
|
+
# Example payload: {:sql=>"SELECT * FROM posts WHERE author = ? and id = ?", :db_adapter=>"mysql2::client", :binds=>["J.K. Rowling", 1]}
|
35
|
+
def check_prepared_stmt_by_placeholder(placeholder, statement, payload)
|
36
|
+
sqlParam = []
|
37
|
+
unless payload[:binds].nil?
|
38
|
+
payload[:binds].each_with_index do |record, idx|
|
39
|
+
if record && defined?(record.value)
|
40
|
+
StackifyRubyAPM::Util.pushToAryIndex(sqlParam, idx, record.value[0..999])
|
41
|
+
elsif ((record && record.instance_of?(Array)) && defined?(record[1]))
|
42
|
+
StackifyRubyAPM::Util.pushToAryIndex(sqlParam, idx, record[1][0..999])
|
43
|
+
else
|
44
|
+
StackifyRubyAPM::Util.pushToAryIndex(sqlParam, idx, record[0..999])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
statement[:PREFIX_SQL_PARAMETERS] = sqlParam[0..99].to_json if sqlParam.count > 0
|
48
|
+
statement[:PREFIX_SQL_PARAMETER_COUNT] = sqlParam.length.to_s if sqlParam.count > 0
|
49
|
+
true
|
50
|
+
else
|
51
|
+
false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Common DB span properties shared to sequel, activerecord, etc.
|
56
|
+
def get_common_db_properties
|
57
|
+
{
|
58
|
+
CATEGORY: 'Database',
|
59
|
+
SUBCATEGORY: 'Execute',
|
60
|
+
COMPONENT_CATEGORY: 'DB Query',
|
61
|
+
COMPONENT_DETAIL: 'Execute SQL Query',
|
62
|
+
PROVIDER: 'generic'
|
63
|
+
}
|
64
|
+
end
|
26
65
|
end
|
27
66
|
include DatabaseHelper
|
@@ -189,6 +189,93 @@ module StackifyRubyAPM
|
|
189
189
|
@custom_class_info[current_class.to_s]['controller'] = true if class_path && class_path.include?('controllers')
|
190
190
|
@custom_class_info[current_class.to_s]['model'] = true if class_path && class_path.include?('models')
|
191
191
|
end
|
192
|
+
|
193
|
+
# Monkey patch the single ruby module file.
|
194
|
+
#
|
195
|
+
# tracked_func - trackedFunction variable in stackify.json
|
196
|
+
# current_module - module variable in stackify.json
|
197
|
+
# class_path - file_path variable in stackify.json
|
198
|
+
# options - other options such as trackedFunctionName
|
199
|
+
def self.patched_module(tracked_func, current_module, class_path, **options)
|
200
|
+
current_method = options[:current_method] || nil
|
201
|
+
tracked_function_name = options[:tracked_function_name] || nil
|
202
|
+
transaction = options[:is_transaction] || nil
|
203
|
+
mod_spy = "#{current_module}Spy"
|
204
|
+
|
205
|
+
unless @custom_instrumented[current_module.to_s]
|
206
|
+
@custom_instrumented[current_module.to_s] = {}
|
207
|
+
end
|
208
|
+
|
209
|
+
unless @custom_instrumented[current_module.to_s][current_method.to_s]
|
210
|
+
@custom_instrumented[current_module.to_s][current_method.to_s] = false
|
211
|
+
end
|
212
|
+
|
213
|
+
unless @custom_class_info[current_module.to_s]
|
214
|
+
@custom_class_info[current_module.to_s] = {}
|
215
|
+
end
|
216
|
+
|
217
|
+
unless @custom_class_info[current_module.to_s]['controller']
|
218
|
+
@custom_class_info[current_module.to_s]['controller'] = false
|
219
|
+
end
|
220
|
+
|
221
|
+
unless @custom_class_info[current_module.to_s]['model']
|
222
|
+
@custom_class_info[current_module.to_s]['model'] = false
|
223
|
+
end
|
224
|
+
|
225
|
+
return unless @custom_instrumented[current_module.to_s][current_method.to_s] == false
|
226
|
+
|
227
|
+
eval <<-RUBY
|
228
|
+
module #{mod_spy}
|
229
|
+
def self.install
|
230
|
+
#{current_module}.class_eval do
|
231
|
+
class<< self
|
232
|
+
alias_method "#{current_method}_without_apm", "#{current_method}"
|
233
|
+
|
234
|
+
def #{current_method}(*args, &block)
|
235
|
+
if StackifyRubyAPM.current_transaction.nil? && #{!transaction.nil?}
|
236
|
+
t = StackifyRubyAPM.transaction("custom.#{current_module}.#{current_method}", TRACETYPE)
|
237
|
+
begin
|
238
|
+
req = #{current_method}_without_apm(*args, &block)
|
239
|
+
rescue Exception => e
|
240
|
+
StackifyRubyAPM.report(e)
|
241
|
+
raise e
|
242
|
+
ensure
|
243
|
+
t.submit
|
244
|
+
end
|
245
|
+
return req
|
246
|
+
elsif StackifyRubyAPM.current_transaction
|
247
|
+
name = "Custom Instrument"
|
248
|
+
type = "#{current_module}##{current_method}"
|
249
|
+
ctx = if "#{tracked_func}" == 'true'
|
250
|
+
Span::Context.new(
|
251
|
+
CATEGORY: 'Ruby',
|
252
|
+
TRACKED_FUNC: "#{tracked_function_name}"
|
253
|
+
)
|
254
|
+
else
|
255
|
+
Span::Context.new(
|
256
|
+
CATEGORY: 'Ruby'
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
StackifyRubyAPM.span name, type, context: ctx do
|
261
|
+
#{current_method}_without_apm(*args, &block)
|
262
|
+
end
|
263
|
+
else
|
264
|
+
return #{current_method}_without_apm(*args, &block)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
StackifyRubyAPM::Spies.register current_module.to_s, class_path.to_s, #{mod_spy}, true, "#{current_method}", "#{tracked_func}", "#{tracked_function_name}"
|
273
|
+
RUBY
|
274
|
+
@custom_instrumented[current_module.to_s][current_method.to_s] = true
|
275
|
+
@custom_class_info[current_module.to_s]['controller'] = true if class_path && class_path.include?('controllers')
|
276
|
+
@custom_class_info[current_module.to_s]['model'] = true if class_path && class_path.include?('models')
|
277
|
+
end
|
278
|
+
|
192
279
|
# rubocop:enable Metrics/CyclomaticComplexity
|
193
280
|
# rubocop:enable Metrics/PerceivedComplexity
|
194
281
|
# rubocop:enable Lint/UselessAssignment
|
@@ -29,7 +29,11 @@ module StackifyRubyAPM
|
|
29
29
|
temp_filename = logdev.gsub('-1.log', '')
|
30
30
|
new_logdev = temp_filename + '-1.log'
|
31
31
|
end
|
32
|
-
|
32
|
+
begin
|
33
|
+
@logdev = LogDevice.new(new_logdev, shift_size: shift_size)
|
34
|
+
rescue StandardError => e
|
35
|
+
puts "Stackify Profiler unable to access [" + logdev + "]"
|
36
|
+
end
|
33
37
|
end
|
34
38
|
end
|
35
39
|
require 'stackify_apm/logger/log_device'
|
@@ -20,7 +20,11 @@ module StackifyRubyAPM
|
|
20
20
|
temp_filename = logdev.gsub('-1.log', '')
|
21
21
|
new_logdev = temp_filename + '-1.log'
|
22
22
|
end
|
23
|
-
|
23
|
+
begin
|
24
|
+
@logdev = LogDevice.new(new_logdev, shift_size: shift_size)
|
25
|
+
rescue StandardError => e
|
26
|
+
puts "Stackify Profiler unable to access [" + logdev + "]"
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
@@ -39,7 +39,8 @@ module StackifyRubyAPM
|
|
39
39
|
# rubocop:disable Metrics/PerceivedComplexity
|
40
40
|
def call(env)
|
41
41
|
begin
|
42
|
-
|
42
|
+
context = StackifyRubyAPM.build_context(env)
|
43
|
+
transaction = build_transaction(env, context) if running?
|
43
44
|
resp = @app.call env
|
44
45
|
|
45
46
|
@rack_status = resp[0].to_i
|
@@ -47,6 +48,8 @@ module StackifyRubyAPM
|
|
47
48
|
@rack_body = resp[2]
|
48
49
|
@configuration = config
|
49
50
|
|
51
|
+
build_prefix_context(transaction, context)
|
52
|
+
|
50
53
|
if okay_to_modify?
|
51
54
|
@configuration.already_instrumented_flag = true
|
52
55
|
if @configuration.rum_enabled.is_a?(TrueClass)
|
@@ -92,8 +95,9 @@ module StackifyRubyAPM
|
|
92
95
|
|
93
96
|
# Start of transaction building with params: name, type, context
|
94
97
|
#
|
95
|
-
def build_transaction(env)
|
96
|
-
|
98
|
+
def build_transaction(env, context=nil)
|
99
|
+
context = context || StackifyRubyAPM.build_context(env)
|
100
|
+
StackifyRubyAPM.transaction 'Rack', 'WEBAPP', context: context
|
97
101
|
end
|
98
102
|
|
99
103
|
def running?
|
@@ -129,5 +133,19 @@ module StackifyRubyAPM
|
|
129
133
|
def xhr?
|
130
134
|
@rack_headers['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
|
131
135
|
end
|
136
|
+
|
137
|
+
# Add prefix instrument data to transaction context if enabled
|
138
|
+
def build_prefix_context(transaction, context)
|
139
|
+
return nil unless @configuration.prefix_enabled.is_a?(TrueClass)
|
140
|
+
|
141
|
+
source = nil
|
142
|
+
body = @rack_body
|
143
|
+
body.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s) }
|
144
|
+
|
145
|
+
transaction.context.prefix.request_body = context && context.request && context.request.body || ""
|
146
|
+
transaction.context.prefix.request_headers = context && context.request && context.request.headers || Hash.new
|
147
|
+
transaction.context.prefix.response_body = source || ""
|
148
|
+
transaction.context.prefix.response_headers = @rack_headers || Hash.new
|
149
|
+
end
|
132
150
|
end
|
133
151
|
end
|
@@ -20,6 +20,7 @@ module StackifyRubyAPM
|
|
20
20
|
return :skip if %w[SCHEMA CACHE].include?(payload[:name])
|
21
21
|
|
22
22
|
statement = query_variables(payload)
|
23
|
+
check_prepared_stmt(statement, payload)
|
23
24
|
name = payload[:sql] || payload[:name] || 'Default'
|
24
25
|
context = Span::Context.new(statement)
|
25
26
|
[name, @type, context]
|
@@ -28,14 +29,14 @@ module StackifyRubyAPM
|
|
28
29
|
private
|
29
30
|
|
30
31
|
def query_variables(payload)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
adapter_config = lookup_adapter_config
|
33
|
+
props = get_common_db_properties
|
34
|
+
props[:PROVIDER] = get_profiler(lookup_adapter)
|
35
|
+
props[:SQL] = payload[:sql]
|
36
|
+
if adapter_config
|
37
|
+
props[:URL] = "#{adapter_config[:host]}:#{adapter_config[:port]}"
|
38
|
+
end
|
39
|
+
props
|
39
40
|
end
|
40
41
|
|
41
42
|
def lookup_adapter
|
@@ -44,6 +45,24 @@ module StackifyRubyAPM
|
|
44
45
|
debug '[SqlNormalizer] lookup_adapter err: ' + error.inspect.to_s
|
45
46
|
nil
|
46
47
|
end
|
48
|
+
|
49
|
+
def lookup_adapter_config
|
50
|
+
::ActiveRecord::Base.connection_config.to_h
|
51
|
+
rescue StandardError => error
|
52
|
+
debug '[SqlNormalizer] lookup_adapter_config err: ' + error.inspect.to_s
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_prepared_stmt(statement, payload)
|
57
|
+
if StackifyRubyAPM.agent.config.prefix_enabled
|
58
|
+
case get_profiler(lookup_adapter)
|
59
|
+
when 'generic', 'mysql', 'sqlite', 'oracle', 'db2'
|
60
|
+
check_prepared_stmt_by_placeholder(payload[:sql].include?('?'), statement, payload)
|
61
|
+
when 'postgresql'
|
62
|
+
check_prepared_stmt_by_placeholder(!!payload[:sql].match(/\$\d/), statement, payload)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
47
66
|
end
|
48
67
|
end
|
49
68
|
end
|