stackify-ruby-apm 1.11.1 → 1.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/stackify_apm/config.rb +32 -5
  3. data/lib/stackify_apm/context.rb +4 -1
  4. data/lib/stackify_apm/context/prefix.rb +30 -0
  5. data/lib/stackify_apm/context/request/headers.rb +30 -0
  6. data/lib/stackify_apm/context_builder.rb +1 -0
  7. data/lib/stackify_apm/helper/database_helper.rb +38 -0
  8. data/lib/stackify_apm/instrumenter_helper.rb +87 -0
  9. data/lib/stackify_apm/middleware.rb +21 -3
  10. data/lib/stackify_apm/normalizers/active_record.rb +16 -8
  11. data/lib/stackify_apm/root_info.rb +6 -0
  12. data/lib/stackify_apm/serializers/transactions.rb +5 -0
  13. data/lib/stackify_apm/span/context.rb +39 -1
  14. data/lib/stackify_apm/spies.rb +4 -2
  15. data/lib/stackify_apm/spies/action_dispatch.rb +6 -1
  16. data/lib/stackify_apm/spies/curb.rb +41 -20
  17. data/lib/stackify_apm/spies/curb/easy.rb +220 -92
  18. data/lib/stackify_apm/spies/curb/multi.rb +26 -12
  19. data/lib/stackify_apm/spies/custom_instrumenter.rb +25 -4
  20. data/lib/stackify_apm/spies/httparty.rb +45 -24
  21. data/lib/stackify_apm/spies/httpclient.rb +41 -20
  22. data/lib/stackify_apm/spies/httprb.rb +39 -18
  23. data/lib/stackify_apm/spies/log4r.rb +59 -0
  24. data/lib/stackify_apm/spies/logger.rb +116 -0
  25. data/lib/stackify_apm/spies/logging.rb +65 -0
  26. data/lib/stackify_apm/spies/net_http.rb +38 -20
  27. data/lib/stackify_apm/spies/redis.rb +36 -30
  28. data/lib/stackify_apm/spies/sequel.rb +28 -11
  29. data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +27 -25
  30. data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +27 -24
  31. data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +18 -8
  32. data/lib/stackify_apm/spies/stackify_logger.rb +28 -16
  33. data/lib/stackify_apm/spies/yell.rb +64 -0
  34. data/lib/stackify_apm/util.rb +10 -0
  35. data/lib/stackify_apm/version.rb +1 -1
  36. metadata +8 -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
