stackify-ruby-apm 1.10.2 → 1.13.2

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/stackify_apm/agent.rb +0 -2
  3. data/lib/stackify_apm/config.rb +36 -5
  4. data/lib/stackify_apm/context.rb +4 -1
  5. data/lib/stackify_apm/context/prefix.rb +30 -0
  6. data/lib/stackify_apm/context/request/headers.rb +30 -0
  7. data/lib/stackify_apm/context_builder.rb +1 -0
  8. data/lib/stackify_apm/helper/database_helper.rb +40 -1
  9. data/lib/stackify_apm/instrumenter_helper.rb +90 -4
  10. data/lib/stackify_apm/logger/logger_high_version.rb +5 -1
  11. data/lib/stackify_apm/logger/logger_lower_version.rb +5 -1
  12. data/lib/stackify_apm/middleware.rb +21 -3
  13. data/lib/stackify_apm/normalizers/active_record.rb +16 -8
  14. data/lib/stackify_apm/root_info.rb +7 -1
  15. data/lib/stackify_apm/serializers/transactions.rb +5 -0
  16. data/lib/stackify_apm/span/context.rb +42 -1
  17. data/lib/stackify_apm/spies.rb +4 -2
  18. data/lib/stackify_apm/spies/action_dispatch.rb +6 -1
  19. data/lib/stackify_apm/spies/curb.rb +41 -20
  20. data/lib/stackify_apm/spies/curb/easy.rb +220 -92
  21. data/lib/stackify_apm/spies/curb/multi.rb +26 -12
  22. data/lib/stackify_apm/spies/custom_instrumenter.rb +25 -4
  23. data/lib/stackify_apm/spies/delayed_job.rb +49 -0
  24. data/lib/stackify_apm/spies/dynamo_db.rb +50 -0
  25. data/lib/stackify_apm/spies/faraday.rb +87 -0
  26. data/lib/stackify_apm/spies/httparty.rb +45 -24
  27. data/lib/stackify_apm/spies/httpclient.rb +41 -20
  28. data/lib/stackify_apm/spies/httprb.rb +39 -18
  29. data/lib/stackify_apm/spies/log4r.rb +60 -0
  30. data/lib/stackify_apm/spies/logger.rb +117 -0
  31. data/lib/stackify_apm/spies/logging.rb +66 -0
  32. data/lib/stackify_apm/spies/net_http.rb +38 -20
  33. data/lib/stackify_apm/spies/redis.rb +36 -30
  34. data/lib/stackify_apm/spies/sequel.rb +28 -11
  35. data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +27 -25
  36. data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +27 -24
  37. data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +18 -8
  38. data/lib/stackify_apm/spies/stackify_logger.rb +28 -16
  39. data/lib/stackify_apm/spies/sucker_punch.rb +39 -0
  40. data/lib/stackify_apm/spies/yell.rb +65 -0
  41. data/lib/stackify_apm/util.rb +10 -9
  42. data/lib/stackify_apm/version.rb +1 -1
  43. data/stackify-ruby-apm.gemspec +1 -0
  44. metadata +26 -2
@@ -14,19 +14,33 @@ module StackifyRubyAPM
14
14
  def self.http(urls_with_config, _multi_options = {}, &blk)
15
15
  return http_without_apm(urls_with_config, _multi_options = {}, &blk) unless StackifyRubyAPM.current_transaction
16
16
  http_without_apm(urls_with_config, _multi_options = {}) do |c, code, method|
