stackify-ruby-apm 1.1.1 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|