stackify-ruby-apm 1.0.1 → 1.1.0

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +38 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +17 -11
  5. data/Gemfile.lock +98 -95
  6. data/Rakefile +7 -5
  7. data/docker/stackify-ruby +8 -0
  8. data/docker/stackify-ruby-rvm +10 -0
  9. data/docker/stackify-ruby-test +28 -0
  10. data/lib/{stackify → stackify_apm}/agent.rb +42 -33
  11. data/lib/{stackify → stackify_apm}/config.rb +56 -39
  12. data/lib/{stackify → stackify_apm}/context.rb +5 -6
  13. data/lib/{stackify → stackify_apm}/context/request.rb +0 -0
  14. data/lib/{stackify → stackify_apm}/context/request/socket.rb +0 -0
  15. data/lib/{stackify → stackify_apm}/context/request/url.rb +2 -6
  16. data/lib/stackify_apm/context/response.rb +33 -0
  17. data/lib/{stackify → stackify_apm}/context_builder.rb +2 -5
  18. data/lib/{stackify → stackify_apm}/error.rb +7 -6
  19. data/lib/stackify_apm/error/exception.rb +37 -0
  20. data/lib/stackify_apm/error/log.rb +24 -0
  21. data/lib/stackify_apm/error_builder.rb +61 -0
  22. data/lib/stackify_apm/helper/database_helper.rb +27 -0
  23. data/lib/{stackify → stackify_apm}/instrumenter.rb +12 -19
  24. data/lib/{stackify → stackify_apm}/internal_error.rb +0 -0
  25. data/lib/{stackify → stackify_apm}/log.rb +0 -0
  26. data/lib/{stackify → stackify_apm}/logger/log_device.rb +22 -11
  27. data/lib/{stackify → stackify_apm}/logger/logger_high_version.rb +1 -6
  28. data/lib/{stackify → stackify_apm}/logger/logger_lower_version.rb +2 -1
  29. data/lib/stackify_apm/middleware.rb +70 -0
  30. data/lib/{stackify → stackify_apm}/naively_hashable.rb +1 -3
  31. data/lib/{stackify → stackify_apm}/normalizers.rb +3 -2
  32. data/lib/{stackify → stackify_apm}/normalizers/action_controller.rb +0 -0
  33. data/lib/{stackify → stackify_apm}/normalizers/action_mailer.rb +0 -0
  34. data/lib/{stackify → stackify_apm}/normalizers/action_view.rb +0 -0
  35. data/lib/{stackify → stackify_apm}/normalizers/active_record.rb +3 -25
  36. data/lib/{stackify → stackify_apm}/railtie.rb +5 -7
  37. data/lib/{stackify → stackify_apm}/root_info.rb +2 -6
  38. data/lib/{stackify → stackify_apm}/serializers.rb +3 -2
  39. data/lib/{stackify → stackify_apm}/serializers/errors.rb +7 -10
  40. data/lib/{stackify → stackify_apm}/serializers/transactions.rb +11 -18
  41. data/lib/{stackify → stackify_apm}/span.rb +8 -9
  42. data/lib/{stackify → stackify_apm}/span/context.rb +3 -1
  43. data/lib/{stackify → stackify_apm}/spies.rb +3 -2
  44. data/lib/{stackify → stackify_apm}/spies/action_dispatch.rb +3 -4
  45. data/lib/stackify_apm/spies/curb.rb +49 -0
  46. data/lib/stackify_apm/spies/curb/easy.rb +157 -0
  47. data/lib/stackify_apm/spies/curb/multi.rb +43 -0
  48. data/lib/{stackify → stackify_apm}/spies/httpclient.rb +10 -8
  49. data/lib/{stackify → stackify_apm}/spies/httprb.rb +7 -9
  50. data/lib/{stackify → stackify_apm}/spies/mongo.rb +5 -3
  51. data/lib/{stackify → stackify_apm}/spies/net_http.rb +4 -5
  52. data/lib/{stackify → stackify_apm}/spies/redis.rb +19 -18
  53. data/lib/stackify_apm/spies/sequel.rb +65 -0
  54. data/lib/{stackify → stackify_apm}/spies/sinatra.rb +7 -10
  55. data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +201 -0
  56. data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +94 -0
  57. data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +46 -0
  58. data/lib/stackify_apm/spies/stackify_logger.rb +60 -0
  59. data/lib/{stackify → stackify_apm}/spies/tilt.rb +3 -3
  60. data/lib/stackify_apm/stacktrace.rb +18 -0
  61. data/lib/stackify_apm/stacktrace/frame.rb +47 -0
  62. data/lib/{stackify → stackify_apm}/stacktrace_builder.rb +10 -11
  63. data/lib/{stackify → stackify_apm}/subscriber.rb +20 -14
  64. data/lib/{stackify → stackify_apm}/trace_logger.rb +10 -16
  65. data/lib/stackify_apm/transaction.rb +127 -0
  66. data/lib/{stackify → stackify_apm}/util.rb +3 -1
  67. data/lib/{stackify → stackify_apm}/util/dig.rb +1 -1
  68. data/lib/{stackify → stackify_apm}/util/inflector.rb +0 -0
  69. data/lib/{stackify → stackify_apm}/util/inspector.rb +1 -3
  70. data/lib/stackify_apm/util/lru_cache.rb +49 -0
  71. data/lib/stackify_apm/util/trace_log_watcher.rb +37 -0
  72. data/lib/stackify_apm/version.rb +6 -0
  73. data/lib/{stackify → stackify_apm}/worker.rb +8 -7
  74. data/lib/stackify_ruby_apm.rb +18 -15
  75. data/run-test-docker.sh +50 -0
  76. data/run-test.sh +1 -3
  77. data/stackify-ruby-apm.gemspec +14 -11
  78. metadata +86 -59
  79. data/lib/stackify/context/response.rb +0 -37
  80. data/lib/stackify/error/exception.rb +0 -36
  81. data/lib/stackify/error/log.rb +0 -25
  82. data/lib/stackify/error_builder.rb +0 -65
  83. data/lib/stackify/middleware.rb +0 -74
  84. data/lib/stackify/spies/sinatra_activerecord/mysql_adapter.rb +0 -177
  85. data/lib/stackify/spies/sinatra_activerecord/postgresql_adapter.rb +0 -96
  86. data/lib/stackify/spies/sinatra_activerecord/sqlite_adapter.rb +0 -48
  87. data/lib/stackify/stacktrace.rb +0 -19
  88. data/lib/stackify/stacktrace/frame.rb +0 -50
  89. data/lib/stackify/transaction.rb +0 -132
  90. data/lib/stackify/util/lru_cache.rb +0 -49
  91. data/lib/stackify/version.rb +0 -4
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  #
3
4
  # Monkey patch for the Tilt::Template class on template rendering and span creation.