17
- status_code = code.zero? ? 404 : code
18
- method = method.upcase
19
- uri = c.url.to_s.strip
20
- name = "#{method} #{uri}"
21
- type = "ext.Curb.Multi.#{method}"
17
+ begin
18
+ status_code = code.zero? ? 404 : code
19
+ method = method.upcase
20
+ uri = c.url.to_s.strip
21
+ name = "#{method} #{uri}"
22
+ type = "ext.Curb.Multi.#{method}"
23
+
24
+ ctx = Span::Context.new(
25
+ CATEGORY: 'Web External',
26
+ SUBCATEGORY: 'Execute',
27
+ URL: uri,
28
+ STATUS: status_code,
29
+ METHOD: method
30
+ )
31
+
32
+ if StackifyRubyAPM.agent.config.prefix_enabled
33
+ ctx.update_request_body(c.post_body || "")
34
+ ctx.update_request_headers(c.headers || Hash.new)
35
+ ctx.update_response_body(c.body || "")
36
+ ctx.update_response_headers(c.proxy_headers || Hash.new)
37
+ end
38
+ rescue Exception => e
39
+ StackifyRubyAPM.agent.error "[CurbMultiSpy] Error: creating span context."
40
+ StackifyRubyAPM.agent.error "[CurbMultiSpy] #{e.inspect}"
41
+ return blk.call(c, code, method)
42
+ end
22
43
 
23
- ctx = Span::Context.new(
24
- CATEGORY: 'Web External',
25
- SUBCATEGORY: 'Execute',
26
- URL: uri,
27
- STATUS: status_code,
28
- METHOD: method
29
- )
30
44
  # Creates new span from HTTP result
31
45
  StackifyRubyAPM.span name, type, context: ctx do
32
46
  blk.call(c, code, method)
@@ -24,16 +24,37 @@ module StackifyRubyAPM
24
24
  return unless !to_instrument.nil? && !to_instrument.empty? && defined?(to_instrument['instrumentation']) && (to_instrument['instrumentation'].count > 0)
25
25
 
26
26
  to_instrument['instrumentation'].each do |custom_spy|
27
- current_class = custom_spy['class']
27
+ current_class = defined?(custom_spy['class']) ? custom_spy['class'] : nil
28
+ current_module = defined?(custom_spy['module']) ? custom_spy['module'] : nil
28
29
  current_method = custom_spy['method']
29
30
  tracked_func = custom_spy['trackedFunction']
30
31
  tracked_func_name = defined?(custom_spy['trackedFunctionName']) ? custom_spy['trackedFunctionName'] : ''
31
32
  transaction = defined?(custom_spy['transaction']) ? custom_spy['transaction'] : nil
32
33
  file_path = defined?(custom_spy['file_path']) ? custom_spy['file_path'] : nil
33
34
 
34
- tracked_function_tpl = tracked_func_name.nil? ? '{{ClassName}}.{{MethodName}}' : tracked_func_name
35
- tracked_function_name = tracked_function_tpl.sub '{{ClassName}}', current_class
36
- tracked_function_name = tracked_function_name.sub '{{MethodName}}', current_method
35
+ if current_class
36
+ tracked_function_tpl = tracked_func_name.nil? ? '{{ClassName}}.{{MethodName}}' : tracked_func_name
37
+ tracked_function_name = tracked_function_tpl.to_s.sub '{{ClassName}}', current_class
38
+ tracked_function_name = tracked_function_name.to_s.sub '{{MethodName}}', current_method
39
+ elsif current_module
40
+ tracked_function_tpl = tracked_func_name.nil? ? '{{ModuleName}}.{{MethodName}}' : tracked_func_name
41
+ tracked_function_name = tracked_function_tpl.to_s.sub '{{ModuleName}}', current_module
42
+ tracked_function_name = tracked_function_name.to_s.sub '{{MethodName}}', current_method
43
+ end
44
+
45
+ begin
46
+ if current_module
47
+ if file_path.nil?
48
+ config.logger.send(:info, "[StackifyRubyAPM] Error: Missing file_path in module which is required in custom instrumentation.")
49
+ else
50
+ require file_path
51
+ StackifyRubyAPM::InstrumenterHelper.patched_module(tracked_func, current_module, file_path,
52
+ current_method: current_method, tracked_function_name: tracked_function_name, is_transaction: transaction)
53
+ end
54
+ end
55
+ rescue => e
56
+ throw e
57
+ end
37
58
 
