stackify-ruby-apm 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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