4
5
  #
@@ -8,12 +9,11 @@ module StackifyRubyAPM
8
9
  module Spies
9
10
  # @api private
10
11
  class TiltSpy
11
-
12
12
  TYPE = 'template.tilt'.freeze
13
13
 
14
14
  def install
15
15
  ::Tilt::Template.class_eval do
16
- alias render_without_apm render
16
+ alias_method 'render_without_apm', 'render'
17
17
 
18
18
  def render(*args, &block)
19
19
  name = options[:__stackify_apm_template_name] || 'Unknown template'
@@ -28,7 +28,7 @@ module StackifyRubyAPM
28
28
  end
29
29
  end
30
30
 
31
- # Registers Tilt spy, go to: /stackify/spies.rb
31
+ # Registers Tilt spy, go to: /stackify_apm/spies.rb
32
32
  #
33
33
  register 'Tilt::Template', 'tilt/template', TiltSpy.new
34
34
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module starts creation for frames
4
+
5
+ module StackifyRubyAPM
6
+ # @api private
7
+ class Stacktrace
8
+ attr_accessor :frames
9
+
10
+ def length
11
+ frames.length
12
+ end
13
+
14
+ def to_a
15
+ frames.map(&:to_h)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StackifyRubyAPM
4
+ class Stacktrace
5
+ # @api private
6
+ class Frame
7
+ include NaivelyHashable
8
+
9
+ attr_accessor(
10
+ :abs_path,
11
+ :filename,
12
+ :function,
13
+ :vars,
14
+ :pre_context,
15
+ :context_line,
16
+ :post_context,
17
+ :library_frame,
18
+ :lineno,
19
+ :module,
20
+ :Method,
21
+ :colno
22
+ )
23
+
24
+ def build_context(context_line_count)
25
+ return unless abs_path && context_line_count.positive?
26
+
27
+ padding = (context_line_count - 1) / 2
28
+ from = lineno - padding - 1
29
+ from = 0 if from.negative?
30
+ to = lineno + padding - 1
31
+ file_lines = read_lines(abs_path, from..to)
32
+
33
+ self.context_line = file_lines[padding]
34
+ self.pre_context = file_lines.first(padding)
35
+ self.post_context = file_lines.last(padding)
36
+ end
37
+
38
+ private
39
+
40
+ def read_lines(path, range)
41
+ File.readlines(path)[range]
42
+ rescue Errno::ENOENT
43
+ []
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
- #
2
+
3
3
  # This module gathers the lists of frames and build them into hash format