38
59
  if !class_exists?(current_class) && !file_path.nil?
39
60
  begin
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patch for the delayed_job class for running async tasks.
4
+ #
5
+
6
+ module StackifyRubyAPM
7
+ # @api private
8
+ module Spies
9
+ # @api private
10
+ class DelayedJobSpy
11
+ def install
12
+ Delayed::Backend::Base.class_eval do
13
+ alias_method 'invoke_job_without_apm', 'invoke_job'
14
+
15
+ def invoke_job(*args, &block)
16
+ ret = nil
17
+ begin
18
+ name = nil
19
+ if payload_object.is_a?(::Delayed::PerformableMethod)
20
+ object = payload_object.object
21
+ klass = object.is_a?(Class) ? object : object.class
22
+ class_name = klass.name
23
+ separator = payload_object.object.is_a?(Class) ? '.' : '#'
24
+ method_name = payload_object.method_name
25
+ name = "#{class_name}#{separator}#{method_name}"
26
+ else
27
+ name = payload_object.class.name
28
+ end
29
+ ctx = StackifyRubyAPM::Context.new
30
+ ctx.category = 'Delayed::Job'
31
+ transaction = StackifyRubyAPM.transaction name, 'TASK', context: ctx
32
+ ret = invoke_job_without_apm(*args, &block)
33
+ rescue StackifyRubyAPM::InternalError
34
+ raise # Don't report StackifyRubyAPM errors
35
+ rescue StandardError => e
36
+ StackifyRubyAPM.report e
37
+ raise e
38
+ ensure
39
+ transaction.submit()
40
+ end
41
+ ret
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ register 'Delayed::Backend::Base', 'delayed/backend/base', DelayedJobSpy.new
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ # Monkey patch for the Aws::DynamoDB::Client class
2
+
3
+ module StackifyRubyAPM
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class DynamoDBSpy
8
+ TYPE = 'db.dynamo.aws'.freeze
9
+ # rubocop:disable Metrics/CyclomaticComplexity
10
+ def install
11
+ Aws::DynamoDB::Client.class_eval do
12
+ # Alias all available operations
13
+ api.operation_names.each do |operation_name|
14
+ alias_method "#{operation_name}_without_apm", "#{operation_name}"
15
+
16
+ define_method(operation_name) do |params = {}, options = {}|
17
+ original_method = method("#{operation_name}_without_apm")
18
+ return original_method.call(params, options) unless StackifyRubyAPM.current_transaction
19
+
20
+ result = nil
21
+
22
+ begin
23
+ name = operation_name
24
+ ctx = Span::Context.new(
25
+ CATEGORY: 'Database',
26
+ SUBCATEGORY: 'DynamoDB',
27
+ ACTION: operation_name,
28
+ )
29
+ rescue Exception => e
30
+ StackifyRubyAPM.agent.error "[DynamoDBSpy] Error: creating span context."
31
+ StackifyRubyAPM.agent.error "[DynamoDBSpy] #{e.inspect}"
32
+ return original_method.call(params, options)
33
+ end
34
+
35
+ StackifyRubyAPM.span name, TYPE, context: ctx do
36
+ result = original_method.call(params, options)
37
+ return result
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ # rubocop:enable Metrics/MethodLength
44
+ end
45
+
46
+ # Registers Aws::DynamoDB::Client spy, go to: /stackify_apm/spies.rb
47
+ #
48
+ register 'Aws::DynamoDB::Client', 'aws-sdk-dynamodb', DynamoDBSpy.new
49
+ end
50
+ end
@@ -0,0 +1,87 @@
1
+ # Monkey patch for the Faraday class
2
+
3
+ module StackifyRubyAPM
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class FaradaySpy
8
+ def install
9
+ Faraday::Connection.class_eval do
10
+ alias_method 'run_request_without_apm', 'run_request'
11
+
12
+ def run_request(method, url, body, headers, &block)
13
+ result = nil
14
+ return run_request_without_apm(method, url, body, headers, &block) unless StackifyRubyAPM.current_transaction
15
+
16
+ begin
17
+ uri = URI(build_url(url))
18
+
19
+ # url is not available yet if it is set inside block
20
+ # we need to build temporary request as of now
21
+ # NOTE: this could have a side effect doing yeild twice
22
+ unless uri.host
23
+ tmp_request = build_request(method) do |req|
24
+ yield(req) if block_given?
25
+ end
26
+ uri = URI(tmp_request.path)
27
+ end
28
+
29
+ host = uri.host
30
+ method_upcase = method.to_s.upcase
31
+ name = "#{method_upcase} #{host}"
32
+ type = "ext.faraday.#{method_upcase}"
33
+
34
+ # Builds span context
35
+ #
36
+ ctx = Span::Context.new(
37
+ CATEGORY: 'Web External',
38
+ SUBCATEGORY: 'Execute',
39
+ URL: uri,
40
+ STATUS: '',
41
+ METHOD: method_upcase
42
+ )
43
+ rescue Exception => e
44
+ StackifyRubyAPM.agent.error "[FaradaySpy] Error: creating span context."
45
+ StackifyRubyAPM.agent.error "[FaradaySpy] #{e.inspect}"
46
+ return run_request_without_apm(method, url, body, headers, &block)
47
+ end
48
+
49
+ # Creates new span from HTTP result
50
+ #
51
+ StackifyRubyAPM.span name, type, context: ctx do
52
+ # Submits HTTP request
53
+ #
54
+ result = run_request_without_apm(method, url, body, headers) do |req|
55
+ yield req if block_given?
56
+
57
+ if StackifyRubyAPM.agent.config.prefix_enabled
58
+ ctx.update_request_body(req.body || body || "")
59
+ ctx.update_request_headers(req.headers || headers || Hash.new)
60
+ end
61
+ end
62
+
63
+ begin
64
+ status_code = result.status.to_s
65
+ ctx.update_status(status_code)
66
+
67
+ if StackifyRubyAPM.agent.config.prefix_enabled
68
+ ctx.update_response_body(result.body || "")
69
+ ctx.update_response_headers(result.headers || Hash.new)
70
+ end
71
+ rescue Exception => e
72
+ StackifyRubyAPM.agent.error '[FaradaySpy] Error: getting status code or updating request/response context.'
73
+ StackifyRubyAPM.agent.error "[FaradaySpy] #{e.inspect}"
74
+ end
75
+ return result
76
+ end
77
+ end
78
+ end
79
+ end
80
+ # rubocop:enable Metrics/MethodLength
81
+ end
82
+
83
+ # Registers Faraday spy, go to: /stackify_apm/spies.rb
84
+ #
85
+ register 'Faraday', 'faraday', FaradaySpy.new
86
+ end
87
+ end
@@ -21,34 +21,55 @@ module StackifyRubyAPM
21
21
  def perform_request(http_method, path, options, &block)