@@ -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
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patch for the Log4r::Outputter class for logging log message.
4
+ #
5
+ module StackifyRubyAPM
6
+ module Spies
7
+ class Log4rSpy
8
+ def install
9
+ Log4r::Outputter.module_eval do
10
+ alias_method 'canonical_log_without_apm', 'canonical_log'
11
+
12
+ # Log message formatted to a logevent object
13
+ def canonical_log(logevent)
14
+ return canonical_log_without_apm(logevent) unless StackifyRubyAPM.current_transaction
15
+
16
+ begin
17
+ name = 'log4r'
18
+ type = 'ext.log4r'
19
+ log_message = ''
20
+ exception = nil
21
+ msg = logevent.data
22
+
23
+ case msg
24
+ when ::String
25
+ log_message = msg
26
+ when ::Exception
27
+ log_message = msg.message
28
+ exception = "(#{ msg.class })\n#{ msg.backtrace.join("\n") if msg.backtrace }"
29
+ else
30
+ log_message = msg.inspect
31
+ end
32
+
33
+ ctx = Span::Context.new(
34
+ CATEGORY: 'Log',
35
+ SUBCATEGORY: 'Log4r',
36
+ LEVEL: Log4r::LNAMES[logevent.level] || 'ANY',
37
+ MESSAGE: log_message
38
+ )
39
+
40
+ if exception
41
+ ctx.EXCEPTION = exception
42
+ end
43
+ rescue Exception => e
44
+ StackifyRubyAPM.agent.error "[Log4rSpy] Error: creating span context."
45
+ StackifyRubyAPM.agent.error "[Log4rSpy] #{e.inspect}"
46
+ return canonical_log_without_apm(logevent)
47
+ end
48
+
49
+ StackifyRubyAPM.span name, type, context: ctx do
50
+ canonical_log_without_apm(logevent)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ register 'Log4r::Outputter', 'log4r', Log4rSpy.new
58
+ end
59
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Monkey patch for the Logger class for logging log messages.
5
+ #
6
+ module StackifyRubyAPM
7
+ module Spies
8
+ class LoggerSpy
9
+ NAME = 'logger'
10
+ TYPE = 'core.devlog'
11
+
12
+ def install
13
+ Logger.class_eval do
14
+ alias_method 'debug_without_apm', 'debug'
15
+ alias_method 'info_without_apm', 'info'
16
+ alias_method 'warn_without_apm', 'warn'
17
+ alias_method 'error_without_apm', 'error'
18
+ alias_method 'fatal_without_apm', 'fatal'
19
+ alias_method 'unknown_without_apm', 'unknown'
20
+
21
+ def debug(progname = nil, &block)
22
+ return debug_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
23
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('DEBUG', nil, progname, &block)
24
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
25
+ debug_without_apm(progname, &block)
26
+ end
27
+ end
28
+
29
+ def info(progname = nil, &block)
30
+ return info_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
31
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('INFO', nil, progname, &block)
32
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
33
+ info_without_apm(progname, &block)
34
+ end
35
+ end
36
+
37
+ def warn(progname = nil, &block)
38
+ return warn_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
39
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('WARN', nil, progname, &block)
40
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
41
+ warn_without_apm(progname, &block)
42
+ end
43
+ end
44
+
45
+ def error(progname = nil, &block)
46
+ return error_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
47
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('ERROR', nil, progname, &block)
48
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
49
+ error_without_apm(progname, &block)
50
+ end
51
+ end
52
+
53
+ def fatal(progname = nil, &block)
54
+ return fatal_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
55
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('FATAL', nil, progname, &block)
56
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
57
+ fatal_without_apm(progname, &block)
58
+ end
59
+ end
60
+
61
+ def unknown(progname = nil, &block)
62
+ return unknown_without_apm(progname, &block) unless StackifyRubyAPM.current_transaction
63
+ ctx = StackifyRubyAPM::Spies::LoggerSpy.get_log_context('UNKNOWN', nil, progname, &block)
64
+ StackifyRubyAPM.span NAME, TYPE, context: ctx do
65
+ unknown_without_apm(progname, &block)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ # create and return logger span context
72
+ def self.get_log_context(severity, message, progname)
73
+ ctx = nil
74
+
75
+ begin
76
+ log_message = ''
77
+ exception = nil
78
+
79
+ if message.nil?
80
+ msg = progname
81
+ else
82
+ msg = message
83
+ end
84
+
85
+ case msg
86
+ when ::String
87
+ log_message = msg
88
+ when ::Exception
89
+ log_message = msg.message
90
+ exception = "(#{ msg.class })\n#{ msg.backtrace.join("\n") if msg.backtrace }"
91
+ else
92
+ log_message = msg.inspect
93
+ end
94
+
95
+ ctx = Span::Context.new(
96
+ CATEGORY: 'Log',
97
+ SUBCATEGORY: 'Logger',
98
+ LEVEL: severity || 'ANY',
99
+ MESSAGE: log_message
100
+ )
101
+
102
+ if exception
103
+ ctx.EXCEPTION = exception
104
+ end
105
+ rescue Exception => e
106
+ StackifyRubyAPM.agent.error "[LoggerSpy] Error: creating span context."
107
+ StackifyRubyAPM.agent.error "[LoggerSpy] #{e.inspect}"
108
+ end
109
+
110
+ ctx
111
+ end
112
+ end
113
+
114
+ register 'Logger', 'logger', LoggerSpy.new
115
+ end
116
+ end