4
- #
5
- require 'stackify/stacktrace/frame'
6
- require 'stackify/util/lru_cache'
4
+
5
+ require 'stackify_apm/stacktrace/frame'
6
+ require 'stackify_apm/util/lru_cache'
7
7
 
8
8
  module StackifyRubyAPM
9
9
  # @api private
@@ -31,7 +31,6 @@ module StackifyRubyAPM
31
31
 
32
32
  private
33
33
 
34
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
34
  def build_frame(cache, keys)
36
35
  line, type = keys
37
36
  abs_path, lineno, function, _module_name = parse_line(line)
@@ -39,11 +38,11 @@ module StackifyRubyAPM
39
38
  library_frame = library_frame?(config, abs_path)
40
39
 
41
40
  frame = Stacktrace::Frame.new
42
- #frame.abs_path = abs_path
43
- #frame.filename = strip_load_path(abs_path)
44
- frame.Method = "#{function} (#{current_filename}:#{lineno})"
45
- #frame.lineno = lineno.to_i
46
- #frame.library_frame = library_frame?(config, abs_path)
41
+ # frame.abs_path = abs_path
42
+ # frame.filename = strip_load_path(abs_path)
43
+ frame.Method = "#{function} (#{current_filename}:#{lineno})"
44
+ # frame.lineno = lineno.to_i
45
+ # frame.library_frame = library_frame?(config, abs_path)
47
46
 
48
47
  line_count =
49
48
  context_lines_for(config, type, library_frame)
@@ -51,7 +50,6 @@ module StackifyRubyAPM
51
50
 
52
51
  cache[[line, type]] = frame
53
52
  end
54
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
55
53
 
56
54
  def parse_line(line)
57
55
  ruby_match = line.match(RUBY_FORMAT)
@@ -73,6 +71,7 @@ module StackifyRubyAPM
73
71
 
74
72
  if abs_path.start_with?(config.root_path)
75
73
  return true if abs_path.start_with?(config.root_path + '/vendor')
74
+
76
75
  return false
77
76
  end
78
77
 
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
- #
2
+
3
3
  # The Subscriber registers the existing events.
4
4
  # This will trigger (together with the Middleware) once there is a web request.
5
- #
6
5
 
7
6
  require 'active_support/notifications'
8
- require 'stackify/normalizers'
7
+ require 'stackify_apm/normalizers'
9
8
 
10
9
  module StackifyRubyAPM
11
10
  # @api private
@@ -36,8 +35,11 @@ module StackifyRubyAPM
36
35
  Notification = Struct.new(:id, :span)
37
36
 
38
37
  # [call] Called when the rails version is 3.x
38
+ # rubocop:disable Metrics/CyclomaticComplexity
39
+ # rubocop:disable Metrics/PerceivedComplexity
39
40
  def call(name, started, finished, id, payload)
40
41
  return unless (transaction = @agent.current_transaction)
42
+
41
43
  debug '[Subscriber] call():'
42
44
  debug id
43
45
  debug name
@@ -47,34 +49,38 @@ module StackifyRubyAPM
47
49
 
48
50
  if started
49
51
  span =