22
22
  req = nil
23
23
  return perform_request_without_apm(http_method, path, options, &block) unless StackifyRubyAPM.current_transaction
24
- # Data configuration
25
- #
26
- method = http_method.to_s.gsub('Net::HTTP::', '').upcase
27
- uri = path.strip
28
- name = "#{method} #{uri}"
29
- type = "ext.HTTParty.#{method}"
30
- # Submits HTTP request
31
- #
32
- # req = perform_request_without_apm(http_method, path, options, &block)
33
- # Builds span context
34
- #
35
- # status_code = req.code
36
-
37
- ctx = Span::Context.new(
38
- CATEGORY: 'Web External',
39
- SUBCATEGORY: 'Execute',
40
- URL: uri,
41
- STATUS: '',
42
- METHOD: method
43
- )
24
+
25
+ begin
26
+ # Data configuration
27
+ #
28
+ method = http_method.to_s.gsub('Net::HTTP::', '').upcase
29
+ uri = path.strip
30
+ name = "#{method} #{uri}"
31
+ type = "ext.HTTParty.#{method}"
32
+ # Submits HTTP request
33
+ #
34
+ # req = perform_request_without_apm(http_method, path, options, &block)
35
+ # Builds span context
36
+ #
37
+ # status_code = req.code
38
+
39
+ ctx = Span::Context.new(
40
+ CATEGORY: 'Web External',
41
+ SUBCATEGORY: 'Execute',
42
+ URL: uri,
43
+ STATUS: '',
44
+ METHOD: method
45
+ )
46
+ rescue Exception => e
47
+ StackifyRubyAPM.agent.error "[HTTPartySpy] Error: creating span context."
48
+ StackifyRubyAPM.agent.error "[HTTPartySpy] #{e.inspect}"
49
+ return perform_request_without_apm(http_method, path, options, &block)
50
+ end
51
+
44
52
  # Creates new span from HTTP result
