stackify-ruby-apm 1.14.5 → 1.15.3.beta1
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/config.rb +31 -4
- data/lib/stackify_apm/context.rb +2 -1
- data/lib/stackify_apm/helper/database_helper.rb +2 -0
- data/lib/stackify_apm/instrumenter_helper.rb +10 -1
- data/lib/stackify_apm/middleware.rb +9 -10
- data/lib/stackify_apm/normalizers/active_record.rb +57 -9
- data/lib/stackify_apm/response_manipulator.rb +19 -14
- data/lib/stackify_apm/root_info.rb +2 -2
- data/lib/stackify_apm/spies/custom_instrumenter.rb +13 -2
- data/lib/stackify_apm/spies/faraday.rb +1 -1
- data/lib/stackify_apm/spies/sinatra.rb +10 -0
- data/lib/stackify_apm/transport/agent_http_client.rb +1 -1
- data/lib/stackify_apm/util.rb +52 -0
- data/lib/stackify_apm/version.rb +1 -1
- data/lib/stackify_ruby_apm.rb +56 -11
- data/stackify-ruby-apm.gemspec +10 -4
- metadata +38 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 014b8a770c78acfd3eba5a9f75e1a4c76eeb2c4d28635d80834e53f985807ed8
|
4
|
+
data.tar.gz: 1f82c3bb6394992ffe325182d32b76b5a7cf665923ec93842461186cf9594c90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0e4223d7f5b01adc3ba840ed02aaced9509fe5277ad112d02a0c4febdabfddcac6f2a456866bdd76027486b14f296256b1515399b41eef78672549d02b7bcc5
|
7
|
+
data.tar.gz: 42ab0ae25cda2ee2d6aae7ff7b54888db652d80c6f72006befc61317d4871068b59986eb92fc5766ca8ff8d96dac8969a69065d08bab661232452dda322481e1
|
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: false,
|
30
30
|
rum_cookie_path: '/',
|
31
31
|
rum_cookie_name: '.Stackify.Rum',
|
32
32
|
transport: StackifyRubyAPM::TRACE_LOG,
|
@@ -71,14 +71,17 @@ module StackifyRubyAPM
|
|
71
71
|
|
72
72
|
queue: true,
|
73
73
|
lambda_handler: '',
|
74
|
-
prefix_enabled: false
|
74
|
+
prefix_enabled: false,
|
75
|
+
|
76
|
+
rum_script_url: 'https://stckjs.stackify.com/stckjs.js',
|
77
|
+
rum_key: ''
|
75
78
|
}.freeze
|
76
79
|
|
77
80
|
ENV_TO_KEY = {
|
78
81
|
'STACKIFY_DEBUG' => [:bool, 'debug_logging'],
|
79
82
|
'STACKIFY_APPLICATION_NAME' => 'application_name',
|
80
83
|
'STACKIFY_ENVIRONMENT_NAME' => 'environment_name',
|
81
|
-
'
|
84
|
+
'RETRACE_RUMV2_ENABLED' => [:bool, 'rum_enabled'],
|
82
85
|
'STACKIFY_RUM_AUTO_INJECT' => [:bool, 'rum_auto_injection'],
|
83
86
|
'STACKIFY_RUM_SCRIPT_SRC' => 'rum_script_src',
|
84
87
|
'STACKIFY_TRANSPORT' => 'transport',
|
@@ -99,7 +102,9 @@ module StackifyRubyAPM
|
|
99
102
|
'STACKIFY_DISABLED_SPIES' => [:list, 'disabled_spies'],
|
100
103
|
'STACKIFY_QUEUE' => [:bool, 'queue'],
|
101
104
|
'STACKIFY_LAMBDA_HANDLER' => 'lambda_handler',
|
102
|
-
'STACKIFY_PREFIX_ENABLED' => [:bool, 'prefix_enabled']
|
105
|
+
'STACKIFY_PREFIX_ENABLED' => [:bool, 'prefix_enabled'],
|
106
|
+
'RETRACE_RUM_SCRIPT_URL' => 'rum_script_url',
|
107
|
+
'RETRACE_RUM_KEY' => 'rum_key'
|
103
108
|
}.freeze
|
104
109
|
|
105
110
|
def initialize(options = {})
|
@@ -180,6 +185,9 @@ module StackifyRubyAPM
|
|
180
185
|
attr_reader :apm_disabled_in_rake
|
181
186
|
attr_reader :client_run_domain
|
182
187
|
|
188
|
+
attr_accessor :rum_script_url
|
189
|
+
attr_accessor :rum_key
|
190
|
+
|
183
191
|
def app=(app)
|
184
192
|
case app_type?(app)
|
185
193
|
when :rails
|
@@ -412,6 +420,25 @@ module StackifyRubyAPM
|
|
412
420
|
info '[Config] transport must be String type.' unless @transport.is_a?(String) && defined?(@transport)
|
413
421
|
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
422
|
info '[Config] prefix_enabled must be Boolean type: true/false.' unless [TrueClass, FalseClass].include?(@prefix_enabled.class) && defined?(@prefix_enabled)
|
423
|
+
|
424
|
+
valid_rum_script_url = false
|
425
|
+
if defined?(@rum_script_url) && @rum_script_url.is_a?(String)
|
426
|
+
valid_rum_script_url = @rum_script_url =~ /^((((https?|ftps?|gopher|telnet|nntp):\/\/)|(mailto:|news:))(%[0-9A-Fa-f]{2}|[\-\(\)_\.!~*';\/?:@&=+$,A-Za-z0-9])+)([\)\.!';\/?:,][\[:blank:|:blank:\]])?$/
|
427
|
+
end
|
428
|
+
|
429
|
+
if !valid_rum_script_url
|
430
|
+
@rum_script_url = 'https://stckjs.stackify.com/stckjs.js'
|
431
|
+
end
|
432
|
+
|
433
|
+
valid_rum_key = false
|
434
|
+
if defined?(@rum_key) && @rum_key.is_a?(String)
|
435
|
+
valid_rum_key = @rum_key =~ %r{^[A-Za-z0-9_-]+$}
|
436
|
+
end
|
437
|
+
|
438
|
+
if !valid_rum_key
|
439
|
+
@rum_key = ''
|
440
|
+
end
|
441
|
+
|
415
442
|
end
|
416
443
|
# rubocop:enable Metrics/CyclomaticComplexity
|
417
444
|
# rubocop:enable Metrics/PerceivedComplexity
|
data/lib/stackify_apm/context.rb
CHANGED
@@ -14,13 +14,14 @@ module StackifyRubyAPM
|
|
14
14
|
class Context
|
15
15
|
include NaivelyHashable
|
16
16
|
|
17
|
-
attr_accessor :request, :response, :aws, :category, :prefix
|
17
|
+
attr_accessor :request, :response, :aws, :category, :prefix, :rum
|
18
18
|
attr_reader :custom, :tags
|
19
19
|
|
20
20
|
def initialize
|
21
21
|
@custom = {}
|
22
22
|
@tags = {}
|
23
23
|
@prefix = Context::Prefix.new
|
24
|
+
@rum = false
|
24
25
|
end
|
25
26
|
|
26
27
|
# add aws context to context instance
|
@@ -35,6 +35,8 @@ module StackifyRubyAPM
|
|
35
35
|
classspyitem = "#{current_class}Spy"
|
36
36
|
module_consget_spy = Module.const_get(current_class)
|
37
37
|
current_method_without = "_without_apm_#{current_method}"
|
38
|
+
is_private_method = options[:is_private_method] || false
|
39
|
+
is_protected_method = options[:is_protected_method] || false
|
38
40
|
|
39
41
|
unless @custom_instrumented[current_class.to_s]
|
40
42
|
@custom_instrumented[current_class.to_s] = {}
|
@@ -63,7 +65,14 @@ module StackifyRubyAPM
|
|
63
65
|
def install
|
64
66
|
#{current_class}.class_eval do
|
65
67
|
alias_method "#{current_method_without}", "#{current_method}"
|
66
|
-
|
68
|
+
|
69
|
+
#{
|
70
|
+
if is_private_method
|
71
|
+
then "private"
|
72
|
+
elsif is_protected_method
|
73
|
+
then "protected"
|
74
|
+
end
|
75
|
+
}
|
67
76
|
def #{current_method}(*args, &block)
|
68
77
|
if StackifyRubyAPM.current_transaction.nil? && #{!transaction.nil?}
|
69
78
|
t = StackifyRubyAPM.transaction("custom.#{current_class}.#{current_method}", TRACETYPE)
|
@@ -28,6 +28,7 @@ module StackifyRubyAPM
|
|
28
28
|
CONTENT_TYPE_REGEX = %r{text\/html|application\/xhtml\+xml/}
|
29
29
|
CONTENT_DISPOSITION = 'Content-Disposition'.freeze
|
30
30
|
ATTACHMENT = 'attachment'.freeze
|
31
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
31
32
|
|
32
33
|
def initialize(app)
|
33
34
|
@app = app
|
@@ -53,17 +54,15 @@ module StackifyRubyAPM
|
|
53
54
|
if okay_to_modify?
|
54
55
|
@configuration.already_instrumented_flag = true
|
55
56
|
if @configuration.rum_enabled.is_a?(TrueClass)
|
56
|
-
|
57
|
-
|
58
|
-
response = Rack::Response.new @rack_body, @rack_status, @rack_headers
|
59
|
-
response.set_cookie(@configuration.rum_cookie_name, value: transaction.id, path: @configuration.rum_cookie_path)
|
60
|
-
resp = response.finish
|
61
|
-
end
|
62
|
-
if @configuration.rum_auto_injection.is_a?(TrueClass)
|
63
|
-
response_manupulate = StackifyRubyAPM::ResponseManipulator.new(env, resp, @configuration)
|
64
|
-
response_string = response_manupulate.adjust_pagehtml_response
|
57
|
+
response_manipulated = StackifyRubyAPM::ResponseManipulator.new(env, resp, @configuration)
|
58
|
+
response_string = response_manipulated.adjust_pagehtml_response
|
65
59
|
if response_string
|
66
|
-
|
60
|
+
if @rack_headers.key?(CONTENT_LENGTH)
|
61
|
+
content_length = response_string ? response_string.bytesize : 0
|
62
|
+
@rack_headers[CONTENT_LENGTH] = content_length.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
response = Rack::Response.new(response_string, @rack_status, @rack_headers)
|
67
66
|
resp = response.finish
|
68
67
|
else
|
69
68
|
resp
|
@@ -14,8 +14,6 @@ module StackifyRubyAPM
|
|
14
14
|
|
15
15
|
def initialize(*args)
|
16
16
|
super(*args)
|
17
|
-
|
18
|
-
@type = format('db.%s.sql', lookup_adapter || 'unknown').freeze
|
19
17
|
end
|
20
18
|
|
21
19
|
def normalize(_transaction, _name, payload)
|
@@ -25,7 +23,9 @@ module StackifyRubyAPM
|
|
25
23
|
check_prepared_stmt(statement, payload)
|
26
24
|
name = payload[:sql] || payload[:name] || 'Default'
|
27
25
|
context = Span::Context.new(statement)
|
28
|
-
|
26
|
+
|
27
|
+
type = format('db.%s.sql', lookup_adapter(payload) || 'unknown').freeze
|
28
|
+
[name, type, context]
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
@@ -33,7 +33,7 @@ module StackifyRubyAPM
|
|
33
33
|
def query_variables(payload)
|
34
34
|
adapter_config = lookup_adapter_config
|
35
35
|
props = get_common_db_properties
|
36
|
-
props[:PROVIDER] = get_profiler(lookup_adapter)
|
36
|
+
props[:PROVIDER] = get_profiler(lookup_adapter(payload))
|
37
37
|
props[:SQL] = payload[:sql]
|
38
38
|
if adapter_config
|
39
39
|
props[:URL] = "#{adapter_config[:host]}:#{adapter_config[:port]}"
|
@@ -41,18 +41,66 @@ module StackifyRubyAPM
|
|
41
41
|
props
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
# Ideally the application doesn't connect to the database during boot,
|
45
|
+
# but sometimes it does. In case it did, we want to empty out the
|
46
|
+
# connection pools so that a non-database-using process (e.g. a master
|
47
|
+
# process in a forking server model) doesn't retain a needless
|
48
|
+
# connection. If it was needed, the incremental cost of reestablishing
|
49
|
+
# this connection is trivial: the rest of the pool would need to be
|
50
|
+
# populated anyway.
|
51
|
+
#
|
52
|
+
# Reference: https://github.com/rails/rails/blob/main/activerecord/lib/active_record/railtie.rb#L253
|
53
|
+
#
|
54
|
+
# Miko: Considering we are getting the connection method, it is retrieving connection from the connection pool
|
55
|
+
# Connection Method: lib/active_record/connection_handling.rb#L264
|
56
|
+
# Retrieve Connection: lib/active_record/connection_handling.rb#L309
|
57
|
+
# Handler Retrieve Connection: lib/active_record/connection_adapters/abstract/connection_pool.rb#L1111
|
58
|
+
|
59
|
+
def lookup_adapter(payload)
|
60
|
+
connection = nil
|
61
|
+
if (payload.key?(:connection))
|
62
|
+
connection = payload[:connection]
|
63
|
+
elsif ::ActiveRecord::Base.connection_pool.instance_variable_defined?(:@reserved_connections) and payload.key?(:connection_id)
|
64
|
+
connection_id = payload[:connection_id] # Connection ID here is the object_id of the connection object
|
65
|
+
connections = ::ActiveRecord::Base.connection_pool.instance_variable_get(:@reserved_connections) # Lets check the reserved connections
|
66
|
+
|
67
|
+
if (
|
68
|
+
(connections.class != nil and connections.respond_to?(:class) and
|
69
|
+
(connections.class.to_s == 'ThreadSafe::Cache' or connections.class.to_s == 'Hash')
|
70
|
+
) and connections.size()
|
71
|
+
)
|
72
|
+
connections.each_value do |val|
|
73
|
+
if val.object_id == connection_id
|
74
|
+
connection = val
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if (connection.nil?)
|
82
|
+
return 'generic'
|
83
|
+
end
|
84
|
+
|
85
|
+
if (connection.respond_to?(:adapter_name) && connection.adapter_name.nil?)
|
86
|
+
return 'generic'
|
87
|
+
end
|
88
|
+
|
89
|
+
connection.adapter_name.downcase
|
46
90
|
rescue StandardError => error
|
47
91
|
debug '[SqlNormalizer] lookup_adapter err: ' + error.inspect.to_s
|
48
92
|
nil
|
49
93
|
end
|
50
94
|
|
51
95
|
def lookup_adapter_config
|
96
|
+
config = nil
|
52
97
|
if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('6.1')
|
53
|
-
::ActiveRecord::Base.connection_db_config
|
98
|
+
config = ::ActiveRecord::Base.connection_db_config
|
54
99
|
else
|
55
|
-
::ActiveRecord::Base.connection_config
|
100
|
+
config = ::ActiveRecord::Base.connection_config
|
101
|
+
end
|
102
|
+
if (config != nil && config.respond_to(:to_h))
|
103
|
+
config.to_h
|
56
104
|
end
|
57
105
|
rescue StandardError => error
|
58
106
|
debug '[SqlNormalizer] lookup_adapter_config err: ' + error.inspect.to_s
|
@@ -61,7 +109,7 @@ module StackifyRubyAPM
|
|
61
109
|
|
62
110
|
def check_prepared_stmt(statement, payload)
|
63
111
|
if StackifyRubyAPM.agent.config.prefix_enabled
|
64
|
-
case get_profiler(lookup_adapter)
|
112
|
+
case get_profiler(lookup_adapter(payload))
|
65
113
|
when 'generic', 'mysql', 'sqlite', 'oracle', 'db2'
|
66
114
|
check_prepared_stmt_by_placeholder(payload[:sql].include?('?'), statement, payload)
|
67
115
|
when 'postgresql'
|
@@ -22,6 +22,7 @@ module StackifyRubyAPM
|
|
22
22
|
GT = '>'.freeze
|
23
23
|
CHARSET_RE = /<\s*meta[^>]+charset\s*=[^>]*>/
|
24
24
|
X_UA_COMPATIBLE_RE = /<\s*meta[^>]+http-equiv\s*=\s*['"]x-ua-compatible['"][^>]*>/
|
25
|
+
STACKIFY_SETTING_TAG = '<script type="text/javascript">(window.StackifySettings ||'
|
25
26
|
|
26
27
|
def initialize(env, rack_response, config)
|
27
28
|
@env = env
|
@@ -46,27 +47,23 @@ module StackifyRubyAPM
|
|
46
47
|
close_old_response(response)
|
47
48
|
return nil unless source
|
48
49
|
|
49
|
-
|
50
|
-
device_id = @config.device_id
|
51
|
-
client_rundomain = @config.client_run_domain
|
50
|
+
transaction_id = defined?(StackifyRubyAPM.current_transaction.id) ? StackifyRubyAPM.current_transaction.id : nil
|
52
51
|
inject_flag = false
|
53
52
|
|
54
|
-
if
|
55
|
-
|
56
|
-
inject_flag = true
|
57
|
-
else
|
58
|
-
info 'RUM Injection Error: Client RUM Domain is invalid.'
|
59
|
-
end
|
53
|
+
if transaction_id
|
54
|
+
inject_flag = true
|
60
55
|
else
|
61
|
-
info 'RUM Injection Error: No
|
56
|
+
info 'RUM Injection Error: No Transaction ID found.'
|
62
57
|
end
|
63
58
|
|
64
59
|
return unless inject_flag
|
65
60
|
|
61
|
+
return if has_stackify_tag(source[0..SCAN_LIMIT])
|
62
|
+
|
66
63
|
# Only scan the first 50k (roughly) then give up.
|
67
64
|
insertion_index = find_end_of_head_open(source[0..SCAN_LIMIT])
|
68
65
|
|
69
|
-
if insertion_index
|
66
|
+
if insertion_index
|
70
67
|
source = source[0...insertion_index] <<
|
71
68
|
StackifyRubyAPM.inject_rum_script <<
|
72
69
|
source[insertion_index..-1]
|
@@ -77,13 +74,21 @@ module StackifyRubyAPM
|
|
77
74
|
# rubocop:enable Metrics/PerceivedComplexity
|
78
75
|
|
79
76
|
def find_end_of_head_open(beginning_of_source)
|
80
|
-
head_open = beginning_of_source.index(
|
81
|
-
beginning_of_source.index(GT, head_open)
|
77
|
+
head_open = beginning_of_source.index(HEAD_START)
|
78
|
+
beginning_of_source.index(GT, head_open) + 1 if head_open
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_stackify_tag(beginning_of_source)
|
82
|
+
!!beginning_of_source.index(STACKIFY_SETTING_TAG)
|
82
83
|
end
|
83
84
|
|
84
85
|
def gather_source(response)
|
85
86
|
source = nil
|
86
|
-
response.
|
87
|
+
if response.respond_to?(:each)
|
88
|
+
response.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s) }
|
89
|
+
else
|
90
|
+
source << response
|
91
|
+
end
|
87
92
|
source
|
88
93
|
end
|
89
94
|
|
@@ -31,7 +31,7 @@ module StackifyRubyAPM
|
|
31
31
|
APPLICATION_FILESYSTEM_PATH: @config.root_path,
|
32
32
|
APPLICATION_NAME: @config.application_name.strip,
|
33
33
|
APPLICATION_ENV: @config.environment_name || 'Development',
|
34
|
-
REPORTING_URL: @transaction.name,
|
34
|
+
REPORTING_URL: @transaction.name || '/',
|
35
35
|
TRACE_ID: @transaction.id,
|
36
36
|
THREAD_ID: Thread.current.object_id,
|
37
37
|
TRACE_SOURCE: 'RUBY',
|
@@ -45,7 +45,7 @@ module StackifyRubyAPM
|
|
45
45
|
hash[:METHOD] = @transaction.context.request.method if @transaction.context && @transaction.context.request && @transaction.context.request.method
|
46
46
|
hash[:STATUS] = @transaction.context.response.status_code if @transaction.context && @transaction.context.response && @transaction.context.response.status_code
|
47
47
|
hash[:URL] = @transaction.context.request.url[:full] if @transaction.context && @transaction.context.request && @transaction.context.request.url[:full]
|
48
|
-
hash[:
|
48
|
+
hash[:ISRUM] = 'TRUE' if @transaction.context && @transaction.context.rum
|
49
49
|
hash[:AWS_LAMBDA_ARN] = @transaction.context.aws[:arn] if @transaction.context && @transaction.context.aws && @transaction.context.aws[:arn]
|
50
50
|
hash[:PREFIX_RESPONSE_BODY] = @transaction.context.prefix.response_body.to_s if @transaction.context.prefix && @transaction.context.prefix.response_body
|
51
51
|
hash[:PREFIX_RESPONSE_SIZE_BYTES] = @transaction.context.prefix.response_body.length.to_s if @transaction.context.prefix && @transaction.context.prefix.response_body
|
@@ -70,6 +70,8 @@ module StackifyRubyAPM
|
|
70
70
|
mod_constant = Module.const_get(current_class.to_s)
|
71
71
|
klass_method_flag = mod_constant.method_defined?(current_method.to_s)
|
72
72
|
singleton_method_flag = mod_constant.respond_to?(current_method.to_s)
|
73
|
+
klass_private_method_flag = mod_constant.private_method_defined?(current_method.to_s)
|
74
|
+
klass_protected_method_flag = mod_constant.protected_method_defined?(current_method.to_s)
|
73
75
|
|
74
76
|
class_location = mod_constant.instance_methods(false).map do |m|
|
75
77
|
mod_constant.instance_method(m).source_location.first
|
@@ -77,8 +79,17 @@ module StackifyRubyAPM
|
|
77
79
|
|
78
80
|
class_path = class_location.last
|
79
81
|
|
80
|
-
if klass_method_flag
|
81
|
-
StackifyRubyAPM::InstrumenterHelper.m_class(
|
82
|
+
if klass_method_flag || klass_private_method_flag
|
83
|
+
StackifyRubyAPM::InstrumenterHelper.m_class(
|
84
|
+
tracked_func,
|
85
|
+
current_class,
|
86
|
+
class_path,
|
87
|
+
current_method: current_method,
|
88
|
+
tracked_function_name: tracked_function_name,
|
89
|
+
is_transaction: transaction,
|
90
|
+
is_private_method: klass_private_method_flag,
|
91
|
+
is_protected_method: klass_protected_method_flag
|
92
|
+
)
|
82
93
|
elsif singleton_method_flag
|
83
94
|
StackifyRubyAPM::InstrumenterHelper.m_singleton(tracked_func, current_class, class_path, current_method: current_method, tracked_function_name: tracked_function_name, is_transaction: transaction)
|
84
95
|
end
|
@@ -11,6 +11,7 @@ module StackifyRubyAPM
|
|
11
11
|
::Sinatra::Base.class_eval do
|
12
12
|
alias_method 'dispatch_without_apm!', 'dispatch!'
|
13
13
|
alias_method 'compile_template_without_apm', 'compile_template'
|
14
|
+
alias_method 'route_eval_without_apm', 'route_eval'
|
14
15
|
|
15
16
|
# Sets transaction name from Sinatra env's route name
|
16
17
|
#
|
@@ -34,6 +35,15 @@ module StackifyRubyAPM
|
|
34
35
|
|
35
36
|
compile_template_without_apm(engine, data, opts, *args, &block)
|
36
37
|
end
|
38
|
+
|
39
|
+
def route_eval(*args, &block)
|
40
|
+
if defined?(StackifyRubyAPM.current_transaction)
|
41
|
+
if env.key?('sinatra.route')
|
42
|
+
StackifyRubyAPM.current_transaction.name = env['sinatra.route']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
route_eval_without_apm(*args, &block)
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
end
|
@@ -42,7 +42,7 @@ module StackifyRubyAPM
|
|
42
42
|
message = get_json_message(transactions)
|
43
43
|
conn = Faraday.new(ssl: { verify: false })
|
44
44
|
response = conn.post do |req|
|
45
|
-
req.url URI(@config.transport_http_endpoint + @config.agent_traces_url)
|
45
|
+
req.url URI(@config.transport_http_endpoint + @config.agent_traces_url).to_s
|
46
46
|
req.headers = get_json_headers
|
47
47
|
req.body = message
|
48
48
|
end
|
data/lib/stackify_apm/util.rb
CHANGED
@@ -35,6 +35,58 @@ module StackifyRubyAPM
|
|
35
35
|
obj[i] = val.to_s
|
36
36
|
ary.push(obj)
|
37
37
|
end
|
38
|
+
|
39
|
+
URL_ID_REGEX = /^(\d+)$/
|
40
|
+
URL_GUID_REGEX = /^(?i)(\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b)$/
|
41
|
+
URL_EMAIL_REGEX = /^((([!#$%&'*+\-\/=?^_`{|}~\w])|([!#$%&'*+\-\/=?^_`{|}~\w][!#$%&'*+\-\/=?^_`{|}~\.\w]{0,}[!#$%&'*+\-\/=?^_`{|}~\w]))[@]\w+([-.]\w+)*\.\w+([-.]\w+)*)$/
|
42
|
+
URL_IP_REGEX = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
43
|
+
|
44
|
+
def self.maskReportingUrl(url)
|
45
|
+
parts = url.split('/', -1)
|
46
|
+
|
47
|
+
if (!parts.length)
|
48
|
+
return '/'
|
49
|
+
end
|
50
|
+
|
51
|
+
maskedParts = []
|
52
|
+
parts.each do |part|
|
53
|
+
maskedParts.push(maskString(part))
|
54
|
+
end
|
55
|
+
|
56
|
+
maskedUrl = maskedParts.join('/')
|
57
|
+
|
58
|
+
if (maskedUrl.length == 1)
|
59
|
+
return maskedUrl
|
60
|
+
end
|
61
|
+
|
62
|
+
if maskedUrl.end_with?('/')
|
63
|
+
trimmedUrl = maskedUrl.slice(0, maskedUrl.length-1)
|
64
|
+
if (!trimmedUrl.length)
|
65
|
+
return '/'
|
66
|
+
end
|
67
|
+
|
68
|
+
return trimmedUrl
|
69
|
+
end
|
70
|
+
|
71
|
+
maskedUrl
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.maskString(string)
|
75
|
+
return '{id}' if string =~ URL_ID_REGEX
|
76
|
+
return '{guid}' if string =~ URL_GUID_REGEX
|
77
|
+
return '{email}' if string =~ URL_EMAIL_REGEX
|
78
|
+
return '{ip}' if string =~ URL_IP_REGEX
|
79
|
+
|
80
|
+
if string.include? ';'
|
81
|
+
string_index = string.index(';');
|
82
|
+
return '' if (string_index <= 0)
|
83
|
+
return string[0..string.index(';')-1] if (string_index)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Default
|
87
|
+
string
|
88
|
+
end
|
89
|
+
|
38
90
|
end
|
39
91
|
end
|
40
92
|
require 'stackify_apm/util/inspector'
|
data/lib/stackify_apm/version.rb
CHANGED
data/lib/stackify_ruby_apm.rb
CHANGED
@@ -50,22 +50,67 @@ module StackifyRubyAPM
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def self.inject_rum_script
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
transaction_id = defined?(StackifyRubyAPM.current_transaction.id) ? StackifyRubyAPM.current_transaction.id : nil
|
53
|
+
transaction = defined?(StackifyRubyAPM.current_transaction) ? StackifyRubyAPM.current_transaction : nil
|
54
|
+
return '' unless transaction
|
55
|
+
|
56
|
+
transaction_id = defined?(transaction.id) ? transaction.id : nil
|
58
57
|
inject_flag = false
|
59
58
|
|
60
|
-
|
61
|
-
|
59
|
+
config = nil
|
60
|
+
if defined?(agent.config)
|
61
|
+
config = agent.config
|
62
|
+
end
|
63
|
+
|
64
|
+
return '' unless config
|
65
|
+
|
66
|
+
if transaction_id
|
67
|
+
inject_flag = true
|
68
|
+
end
|
69
|
+
|
70
|
+
return '' unless inject_flag
|
71
|
+
|
72
|
+
return '' if config.rum_script_url.to_s.empty? || config.rum_key.to_s.empty?
|
73
|
+
|
74
|
+
rum_settings = {
|
75
|
+
"ID" => transaction_id
|
76
|
+
}
|
77
|
+
|
78
|
+
if defined?(config.environment_name)
|
79
|
+
environment_name = 'Development'
|
80
|
+
if !config.environment_name.to_s.empty?
|
81
|
+
environment_name = config.environment_name
|
82
|
+
end
|
83
|
+
|
84
|
+
rum_settings["Env"] = Base64.strict_encode64(environment_name.encode('utf-8'))
|
85
|
+
end
|
86
|
+
|
87
|
+
application_name = config.application_name
|
88
|
+
return '' if application_name.to_s.empty?
|
89
|
+
|
90
|
+
rum_settings["Name"] = Base64.strict_encode64(application_name.strip.encode('utf-8'))
|
91
|
+
|
92
|
+
if defined?(transaction.name) && !transaction.name.to_s.empty?
|
93
|
+
reporting_url = Util.maskReportingUrl(transaction.name)
|
94
|
+
|
95
|
+
if defined?(transaction.context.request.method) && !transaction.context.request.nil? && !transaction.context.request.method.nil?
|
96
|
+
reporting_url = "#{transaction.context.request.method}-#{reporting_url}"
|
97
|
+
end
|
98
|
+
|
99
|
+
rum_settings["Trans"] = Base64.strict_encode64(reporting_url.encode('utf-8'))
|
62
100
|
end
|
63
101
|
|
64
|
-
return unless inject_flag
|
65
|
-
data_request_id = "V2|#{transaction_id}|#{client_id}|#{device_id}"
|
66
102
|
@rum_script_injected = true
|
67
|
-
|
68
|
-
|
103
|
+
|
104
|
+
if defined?(transaction.context)
|
105
|
+
transaction.context.rum = true
|
106
|
+
end
|
107
|
+
|
108
|
+
rum_content = "<script type=\"text/javascript\">(window.StackifySettings || (window.StackifySettings = #{rum_settings.to_json}))</script><script src=\"#{config.rum_script_url}\" data-key=\"#{config.rum_key}\" async></script>"
|
109
|
+
if rum_content.respond_to?(:html_safe)
|
110
|
+
rum_content.html_safe
|
111
|
+
else
|
112
|
+
rum_content
|
113
|
+
end
|
69
114
|
end
|
70
115
|
|
71
116
|
# Run the custom instrument
|
data/stackify-ruby-apm.gemspec
CHANGED
@@ -32,16 +32,22 @@ Gem::Specification.new do |spec|
|
|
32
32
|
if RUBY_VERSION > '2.5'
|
33
33
|
spec.add_development_dependency 'bigdecimal'
|
34
34
|
end
|
35
|
+
|
36
|
+
if RUBY_PLATFORM == 'i386-mingw32'
|
37
|
+
spec.add_development_dependency 'tzinfo-data'
|
38
|
+
else
|
39
|
+
spec.add_development_dependency 'mongo'
|
40
|
+
spec.add_development_dependency 'curb', '0.9.8'
|
41
|
+
end
|
35
42
|
|
36
|
-
spec.add_development_dependency 'bundler', '~> 1.
|
43
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
37
44
|
spec.add_development_dependency 'rake', '~> 10.0'
|
38
45
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
46
|
spec.add_development_dependency 'activerecord'
|
40
|
-
|
47
|
+
|
41
48
|
spec.add_development_dependency 'fakeredis'
|
42
49
|
spec.add_development_dependency 'http'
|
43
50
|
spec.add_development_dependency 'httpclient'
|
44
|
-
spec.add_development_dependency 'mongo'
|
45
51
|
spec.add_development_dependency 'mysql2'
|
46
52
|
spec.add_development_dependency 'pg', '~> 0.20'
|
47
53
|
spec.add_development_dependency 'rack-test'
|
@@ -55,9 +61,9 @@ Gem::Specification.new do |spec|
|
|
55
61
|
spec.add_development_dependency 'to_bool'
|
56
62
|
spec.add_development_dependency 'webmock'
|
57
63
|
spec.add_development_dependency 'delayed_job'
|
64
|
+
spec.add_development_dependency('delegate_matcher', '~> 0.4') # for testing
|
58
65
|
|
59
66
|
spec.add_dependency('concurrent-ruby', '~> 1.0')
|
60
|
-
spec.add_dependency('delegate_matcher', '~> 0.4')
|
61
67
|
spec.add_dependency('faraday', '~> 0.8')
|
62
68
|
spec.add_dependency('net_http_unix', '~> 0.2')
|
63
69
|
spec.add_dependency('rufus-scheduler', '~> 3.0')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackify-ruby-apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.15.3.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stackify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -39,77 +39,77 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: mongo
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: curb
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.9.8
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.9.8
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '1.17'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '1.17'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
89
|
+
version: '10.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
96
|
+
version: '10.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rspec
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0
|
103
|
+
version: '3.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0
|
110
|
+
version: '3.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: activerecord
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: fakeredis
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: http
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: httpclient
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
@@ -347,33 +347,33 @@ dependencies:
|
|
347
347
|
- !ruby/object:Gem::Version
|
348
348
|
version: '0'
|
349
349
|
- !ruby/object:Gem::Dependency
|
350
|
-
name:
|
350
|
+
name: delegate_matcher
|
351
351
|
requirement: !ruby/object:Gem::Requirement
|
352
352
|
requirements:
|
353
353
|
- - "~>"
|
354
354
|
- !ruby/object:Gem::Version
|
355
|
-
version: '
|
356
|
-
type: :
|
355
|
+
version: '0.4'
|
356
|
+
type: :development
|
357
357
|
prerelease: false
|
358
358
|
version_requirements: !ruby/object:Gem::Requirement
|
359
359
|
requirements:
|
360
360
|
- - "~>"
|
361
361
|
- !ruby/object:Gem::Version
|
362
|
-
version: '
|
362
|
+
version: '0.4'
|
363
363
|
- !ruby/object:Gem::Dependency
|
364
|
-
name:
|
364
|
+
name: concurrent-ruby
|
365
365
|
requirement: !ruby/object:Gem::Requirement
|
366
366
|
requirements:
|
367
367
|
- - "~>"
|
368
368
|
- !ruby/object:Gem::Version
|
369
|
-
version: '0
|
369
|
+
version: '1.0'
|
370
370
|
type: :runtime
|
371
371
|
prerelease: false
|
372
372
|
version_requirements: !ruby/object:Gem::Requirement
|
373
373
|
requirements:
|
374
374
|
- - "~>"
|
375
375
|
- !ruby/object:Gem::Version
|
376
|
-
version: '0
|
376
|
+
version: '1.0'
|
377
377
|
- !ruby/object:Gem::Dependency
|
378
378
|
name: faraday
|
379
379
|
requirement: !ruby/object:Gem::Requirement
|
@@ -533,9 +533,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
533
533
|
version: '0'
|
534
534
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
535
535
|
requirements:
|
536
|
-
- - "
|
536
|
+
- - ">"
|
537
537
|
- !ruby/object:Gem::Version
|
538
|
-
version:
|
538
|
+
version: 1.3.1
|
539
539
|
requirements: []
|
540
540
|
rubygems_version: 3.0.1
|
541
541
|
signing_key:
|