50
- if normalized == :skip
51
- nil
52
- else
53
- name, type, context = normalized
54
- @agent.span(name, type, context: context)
55
- end
52
+ if normalized == :skip
53
+ nil
54
+ else
55
+ name, type, context = normalized
56
+ @agent.span(name, type, context: context)
57
+ end
56
58
  transaction.notifications << Notification.new(id, span)
57
59
  end
60
+ # rubocop:enable Metrics/CyclomaticComplexity
61
+ # rubocop:enable Metrics/PerceivedComplexity
62
+ # rubocop:disable Style/GuardClause
58
63
 
59
64
  if finished
60
65
  while (notification = transaction.notifications.pop)
61
66
  next unless notification.id == id
62
-
63
67
  if (span = notification.span)
64
68
  span.done
65
69
  end
66
70
  return
67
71
  end
68
72
  end
73
+ # rubocop:enable Style/GuardClause
69
74
  end
70
75
 
71
76
  # [start] Called when the rails version is NOT 3.x
72
77
  def start(name, id, payload)
73
78
  return unless (transaction = @agent.current_transaction)
74
- debug '[Subscriber] start():'
75
- debug id
76
- debug name
77
- debug transaction
79
+
80
+ debug '[Subscriber] start():'
81
+ debug id
82
+ debug name
83
+ debug transaction
78
84
  normalized = @normalizers.normalize(transaction, name, payload)
79
85
 
80
86
  span =
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
- #
2
+
3
3
  # This class generates/appends the json log file after every Web Request
4
- #
5
4
 
6
- require 'stackify/root_info'
7
- require 'stackify/serializers'
5
+ require 'stackify_apm/root_info'
6
+ require 'stackify_apm/serializers'
8
7
 
9
8
  module StackifyRubyAPM
10
-
11
9
  # @api private
12
10
  class TraceLogger
13
11
  include Log
@@ -15,14 +13,12 @@ module StackifyRubyAPM
15
13
  def initialize(config)
16
14
  @config = config
17
15
  @trace_file_counter = 0
18
- @process_id = 0
19
16
  @transaction_serializers = Serializers::Transactions.new(config)
20
17
  end
21
18
 
22
- attr_accessor :trace_file_counter, :process_id
19
+ attr_accessor :trace_file_counter
23
20
 
24
21
  def post(transactions = [])
25
-
26
22
  # convert transactions to json
27
23
  json_traces = []
28
24
  transactions.each do |transaction|
@@ -33,16 +29,14 @@ module StackifyRubyAPM
33
29
  json_traces.push(json_transaction)
34
30
  end
35
31
 
36
- pid = $PID || Process.pid
37
- host_name = @config.hostname || `hostname`
38
32
  date_now = Time.now
39
- current_datetime = date_now.strftime("%Y-%m-%d, %H:%M:%S.%6N")
33
+ current_datetime = date_now.strftime('%Y-%m-%d, %H:%M:%S.%6N')
34
+
35
+ return unless ENV['STACKIFY_RUBY_ENV'] != 'rspec'
40
36
 
41
- if(ENV['STACKIFY_RUBY_ENV'] != 'rspec')
42
- json_traces.each do |json_trace|
43
- update_json = current_datetime + " > " + json_trace + "\n"
44
- @config.tracer_logger.<<(update_json)
45
- end
37
+ json_traces.each do |json_trace|
38
+ update_json = current_datetime + ' > ' + json_trace + "\n"
39
+ @config.tracer_logger.<<(update_json)
46
40
  end
47
41
  end
48
42
  end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This class creates and initializes a new transaction.