45
53
  #
46
54
  # class_info = { 'classname' => 'httparty', 'hostname' => URI.parse(uri).host }
47
55
  StackifyRubyAPM.span name, type, context: ctx do
48
- req = perform_request_without_apm(http_method, path, options, &block)
49
- status_code = req.code
50
- ctx.update_status(status_code)
51
- req
56
+ res = perform_request_without_apm(http_method, path, options, &block)
57
+
58
+ begin
59
+ status_code = res.code
60
+ ctx.update_status(status_code)
61
+
62
+ if StackifyRubyAPM.agent.config.prefix_enabled
63
+ ctx.update_request_body(options[:body] || "")
64
+ ctx.update_request_headers(options[:headers] || Hash.new)
65
+ ctx.update_response_body(res.body || "")
66
+ ctx.update_response_headers(res.each_header || Hash.new)
67
+ end
68
+ rescue Exception => e
69
+ StackifyRubyAPM.agent.error '[HTTPartySpy] Error: getting status code or updating request/response context.'
70
+ StackifyRubyAPM.agent.error "[HTTPartySpy] #{e.inspect}"
71
+ end
72
+ res
52
73
  end
53
74
  end
54
75
  end
@@ -16,31 +16,52 @@ module StackifyRubyAPM
16
16
  req = nil
17
17
  return request_without_apm(method, uri, *args, &block) unless StackifyRubyAPM.current_transaction
18
18
 
19
- # Data configuration
20
- #
21
- method = method.upcase
22
- uri = uri.strip
23
- name = "#{method} #{uri}"
24
- type = "ext.httpclient.#{method}"
25
-
26
- # Submits HTTP request
27
- #
28
- req = request_without_apm(method, uri, *args, &block)
19
+ begin
20
+ # Data configuration
21
+ #
22
+ method = method.upcase
23
+ uri = uri.strip
24
+ name = "#{method} #{uri}"
25
+ type = "ext.httpclient.#{method}"
29
26
 
30
- # Builds span context
31
- #
32
- ctx = Span::Context.new(
33
- CATEGORY: 'Web External',
34
- SUBCATEGORY: 'Execute',
35
- URL: uri,
36
- STATUS: req.status_code,
37
- METHOD: method
38
- )
27
+ # Builds span context
28
+ #
29
+ ctx = Span::Context.new(
30
+ CATEGORY: 'Web External',
31
+ SUBCATEGORY: 'Execute',
32
+ URL: uri,
33
+ STATUS: '',
34
+ METHOD: method
35
+ )
36
+ rescue Exception => e
37
+ StackifyRubyAPM.agent.error "[HTTPClientSpy] Error: creating span context."
38
+ StackifyRubyAPM.agent.error "[HTTPClientSpy] #{e.inspect}"
39
+ return request_without_apm(method, uri, *args, &block)
40
+ end
39
41
 
