stackify-ruby-apm 1.1.1 → 1.2.4
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 +5 -5
- data/.gemignore +8 -0
- data/README.md +31 -1
- data/lib/stackify_apm/agent.rb +1 -0
- data/lib/stackify_apm/config.rb +23 -0
- data/lib/stackify_apm/middleware.rb +62 -2
- data/lib/stackify_apm/response_manipulator.rb +139 -0
- data/lib/stackify_apm/span/context.rb +2 -1
- data/lib/stackify_apm/spies.rb +20 -0
- data/lib/stackify_apm/spies/custom_instrumenter.rb +108 -0
- data/lib/stackify_apm/version.rb +1 -1
- data/lib/stackify_ruby_apm.rb +36 -1
- data/stackify-ruby-apm.gemspec +3 -5
- metadata +6 -9
- data/docker/stackify-ruby +0 -8
- data/docker/stackify-ruby-rvm +0 -10
- data/docker/stackify-ruby-test +0 -28
- data/run-test-docker.sh +0 -50
- data/run-test.sh +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c797df00561661da936aeef53e4064ac64a7742156b7c83c6ba770880202f53f
|
4
|
+
data.tar.gz: 32f9a06b9f3b0ab131c552d7cbfa3e22dd074b5689e67ed5d42cb3edd99c43a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ca79814064746539fb96db7d3e5ff5241c8213973a9fba444fc69507756e60d138e4335202bc73d0ca5a6e2df1984afeeb6836b98681f64bf06f7337523b28f
|
7
|
+
data.tar.gz: 824820eb08bd65189a0292d3c0f6d8138923aff31552828bc788a072ad781d7bb660c51fe7439eb00891a8a9597713ed92178f7133fe9a13fb7d86a0becf6952
|
data/.gemignore
ADDED
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
## Installation Guide
|
5
5
|
|
6
|
+
### Rails Guide
|
7
|
+
|
6
8
|
1. Install **Stackify Linux Agent**.
|
7
9
|
|
8
10
|
2. Check that your setup meets our system requirements.
|
@@ -21,5 +23,33 @@
|
|
21
23
|
application_name: 'Ruby Application'
|
22
24
|
environment_name: 'Production'
|
23
25
|
```
|
24
|
-
5. Build/Deploy your application
|
26
|
+
5. Build/Deploy your application
|
27
|
+
|
28
|
+
### Non-Rails Guide
|
29
|
+
|
30
|
+
1. Install **Stackify Linux Agent**.
|
31
|
+
|
32
|
+
2. Check that your setup meets our system requirements.
|
33
|
+
* Ruby version 2.0+
|
34
|
+
* Frameworks
|
35
|
+
* Rack 1+
|
36
|
+
* Sinatra 1.4+
|
37
|
+
* Operating System
|
38
|
+
* Linux
|
39
|
+
|
40
|
+
3. Add `gem 'stackify-ruby-apm'` to your `Gemfile`.
|
41
|
+
|
42
|
+
4. In the root folder of your Rails app create a file `config/stackify_apm.yml` and then add this configuration
|
43
|
+
```yaml
|
44
|
+
application_name: 'Ruby Application'
|
45
|
+
environment_name: 'Production'
|
46
|
+
```
|
47
|
+
|
48
|
+
5. In the `config.ru` add the following lines:
|
49
|
+
|
50
|
+
``` use StackifyRubyAPM::Middleware
|
51
|
+
StackifyRubyAPM.start
|
52
|
+
at_exit { StackifyRubyAPM.stop }
|
53
|
+
```
|
25
54
|
|
55
|
+
6. Build/Deploy your application
|
data/lib/stackify_apm/agent.rb
CHANGED
data/lib/stackify_apm/config.rb
CHANGED
@@ -13,8 +13,14 @@ module StackifyRubyAPM
|
|
13
13
|
include Log
|
14
14
|
DEFAULTS = {
|
15
15
|
config_file: 'config/stackify_apm.yml',
|
16
|
+
json_config_file: 'config/stackify.json',
|
17
|
+
rum_script_src: 'https://stckjs.azureedge.net/stckjs.js',
|
18
|
+
application_name: 'Ruby Application',
|
16
19
|
environment_name: ENV['RAILS_ENV'] || ENV['RACK_ENV'],
|
20
|
+
already_instrumented_flag: false,
|
21
|
+
rum_auto_injection: false,
|
17
22
|
instrument: true,
|
23
|
+
debug_logging: false,
|
18
24
|
log_path: '/usr/local/stackify/stackify-ruby-apm/log/stackify-ruby-apm.log',
|
19
25
|
log_level: Logger::INFO,
|
20
26
|
log_trace_path: '/usr/local/stackify/stackify-ruby-apm/log/',
|
@@ -50,6 +56,10 @@ module StackifyRubyAPM
|
|
50
56
|
|
51
57
|
ENV_TO_KEY = {
|
52
58
|
'STACKIFY_APM_ENVIRONMENT_NAME' => 'environment_name',
|
59
|
+
'STACKIFY_APM_RUM_SCRIPT_SRC' => 'rum_script_src',
|
60
|
+
'STACKIFY_APM_RUM_AUTO_INJECTION' => 'rum_auto_injection',
|
61
|
+
'STACKIFY_ALREADY_INSTRUMENTED_FLAG' => 'already_instrumented_flag',
|
62
|
+
'STACKIFY_APM_DEBUG_LOGGING' => 'debug_logging',
|
53
63
|
'STACKIFY_APM_INSTRUMENT' => [:bool, 'instrument'],
|
54
64
|
'STACKIFY_APM_HOSTNAME' => 'hostname',
|
55
65
|
'STACKIFY_APM_LOG_PATH' => 'log_path',
|
@@ -79,7 +89,12 @@ module StackifyRubyAPM
|
|
79
89
|
end
|
80
90
|
|
81
91
|
attr_accessor :config_file
|
92
|
+
attr_accessor :json_config_file
|
82
93
|
attr_accessor :environment_name
|
94
|
+
attr_accessor :rum_script_src
|
95
|
+
attr_accessor :rum_auto_injection
|
96
|
+
attr_accessor :already_instrumented_flag
|
97
|
+
attr_accessor :rum_script_injected
|
83
98
|
attr_accessor :instrument
|
84
99
|
attr_accessor :enabled_environments
|
85
100
|
attr_accessor :stackify_properties_file
|
@@ -90,6 +105,7 @@ module StackifyRubyAPM
|
|
90
105
|
attr_accessor :log_path
|
91
106
|
attr_accessor :log_level
|
92
107
|
attr_accessor :logger
|
108
|
+
attr_accessor :debug_logging
|
93
109
|
attr_accessor :logger_byte_size
|
94
110
|
attr_accessor :filenum_rotate
|
95
111
|
attr_accessor :debugger_byte_size
|
@@ -123,6 +139,7 @@ module StackifyRubyAPM
|
|
123
139
|
attr_reader :client_id
|
124
140
|
attr_reader :device_id
|
125
141
|
attr_reader :apm_disabled_in_rake
|
142
|
+
attr_reader :client_run_domain
|
126
143
|
|
127
144
|
def app=(app)
|
128
145
|
case app_type?(app)
|
@@ -150,6 +167,7 @@ module StackifyRubyAPM
|
|
150
167
|
action_dispatch
|
151
168
|
mongo
|
152
169
|
net_http
|
170
|
+
custom_instrumenter
|
153
171
|
httpclient
|
154
172
|
redis
|
155
173
|
sequel
|
@@ -250,9 +268,11 @@ module StackifyRubyAPM
|
|
250
268
|
str.gsub('::', '_')
|
251
269
|
end
|
252
270
|
|
271
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
253
272
|
def load_stackify_props
|
254
273
|
@client_id = nil
|
255
274
|
@device_id = nil
|
275
|
+
@client_run_domain = nil
|
256
276
|
begin
|
257
277
|
info 'Reading the stackify.properties file'
|
258
278
|
IO.foreach(@stackify_properties_file) do |line|
|
@@ -261,6 +281,8 @@ module StackifyRubyAPM
|
|
261
281
|
@client_id = line.split('=')[1].strip
|
262
282
|
when /deviceid=\d+/
|
263
283
|
@device_id = line.split('=')[1].strip
|
284
|
+
when /clientrumdomain=([\w\s\d\"\']+)+/i
|
285
|
+
@client_run_domain = line.split('=')[1].strip
|
264
286
|
end
|
265
287
|
end
|
266
288
|
info "stackify.properties: clientId=#{@client_id}, deviceId=#{@device_id}"
|
@@ -271,5 +293,6 @@ module StackifyRubyAPM
|
|
271
293
|
info 'No clientId found from stackify.properties file.' if @client_id.nil?
|
272
294
|
info 'No deviceId found from stackify.properties file.' if @device_id.nil?
|
273
295
|
end
|
296
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
274
297
|
end
|
275
298
|
end
|
@@ -18,23 +18,55 @@
|
|
18
18
|
# -> transaction will be stored in queue by Agent
|
19
19
|
# -> worker will constantly execute transaction sending to APM
|
20
20
|
#
|
21
|
+
require 'stackify_apm/response_manipulator'
|
21
22
|
|
22
23
|
module StackifyRubyAPM
|
23
24
|
# @api private
|
24
25
|
class Middleware
|
26
|
+
attr_accessor :rack_body, :rack_headers, :rack_status, :configuration
|
27
|
+
|
28
|
+
CONTENT_TYPE_REGEX = %r{text\/html|application\/xhtml\+xml/}
|
29
|
+
CONTENT_DISPOSITION = 'Content-Disposition'.freeze
|
30
|
+
ATTACHMENT = 'attachment'.freeze
|
31
|
+
|
25
32
|
def initialize(app)
|
26
33
|
@app = app
|
27
34
|
end
|
28
35
|
|
29
36
|
# This is where the requests are received, built into a transaction, and then submitted
|
30
37
|
#
|
38
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
39
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
31
40
|
def call(env)
|
32
41
|
begin
|
33
|
-
# if running? && !path_ignored?(env)
|
34
42
|
transaction = build_transaction(env) if running?
|
35
|
-
|
36
43
|
resp = @app.call env
|
37
44
|
|
45
|
+
@rack_status = resp[0].to_i
|
46
|
+
@rack_headers = resp[1]
|
47
|
+
@rack_body = resp[2]
|
48
|
+
@configuration = config
|
49
|
+
response_manupulate = StackifyRubyAPM::ResponseManipulator.new(env, resp, @configuration)
|
50
|
+
|
51
|
+
if okay_to_modify?
|
52
|
+
@configuration.already_instrumented_flag = true
|
53
|
+
if StackifyRubyAPM.rum_script_inject
|
54
|
+
resp
|
55
|
+
else
|
56
|
+
if @configuration.rum_auto_injection
|
57
|
+
response_string = response_manupulate.call_manipulate
|
58
|
+
end
|
59
|
+
if response_string
|
60
|
+
response = Rack::Response.new(response_string, @rack_status, @rack_headers)
|
61
|
+
resp = response.finish
|
62
|
+
else
|
63
|
+
resp
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
resp
|
68
|
+
end
|
69
|
+
|
38
70
|
submit_transaction(transaction, *resp) if transaction
|
39
71
|
rescue InternalError
|
40
72
|
raise # Don't report StackifyRubyAPM errors
|
@@ -47,6 +79,8 @@ module StackifyRubyAPM
|
|
47
79
|
|
48
80
|
resp
|
49
81
|
end
|
82
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
83
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
50
84
|
|
51
85
|
def submit_transaction(transaction, status, headers, _body)
|
52
86
|
result = status.to_i
|
@@ -66,5 +100,31 @@ module StackifyRubyAPM
|
|
66
100
|
def config
|
67
101
|
StackifyRubyAPM.agent.config
|
68
102
|
end
|
103
|
+
|
104
|
+
def okay_to_modify?
|
105
|
+
return false if xhr?
|
106
|
+
return false unless modifiable_content_type?
|
107
|
+
return false if @rack_status == 404 || @rack_status == 501
|
108
|
+
return false if attachment?
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def modifiable_content_type?
|
113
|
+
@rack_headers['Content-Type'] =~ self.class::CONTENT_TYPE_REGEX
|
114
|
+
end
|
115
|
+
|
116
|
+
def attachment?
|
117
|
+
@rack_headers[CONTENT_DISPOSITION] && @rack_headers[CONTENT_DISPOSITION].include?(ATTACHMENT)
|
118
|
+
end
|
119
|
+
|
120
|
+
def streaming?(env)
|
121
|
+
return false unless defined?(ActionController::Live)
|
122
|
+
|
123
|
+
env['action_controller.instance'].class.included_modules.include?(ActionController::Live)
|
124
|
+
end
|
125
|
+
|
126
|
+
def xhr?
|
127
|
+
@rack_headers['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
|
128
|
+
end
|
69
129
|
end
|
70
130
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class will manupulate the html page d: e.g., call(env)
|
5
|
+
#
|
6
|
+
#
|
7
|
+
module StackifyRubyAPM
|
8
|
+
# an abstraction for manipulating the HTML we capture in the middleware
|
9
|
+
class ResponseManipulator
|
10
|
+
attr_reader :rack_response, :jsfile_to_inject
|
11
|
+
attr_reader :rack_status, :rack_headers, :rack_body, :Rack_flagger
|
12
|
+
attr_reader :env
|
13
|
+
|
14
|
+
# examine in order to look for a RUM insertion point.
|
15
|
+
SCAN_LIMIT = 50_000
|
16
|
+
HEAD_START = '<head'.freeze
|
17
|
+
HEAD_END = '</head'.freeze
|
18
|
+
RUM_SCRIPT_VARIABLE = '[RUM_SCRIPT_VARIABLE'.freeze
|
19
|
+
BRKT = ']'.freeze
|
20
|
+
GT = '>'.freeze
|
21
|
+
CHARSET_RE = /<\s*meta[^>]+charset\s*=[^>]*>/
|
22
|
+
X_UA_COMPATIBLE_RE = /<\s*meta[^>]+http-equiv\s*=\s*['"]x-ua-compatible['"][^>]*>/
|
23
|
+
|
24
|
+
def initialize(env, rack_response, config)
|
25
|
+
@env = env
|
26
|
+
@rack_response = rack_response
|
27
|
+
|
28
|
+
@rack_status = rack_response[0]
|
29
|
+
@rack_headers = rack_response[1]
|
30
|
+
@rack_body = rack_response[2]
|
31
|
+
@rack_flagger = nil
|
32
|
+
@config = config
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_manipulate
|
36
|
+
if check_rumscript_variable
|
37
|
+
adjust_pagehtml_response(true)
|
38
|
+
else
|
39
|
+
adjust_pagehtml_response
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_rumscript_variable
|
44
|
+
response = @rack_body
|
45
|
+
source = gather_source(response)
|
46
|
+
close_old_response(response)
|
47
|
+
return false unless source
|
48
|
+
|
49
|
+
return true if source.include?(RUM_SCRIPT_VARIABLE)
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def rebuild_rack_response
|
54
|
+
[rack_status, rack_headers, rack_body]
|
55
|
+
end
|
56
|
+
|
57
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
58
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
59
|
+
def adjust_pagehtml_response(rum_variable_script = false)
|
60
|
+
response = @rack_body
|
61
|
+
source = gather_source(response)
|
62
|
+
close_old_response(response)
|
63
|
+
return nil unless source
|
64
|
+
|
65
|
+
client_id = @config.client_id
|
66
|
+
device_id = @config.device_id
|
67
|
+
client_rundomain = @config.client_run_domain
|
68
|
+
transaction_id = defined?(StackifyRubyAPM.current_transaction.id) ? StackifyRubyAPM.current_transaction.id : nil
|
69
|
+
data_request_id = 'V2|' + transaction_id + '|C' + client_id + '|' + device_id
|
70
|
+
inject_flag = false
|
71
|
+
|
72
|
+
if client_rundomain != '' && StackifyRubyAPM.check_isdomain(client_rundomain)
|
73
|
+
inject_flag = true
|
74
|
+
else
|
75
|
+
warn 'ClientRumDomain might be empty or Invalid URL stackify.properties.'
|
76
|
+
end
|
77
|
+
|
78
|
+
jsfile_to_inject = '<script src="' + @config.rum_script_src + '" data-host="' + client_rundomain + '"
|
79
|
+
data-requestId="' + data_request_id + '"
|
80
|
+
data-a="' + @config.application_name + '"
|
81
|
+
data-e="' + @config.environment_name + '"
|
82
|
+
data-enableInternalLogging="' + @config.debug_logging.to_s + '" type="text/javascript" async></script>'
|
83
|
+
|
84
|
+
# Only scan the first 50k (roughly) then give up.
|
85
|
+
beginning_of_source = source[0..SCAN_LIMIT]
|
86
|
+
|
87
|
+
meta_tag_positions = [
|
88
|
+
find_x_ua_compatible_position(beginning_of_source),
|
89
|
+
find_charset_position(beginning_of_source)
|
90
|
+
].compact
|
91
|
+
|
92
|
+
insertion_index = if !meta_tag_positions.empty?
|
93
|
+
meta_tag_positions.max
|
94
|
+
else
|
95
|
+
find_end_of_head_open(beginning_of_source)
|
96
|
+
end
|
97
|
+
|
98
|
+
if insertion_index && inject_flag
|
99
|
+
source = source[0...insertion_index] <<
|
100
|
+
jsfile_to_inject <<
|
101
|
+
source[insertion_index..-1]
|
102
|
+
source = source.gsub('[RUM_SCRIPT_VARIABLE]', '') if rum_variable_script
|
103
|
+
end
|
104
|
+
source
|
105
|
+
end
|
106
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
107
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
108
|
+
|
109
|
+
def find_end_of_head_open(beginning_of_source)
|
110
|
+
head_open = beginning_of_source.index(HEAD_END)
|
111
|
+
beginning_of_source.index(GT, head_open) - 6 if head_open
|
112
|
+
end
|
113
|
+
|
114
|
+
def find_rum_script_variable_index(beginning_of_source)
|
115
|
+
head_open = beginning_of_source.index(RUM_SCRIPT_VARIABLE)
|
116
|
+
beginning_of_source.index(BRKT, head_open) - 20 if head_open
|
117
|
+
end
|
118
|
+
|
119
|
+
def find_x_ua_compatible_position(beginning_of_source)
|
120
|
+
match = X_UA_COMPATIBLE_RE.match(beginning_of_source)
|
121
|
+
match.end(0) if match
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_charset_position(beginning_of_source)
|
125
|
+
match = CHARSET_RE.match(beginning_of_source)
|
126
|
+
match.end(0) if match
|
127
|
+
end
|
128
|
+
|
129
|
+
def gather_source(response)
|
130
|
+
source = nil
|
131
|
+
response.each { |fragment| source ? (source << fragment.to_s) : (source = fragment.to_s) }
|
132
|
+
source
|
133
|
+
end
|
134
|
+
|
135
|
+
def close_old_response(response)
|
136
|
+
response.close if response.respond_to?(:close)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/lib/stackify_apm/spies.rb
CHANGED
@@ -65,6 +65,26 @@ module StackifyRubyAPM
|
|
65
65
|
def self.safe_defined?(const_name)
|
66
66
|
Util::Inflector.safe_constantize(const_name)
|
67
67
|
end
|
68
|
+
|
69
|
+
def self.parse_json_config(file)
|
70
|
+
jsondata = {}
|
71
|
+
if ENV['STACKIFY_RUBY_ENV'] == 'rspec'
|
72
|
+
file = 'spec/integration/stackify.json'
|
73
|
+
end
|
74
|
+
return unless File.exist?(file)
|
75
|
+
|
76
|
+
File.open(file) do |f|
|
77
|
+
jsondata = JSON.parse(f.read)
|
78
|
+
end
|
79
|
+
jsondata
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.class_exists?(class_name)
|
83
|
+
klass = Module.const_get(class_name)
|
84
|
+
return klass.is_a?(Class)
|
85
|
+
rescue NameError
|
86
|
+
return false
|
87
|
+
end
|
68
88
|
end
|
69
89
|
end
|
70
90
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patch for the custom instrumentation any values from config/stackify.json will be loop and will
|
4
|
+
# register as a spy.
|
5
|
+
#
|
6
|
+
require 'stackify_apm/config'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module StackifyRubyAPM
|
10
|
+
# @api private
|
11
|
+
module Spies
|
12
|
+
def self.m_class(tracked_func, current_class, current_method, tracked_function_name)
|
13
|
+
Module.const_get(current_class.to_s).class_eval <<-RUBY
|
14
|
+
alias :"__without_apm_#{current_method}" :"#{current_method}"
|
15
|
+
|
16
|
+
def #{current_method}(*args, &block)
|
17
|
+
return __without_apm_#{current_method}(*args, &block) unless StackifyRubyAPM.current_transaction
|
18
|
+
name = "Custom Instrument"
|
19
|
+
type = "#{current_class}##{current_method}"
|
20
|
+
ctx = if "#{tracked_func}" == 'true'
|
21
|
+
Span::Context.new(
|
22
|
+
CATEGORY: 'Ruby',
|
23
|
+
TRACKED_FUNC: "#{tracked_function_name}"
|
24
|
+
)
|
25
|
+
else
|
26
|
+
Span::Context.new(
|
27
|
+
CATEGORY: 'Ruby'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
req = __without_apm_#{current_method}(*args, &block)
|
31
|
+
StackifyRubyAPM.span name, type, context: ctx do
|
32
|
+
req
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_class_name
|
37
|
+
return self.class.name
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.m_singleton(tracked_func, current_class, current_method, tracked_function_name)
|
43
|
+
Module.const_get(current_class.to_s).class_eval <<-RUBY
|
44
|
+
singleton_class.send(:alias_method, :"__self_without_apm_#{current_method}", :"#{current_method}")
|
45
|
+
|
46
|
+
def self.#{current_method}(*args, &block)
|
47
|
+
return __self_without_apm_#{current_method}(*args, &block) unless StackifyRubyAPM.current_transaction
|
48
|
+
|
49
|
+
name = "Custom Instrument"
|
50
|
+
type = "#{current_class}##{current_method}"
|
51
|
+
ctx = if "#{tracked_func}" == 'true'
|
52
|
+
Span::Context.new(
|
53
|
+
CATEGORY: 'Ruby',
|
54
|
+
TRACKED_FUNC: "#{tracked_function_name}"
|
55
|
+
)
|
56
|
+
else
|
57
|
+
Span::Context.new(
|
58
|
+
CATEGORY: 'Ruby'
|
59
|
+
)
|
60
|
+
end
|
61
|
+
req = __self_without_apm_#{current_method}(*args, &block)
|
62
|
+
StackifyRubyAPM.span name, type, context: ctx do
|
63
|
+
req
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.get_class_name
|
68
|
+
return self.class.name
|
69
|
+
end
|
70
|
+
RUBY
|
71
|
+
end
|
72
|
+
|
73
|
+
config = Config.new
|
74
|
+
to_instrument = parse_json_config(config.json_config_file)
|
75
|
+
if defined?(to_instrument['instrumentation']) && (to_instrument['instrumentation'].count > 0)
|
76
|
+
to_instrument['instrumentation'].each do |custom_spy|
|
77
|
+
current_class = custom_spy['class']
|
78
|
+
current_method = custom_spy['method']
|
79
|
+
tracked_func = custom_spy['trackedFunction']
|
80
|
+
tracked_func_name = if defined?(custom_spy['trackedFunctionName'])
|
81
|
+
custom_spy['trackedFunctionName']
|
82
|
+
end
|
83
|
+
|
84
|
+
tracked_function_tpl = if tracked_func_name.nil?
|
85
|
+
'{{ClassName}}.{{MethodName}}'
|
86
|
+
else
|
87
|
+
tracked_func_name
|
88
|
+
end
|
89
|
+
|
90
|
+
tracked_function_name = tracked_function_tpl.gsub '{{ClassName}}', current_class
|
91
|
+
tracked_function_name = tracked_function_name.gsub '{{MethodName}}', current_method
|
92
|
+
|
93
|
+
# rubocop:disable Style/Next
|
94
|
+
if class_exists?(current_class)
|
95
|
+
mod_constant = Module.const_get(current_class.to_s)
|
96
|
+
klass_method_flag = mod_constant.method_defined?(current_method.to_s)
|
97
|
+
singleton_method_flag = mod_constant.respond_to?(current_method.to_s)
|
98
|
+
if klass_method_flag
|
99
|
+
StackifyRubyAPM::Spies.m_class(tracked_func, current_class, current_method, tracked_function_name)
|
100
|
+
elsif singleton_method_flag
|
101
|
+
StackifyRubyAPM::Spies.m_singleton(tracked_func, current_class, current_method, tracked_function_name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
# rubocop:enable Style/Next
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/stackify_apm/version.rb
CHANGED
data/lib/stackify_ruby_apm.rb
CHANGED
@@ -25,7 +25,6 @@ require 'stackify_apm/context'
|
|
25
25
|
require 'stackify_apm/instrumenter'
|
26
26
|
require 'stackify_apm/internal_error'
|
27
27
|
require 'stackify_apm/util'
|
28
|
-
|
29
28
|
require 'stackify_apm/middleware'
|
30
29
|
|
31
30
|
# Checks if the framework using is Rails
|
@@ -41,6 +40,27 @@ module StackifyRubyAPM
|
|
41
40
|
Agent.start config
|
42
41
|
end
|
43
42
|
|
43
|
+
def self.inject_rum_script
|
44
|
+
config = StackifyRubyAPM::Config.new
|
45
|
+
client_id = config.client_id
|
46
|
+
device_id = config.device_id
|
47
|
+
client_rundomain = config.client_run_domain
|
48
|
+
transaction_id = defined?(StackifyRubyAPM.current_transaction.id) ? StackifyRubyAPM.current_transaction.id : nil
|
49
|
+
data_request_id = 'V2|' + transaction_id + '|C' + client_id + '|' + device_id
|
50
|
+
inject_flag = false
|
51
|
+
|
52
|
+
if client_rundomain != '' && check_isdomain(client_rundomain)
|
53
|
+
inject_flag = true
|
54
|
+
else
|
55
|
+
warn 'ClientRumDomain might be empty or Invalid URL stackify.properties.'
|
56
|
+
end
|
57
|
+
|
58
|
+
return unless inject_flag
|
59
|
+
@rum_script_injected = true
|
60
|
+
"<script src=\"#{config.rum_script_src}\" data-host=\"#{client_rundomain}\" data-requestId=\"#{data_request_id}\"
|
61
|
+
data-a=\"#{config.application_name}\" data-e=\"#{config.environment_name}\" data-enableInternalLogging=\"#{config.debug_logging}\" type=\"text/javascript\" async></script>"
|
62
|
+
end
|
63
|
+
|
44
64
|
# Stops the StackifyRubyAPM Agent
|
45
65
|
def self.stop
|
46
66
|
Agent.stop
|
@@ -126,4 +146,19 @@ module StackifyRubyAPM
|
|
126
146
|
def self.report_message(message, **attrs)
|
127
147
|
agent && agent.report_message(message, backtrace: caller, **attrs)
|
128
148
|
end
|
149
|
+
|
150
|
+
# Check if the URL is a domain
|
151
|
+
#
|
152
|
+
# @param message [String] The message
|
153
|
+
# @return TRUE/FALASE
|
154
|
+
def self.check_isdomain(url)
|
155
|
+
url =~ %r{^(http|https):\/\/|[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/.*)?$}
|
156
|
+
end
|
157
|
+
|
158
|
+
# Check if the RUM Script is Injected
|
159
|
+
#
|
160
|
+
# @return TRUE/FALASE
|
161
|
+
def self.rum_script_inject
|
162
|
+
@rum_script_injected
|
163
|
+
end
|
129
164
|
end
|
data/stackify-ruby-apm.gemspec
CHANGED
@@ -13,12 +13,10 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = 'http://www.stackify.com'
|
14
14
|
spec.license = 'Stackify'
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
git_tracked_files = `git ls-files -z`.split("\x0")
|
17
|
+
gem_ignored_files = `git ls-files -i -X .gemignore -z`.split("\x0")
|
18
|
+
spec.files = git_tracked_files - gem_ignored_files
|
18
19
|
|
19
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|docs)/}) }
|
21
|
-
end
|
22
20
|
spec.bindir = 'exe'
|
23
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
22
|
spec.require_paths = ['lib']
|
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.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stackify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -101,6 +101,7 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- ".gemignore"
|
104
105
|
- ".gitignore"
|
105
106
|
- ".rspec"
|
106
107
|
- ".rubocop.yml"
|
@@ -111,9 +112,6 @@ files:
|
|
111
112
|
- README.md
|
112
113
|
- Rakefile
|
113
114
|
- docker-compose.yml
|
114
|
-
- docker/stackify-ruby
|
115
|
-
- docker/stackify-ruby-rvm
|
116
|
-
- docker/stackify-ruby-test
|
117
115
|
- lib/stackify-ruby-apm.rb
|
118
116
|
- lib/stackify_apm/agent.rb
|
119
117
|
- lib/stackify_apm/config.rb
|
@@ -142,6 +140,7 @@ files:
|
|
142
140
|
- lib/stackify_apm/normalizers/action_view.rb
|
143
141
|
- lib/stackify_apm/normalizers/active_record.rb
|
144
142
|
- lib/stackify_apm/railtie.rb
|
143
|
+
- lib/stackify_apm/response_manipulator.rb
|
145
144
|
- lib/stackify_apm/root_info.rb
|
146
145
|
- lib/stackify_apm/serializers.rb
|
147
146
|
- lib/stackify_apm/serializers/errors.rb
|
@@ -153,6 +152,7 @@ files:
|
|
153
152
|
- lib/stackify_apm/spies/curb.rb
|
154
153
|
- lib/stackify_apm/spies/curb/easy.rb
|
155
154
|
- lib/stackify_apm/spies/curb/multi.rb
|
155
|
+
- lib/stackify_apm/spies/custom_instrumenter.rb
|
156
156
|
- lib/stackify_apm/spies/httpclient.rb
|
157
157
|
- lib/stackify_apm/spies/httprb.rb
|
158
158
|
- lib/stackify_apm/spies/mongo.rb
|
@@ -180,8 +180,6 @@ files:
|
|
180
180
|
- lib/stackify_apm/version.rb
|
181
181
|
- lib/stackify_apm/worker.rb
|
182
182
|
- lib/stackify_ruby_apm.rb
|
183
|
-
- run-test-docker.sh
|
184
|
-
- run-test.sh
|
185
183
|
- stackify-ruby-apm.gemspec
|
186
184
|
homepage: http://www.stackify.com
|
187
185
|
licenses:
|
@@ -202,8 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
202
200
|
- !ruby/object:Gem::Version
|
203
201
|
version: '0'
|
204
202
|
requirements: []
|
205
|
-
|
206
|
-
rubygems_version: 2.5.2.3
|
203
|
+
rubygems_version: 3.0.1
|
207
204
|
signing_key:
|
208
205
|
specification_version: 4
|
209
206
|
summary: Stackify APM for Ruby
|
data/docker/stackify-ruby
DELETED
data/docker/stackify-ruby-rvm
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
FROM ubuntu:18.04
|
2
|
-
|
3
|
-
RUN \
|
4
|
-
export DEBIAN_FRONTEND=noninteractiv && \
|
5
|
-
apt-get update && \
|
6
|
-
apt-get install -y curl git libcurl4-gnutls-dev libmysqlclient-dev libmysqld-dev libpq-dev tzdata && \
|
7
|
-
curl -L https://get.rvm.io | bash -s stable
|
8
|
-
|
9
|
-
ENV PATH /usr/local/rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
10
|
-
|
data/docker/stackify-ruby-test
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
ARG from_version
|
2
|
-
|
3
|
-
FROM stackify-ruby-${from_version}:latest
|
4
|
-
|
5
|
-
ARG version
|
6
|
-
|
7
|
-
RUN mkdir -p /usr/local/stackify/stackify-ruby-apm/log
|
8
|
-
RUN mkdir /build
|
9
|
-
COPY . /build/
|
10
|
-
|
11
|
-
RUN /bin/bash --login -c "\
|
12
|
-
echo $version && \
|
13
|
-
rvm use ruby-$version && \
|
14
|
-
ruby -v && \
|
15
|
-
gem install bundler -v '~> 1.16' && \
|
16
|
-
gem install tzinfo-data && \
|
17
|
-
cd build && \
|
18
|
-
ls -l && \
|
19
|
-
bundle install \
|
20
|
-
"
|
21
|
-
CMD /bin/bash --login -c "\
|
22
|
-
echo $version && \
|
23
|
-
echo ${version} && \
|
24
|
-
rvm use ruby-$version && \
|
25
|
-
ruby -v && \
|
26
|
-
cd build && \
|
27
|
-
bundle exec rspec spec/ --format documentation --fail-fast \
|
28
|
-
"
|
data/run-test-docker.sh
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
|
3
|
-
function startContainers() {
|
4
|
-
echo "Starting up containers..."
|
5
|
-
docker-compose up -d -V --remove-orphans --force-recreate
|
6
|
-
}
|
7
|
-
|
8
|
-
function stopContainers() {
|
9
|
-
echo "Stopping Containers..."
|
10
|
-
docker-compose down --remove-orphans
|
11
|
-
|
12
|
-
# delete volumes
|
13
|
-
echo "Delete Volumes..."
|
14
|
-
docker volume rm stackify-ruby-apm_postgresdata &> /dev/null
|
15
|
-
docker volume rm stackify-ruby-apm_mysqldata &> /dev/null
|
16
|
-
docker volume rm stackify-ruby-apm_mongodata &> /dev/null
|
17
|
-
docker volume rm stackify-ruby-apm_mongodata_config &> /dev/null
|
18
|
-
echo "Deleted Volumes"
|
19
|
-
}
|
20
|
-
|
21
|
-
if [[ "$(docker images -q stackify-ruby-rvm:latest 2> /dev/null)" == "" ]]; then
|
22
|
-
docker build --file docker/stackify-ruby-rvm . -t stackify-ruby-rvm:latest
|
23
|
-
fi
|
24
|
-
|
25
|
-
RUBY_VERSIONS=('2.0.0-p648' '2.1.10' '2.2.10' '2.3.7' '2.4.0' '2.5.1' '2.6.0-preview3')
|
26
|
-
|
27
|
-
for ver in "${RUBY_VERSIONS[@]}"
|
28
|
-
do
|
29
|
-
|
30
|
-
echo "Testing Ruby Version ${RUBY_VERSIONS}"
|
31
|
-
|
32
|
-
if [[ "$(docker images -q stackify-ruby-$ver:latest 2> /dev/null)" == "" ]]; then
|
33
|
-
docker build --build-arg version=${ver} --file docker/stackify-ruby . -t stackify-ruby-$ver:latest
|
34
|
-
fi
|
35
|
-
|
36
|
-
docker build --build-arg from_version=${ver} --build-arg version=${ver} --file docker/stackify-ruby-test . -t stackify-ruby-$ver-test:latest
|
37
|
-
|
38
|
-
startContainers
|
39
|
-
docker run -e version=${ver} --network="host" --name "stackify-ruby-${ver}-test" stackify-ruby-$ver-test:latest
|
40
|
-
stopContainers
|
41
|
-
|
42
|
-
docker rm "stackify-ruby-${ver}-test"
|
43
|
-
|
44
|
-
done
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
data/run-test.sh
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
|
3
|
-
function startContainers() {
|
4
|
-
echo "Starting up containers..."
|
5
|
-
docker-compose up -d -V --remove-orphans --force-recreate
|
6
|
-
}
|
7
|
-
|
8
|
-
function waitForContainers() {
|
9
|
-
echo 'Waiting for container startup 10s...'
|
10
|
-
sleep 5
|
11
|
-
echo 'Waiting for container startup 5s...'
|
12
|
-
sleep 5
|
13
|
-
}
|
14
|
-
|
15
|
-
function stopContainers() {
|
16
|
-
echo "Stopping Containers..."
|
17
|
-
docker-compose down --remove-orphans
|
18
|
-
|
19
|
-
# delete volumes
|
20
|
-
echo "Delete Volumes..."
|
21
|
-
docker volume rm stackify-ruby-apm_postgresdata &> /dev/null
|
22
|
-
docker volume rm stackify-ruby-apm_mysqldata &> /dev/null
|
23
|
-
docker volume rm stackify-ruby-apm_mongodata &> /dev/null
|
24
|
-
docker volume rm stackify-ruby-apm_mongodata_config &> /dev/null
|
25
|
-
echo "Deleted Volumes"
|
26
|
-
}
|
27
|
-
|
28
|
-
function rspec_on_multiple_versions(){
|
29
|
-
# test against multiple ruby versions
|
30
|
-
set -e
|
31
|
-
|
32
|
-
RUBY_VERSIONS=('2.0.0-p648' '2.1.10' '2.2.10' '2.3.7' '2.4.0' '2.5.1' '2.6.0-preview3')
|
33
|
-
|
34
|
-
for ver in "${RUBY_VERSIONS[@]}"
|
35
|
-
do
|
36
|
-
if ! rbenv versions | grep -w $ver; then
|
37
|
-
# remove stale rbenv shim before rehashing
|
38
|
-
SHIM=/home/$USER/.rbenv/shims/.rbenv-shim
|
39
|
-
if [ -f $SHIM ]; then
|
40
|
-
rm $SHIM
|
41
|
-
fi
|
42
|
-
|
43
|
-
rbenv install --verbose $ver
|
44
|
-
rbenv rehash
|
45
|
-
rbenv local $ver
|
46
|
-
gem install bundler -v '~> 1.16'
|
47
|
-
fi
|
48
|
-
|
49
|
-
rbenv local $ver
|
50
|
-
rbenv exec bundle update
|
51
|
-
|
52
|
-
echo "====================================================="
|
53
|
-
echo "$ver: Start Test"
|
54
|
-
echo "====================================================="
|
55
|
-
|
56
|
-
if ! bundle exec rspec spec/ --format documentation --fail-fast; then
|
57
|
-
echo ">>>Unit Test Error on Version: $ver<<<"
|
58
|
-
stopContainers
|
59
|
-
exit 1
|
60
|
-
else
|
61
|
-
echo "====================================================="
|
62
|
-
echo "$ver: End Test"
|
63
|
-
echo "====================================================="
|
64
|
-
fi
|
65
|
-
done
|
66
|
-
}
|
67
|
-
|
68
|
-
|
69
|
-
startContainers
|
70
|
-
waitForContainers
|
71
|
-
|
72
|
-
rspec_on_multiple_versions
|
73
|
-
stopContainers
|