5
+ #
6
+ module StackifyRubyAPM
7
+ require 'securerandom'
8
+ # @api private
9
+ class Transaction
10
+ DEFAULT_TYPE = 'custom'.freeze
11
+
12
+ def initialize(instrumenter, name = nil,
13
+ type = nil, context: nil)
14
+ # puts "Loads transaction new initialize
15
+ # instrumenter, name = nil, type = nil, context: nil"
16
+ @id = SecureRandom.uuid
17
+ @instrumenter = instrumenter
18
+ @name = name
19
+ @type = type || DEFAULT_TYPE
20
+ @timestamp = Time.now.to_f * 1000
21
+
22
+ @spans = []
23
+ @span_id_ticker = -1
24
+ @dropped_spans = 0
25
+
26
+ @notifications = [] # for AS::Notifications
27
+ @context = context || Context.new
28
+ @exceptions = []
29
+
30
+ yield self if block_given?
31
+ end
32
+
33
+ attr_accessor :name, :type, :http_status
34
+ attr_reader :id, :context, :duration, :dropped_spans, :instrumenter
35
+ attr_reader :timestamp, :spans, :result, :notifications, :exceptions
36
+
37
+ def add_exception(exception)
38
+ @exceptions << exception
39
+ end
40
+
41
+ def release
42
+ @instrumenter.current_transaction = nil
43
+ end
44
+
45
+ def done(result = nil)
46
+ @duration = Time.now.to_f * 1000
47
+ @result = result
48
+ @http_status = result
49
+
50
+ self
51
+ end
52
+
53
+ def done?
54
+ !@duration.nil?
55
+ end
56
+
57
+ def submit(result = nil, status: nil, headers: {})
58
+ done result unless duration
59
+ context.response = Context::Response.new(status, headers: headers) if status
60
+
61
+ release
62
+ @instrumenter.submit_transaction self
63
+
64
+ self
65
+ end
66
+
67
+ # This method is being used in unit testing
68
+ def running_spans
69
+ spans.select(&:running?)
70
+ end
71
+
72
+ def span(name, type = nil, backtrace: nil, context: nil)
73
+ span = build_and_start_span(name, type, context, backtrace)
74
+ return span unless block_given?
75
+
76
+ begin
77
+ result = yield span
78
+ ensure
79
+ span.done
80
+ end
81
+
82
+ result
83
+ end
84
+
85
+ def current_span
86
+ spans.reverse.lazy.find(&:running?)
87
+ end
88
+
89
+ def inspect
90
+ "<StackifyRubyAPM::Transaction id:#{id}" \
91
+ " name:#{name.inspect}" \
92
+ " type:#{type.inspect}" \
93
+ '>'
94
+ end
95
+
96
+ private
97
+
98
+ def next_span_id
99
+ @span_id_ticker += 1
100
+ end
101
+
102
+ def next_span(name, type, context)
103
+ Span.new(
104
+ self,
105
+ next_span_id,
106
+ name,
107
+ type,
108
+ parent_id: current_span.nil? ? -1 : current_span.id,
109
+ context: context,
110
+ http_status: @http_status
111
+ )
112
+ end
113
+
114
+ def span_frames_min_duration?
115
+ @instrumenter.agent.config.span_frames_min_duration != 0
116
+ end
117
+
118
+ def build_and_start_span(name, type, context, backtrace)
119
+ span = next_span(name, type, context)
120
+ spans << span
121
+
122
+ span.original_backtrace = backtrace if backtrace && span_frames_min_duration?
123
+
124
+ span.start
125
+ end
126
+ end
127
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This module utilizes or handles the time usage or duration of transaction
2
4
  module StackifyRubyAPM
3
5
  # @api private
@@ -7,4 +9,4 @@ module StackifyRubyAPM
7
9
  end
8
10
  end
9
11
  end
10
- require 'stackify/util/inspector'
12
+ require 'stackify_apm/util/inspector'
@@ -28,4 +28,4 @@ if RUBY_VERSION < '2.3'
28
28
  class Hash
29
29
  include RubyDig
30
30
  end
31
- end
31
+ end
File without changes
@@ -6,7 +6,6 @@ module StackifyRubyAPM
6
6
  @width = width
7
7
  end
8
8
 
9
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
10
9
  def transaction(transaction)
11
10
  unless transaction.done?
12
11
  raise ArgumentError, 'Transaction still running'
@@ -47,7 +46,6 @@ module StackifyRubyAPM
47
46
  puts e.backtrace.join("\n")
48
47
  nil
49
48
  end
50
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
51
49
 
52
50
  private
53
51
 
@@ -56,4 +54,4 @@ module StackifyRubyAPM
56
54
  end
57
55
  end
58
56
  end
59
- end
57
+ end