40
42
  # Creates new span from HTTP result
41
43
  #
42
44
  StackifyRubyAPM.span name, type, context: ctx do
43
- req
45
+ # Submits HTTP request
46
+ #
47
+ res = request_without_apm(method, uri, *args, &block)
48
+
49
+ begin
50
+ ctx.update_status(res.status_code)
51
+
52
+ if StackifyRubyAPM.agent.config.prefix_enabled
53
+ options = args && args[0] || Hash.new
54
+ ctx.update_request_body(options[:body] || "")
55
+ ctx.update_request_headers(options[:header] || Hash.new)
56
+ ctx.update_response_body(res.body || "")
57
+ ctx.update_response_headers(res.headers || Hash.new)
58
+ end
59
+ rescue Exception => e
60
+ StackifyRubyAPM.agent.error '[HTTPClientSpy] Error: getting status code or updating request/response context.'
61
+ StackifyRubyAPM.agent.error "[HTTPClientSpy] #{e.inspect}"
62
+ end
63
+
64
+ res
44
65
  end
45
66
  end
46
67
  end
@@ -10,28 +10,49 @@ module StackifyRubyAPM
10
10
  class HTTPRbSpy
11
11
  def install
12
12
  HTTP::Client.class_eval do
13
- alias_method 'request_without_apm', 'request'
13
+ alias_method 'perform_without_apm', 'perform'
14
14
 
15
15
  # Make HTTP request
16
- def request(verb, uri, _opts = {})
17
- return request_without_apm(verb, uri, _opts = {}) unless StackifyRubyAPM.current_transaction
18
-
19
- method = verb.upcase
20
- uri = uri.strip
21
- name = "#{method} #{uri}"
22
- type = "ext.httprb.#{method}"
23
-
24
- req = request_without_apm(verb, uri, _opts = {})
25
- ctx = Span::Context.new(
26
- CATEGORY: 'Web External',
27
- SUBCATEGORY: 'Execute',
28
- URL: uri,
29
- STATUS: req.code,
30
- METHOD: method
31
- )
16
+ def perform(req, options)
17
+ return perform_without_apm(req, options) unless StackifyRubyAPM.current_transaction
18
+
19
+ begin
20
+ method = req.verb.upcase
21
+ uri = req.uri.to_s.strip
22
+ name = "#{method} #{uri}"
23
+ type = "ext.httprb.#{method}"
24
+
25
+ ctx = Span::Context.new(
26
+ CATEGORY: 'Web External',
27
+ SUBCATEGORY: 'Execute',
28
+ URL: uri,
29
+ STATUS: '',
30
+ METHOD: method
31
+ )
32
+ rescue Exception => e
33
+ StackifyRubyAPM.agent.error "[HTTPRbSpy] Error: creating span context."
34
+ StackifyRubyAPM.agent.error "[HTTPRbSpy] #{e.inspect}"
35
+ return perform_without_apm(req, options)
36
+ end
32
37
 
33
38
  StackifyRubyAPM.span name, type, context: ctx do
34
- req
39
+ res = perform_without_apm(req, options)
40
+
41
+ begin
42
+ ctx.update_status(res.code)
43
+
44
+ if StackifyRubyAPM.agent.config.prefix_enabled
45
+ ctx.update_request_body(req.body.source || "")
46
+ ctx.update_request_headers(req.headers || Hash.new)
47
+ ctx.update_response_body(res.body || "")
48
+ ctx.update_response_headers(res.headers || Hash.new)
49
+ end
50
+ rescue Exception => e
51
+ StackifyRubyAPM.agent.error '[HTTPRbSpy] Error: getting status code or updating request/response context.'
52
+ StackifyRubyAPM.agent.error "[HTTPRbSpy] #{e.inspect}"
53
+ end
54
+
55
+ res
35
56
  end
36
57
  end
37
58
  end