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,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
- #
2
+
3
3
  # This class builds the context of the Transaction/Span
4
4
  # Context represents the HTTP request information such as headers, cookies, body.
5
- #
6
5
 
7
6
  module StackifyRubyAPM
8
7
  # @api private
@@ -19,8 +18,6 @@ module StackifyRubyAPM
19
18
 
20
19
  private
21
20
 
22
- # rubocop:disable Metrics/AbcSize
23
- #
24
21
  # Request format and values are assigned to context
25
22
  #
26
23
  def apply_to_request(context, rack_env)
@@ -36,7 +33,6 @@ module StackifyRubyAPM
36
33
 
37
34
  context
38
35
  end
39
- # rubocop:enable Metrics/AbcSize
40
36
 
41
37
  def get_body(req)
42
38
  return req.POST if req.form_data?
@@ -73,6 +69,7 @@ module StackifyRubyAPM
73
69
 
74
70
  def build_http_version(rack_env)
75
71
  return unless (http_version = rack_env['HTTP_VERSION'])
72
+
76
73
  http_version.gsub(%r{HTTP/}, '')
77
74
  end
78
75
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'stackify/stacktrace'
4
- require 'stackify/context'
5
- require 'stackify/error/exception'
6
- require 'stackify/error/log'
3
+ require 'stackify_apm/stacktrace'
4
+ require 'stackify_apm/context'
5
+ require 'stackify_apm/error/exception'
6
+ require 'stackify_apm/error/log'
7
7
 
8
8
  module StackifyRubyAPM
9
9
  # @api private
@@ -11,8 +11,9 @@ module StackifyRubyAPM
11
11
  def initialize(culprit: nil)
12
12
  @id = SecureRandom.uuid
13
13
  @culprit = culprit
14
+ time_now = Time.now
14
15
 
15
- @timestamp = (Time.now).to_f * 1000
16
+ @timestamp = time_now.to_f * 1000
16
17
  @context = Context.new
17
18
 
18
19
  @transaction_id = nil
@@ -21,4 +22,4 @@ module StackifyRubyAPM
21
22
  attr_accessor :id, :culprit, :exception, :log, :transaction_id, :context
22
23
  attr_reader :timestamp
23
24
  end
24
- end
25
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StackifyRubyAPM
4
+ class Error
5
+ # @api private
6
+ class Exception
7
+ MOD_SPLIT = '::'.freeze
8
+
9
+ def initialize(exception, **attrs)
10
+ @message =
11
+ "#{exception.class}: #{exception.message}"
12
+ @type = exception.class.to_s
13
+ @module = format_module exception
14
+
15
+ attrs.each do |key, val|
16
+ send(:"#{key}=", val)
17
+ end
18
+ end
19
+
20
+ attr_accessor(
21
+ :attributes,
22
+ :code,
23
+ :handled,
24
+ :message,
25
+ :module,
26
+ :stacktrace,
27
+ :type
28
+ )
29
+
30
+ private
31
+
32
+ def format_module(exception)
33
+ exception.class.to_s.split(MOD_SPLIT)[0...-1].join(MOD_SPLIT)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StackifyRubyAPM
4
+ class Error
5
+ # @api private
6
+ class Log
7
+ def initialize(message, attrs = {})
8
+ @message = message
9
+
10
+ attrs.each do |key, val|
11
+ send(:"#{key}=", val)
12
+ end
13
+ end
14
+
15
+ attr_accessor(
16
+ :level,
17
+ :logger_name,
18
+ :message,
19
+ :param_message,
20
+ :stacktrace
21
+ )
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module build errors/exceptions and the Frames/stacktraces affected/related to the error
4
+
5
+ module StackifyRubyAPM
6
+ # @api private
7
+ class ErrorBuilder
8
+ def initialize(agent)
9
+ @agent = agent
10
+ end
11
+
12
+ def build_exception(exception, handled: true)
13
+ error = Error.new
14
+ error.exception = Error::Exception.new(exception, handled: handled)
15
+
16
+ if exception.backtrace
17
+ add_stacktrace error, :exception, exception.backtrace
18
+ end
19
+
20
+ add_transaction_id error
21
+
22
+ if (transaction = StackifyRubyAPM.current_transaction)
23
+ error.context = transaction.context.dup
24
+ end
25
+
26
+ error
27
+ end
28
+
29
+ def build_log(message, backtrace: nil, **attrs)
30
+ error = Error.new
31
+ error.log = Error::Log.new(message, **attrs)
32
+
33
+ add_stacktrace error, :log, backtrace if backtrace
34
+ add_transaction_id error
35
+
36
+ error
37
+ end
38
+
39
+ private
40
+
41
+ def add_stacktrace(error, kind, backtrace)
42
+ stacktrace =
43
+ @agent.stacktrace_builder.build(backtrace, :error)
44
+ return unless stacktrace
45
+
46
+ case kind
47
+ when :exception
48
+ error.exception.stacktrace = stacktrace
49
+ when :log
50
+ error.log.stacktrace = stacktrace
51
+ end
52
+
53
+ error.culprit = stacktrace.frames.first.function
54
+ end
55
+
56
+ def add_transaction_id(error)
57
+ return unless (transaction = StackifyRubyAPM.current_transaction)
58
+ error.transaction_id = transaction.id
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # get_profiler - It will return the database driver of the Object.
4
+ #
5
+ module DatabaseHelper
6
+ # return back valid PROVIDER based on driver name passed in
7
+ # rubocop:disable Metrics/CyclomaticComplexity
8
+ # rubocop:disable Metrics/PerceivedComplexity
9
+ def get_profiler(driver)
10
+ if driver.to_s.empty?
11
+ 'generic'
12
+ elsif driver.include? 'mysql'
13
+ 'mysql'
14
+ elsif driver.include? 'postgres'
15
+ 'postgresql'
16
+ elsif driver.include? 'oci8'
17
+ 'oracle'
18
+ elsif driver.include? 'db2'
19
+ 'db2'
20
+ elsif driver.include? 'sqlite'
21
+ 'generic'
22
+ end
23
+ end
24
+ # rubocop:enable Metrics/CyclomaticComplexity
25
+ # rubocop:enable Metrics/PerceivedComplexity
26
+ end
27
+ include DatabaseHelper
@@ -1,24 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stackify_apm/span'
4
+ require 'stackify_apm/transaction'
1
5
  #
2
6
  # The Instrumenter will build/process the transactions and spans.
3
7
  #
4
-
5
- require 'stackify/span'
6
- require 'stackify/transaction'
7
-
8
8
  module StackifyRubyAPM
9
- # @api private
10
- class Instrumenter
9
+ # @api private
10
+ class Instrumenter
11
11
  include Log
12
12
 
13
13
  KEY = :_stackify_transaction_key
14
-
14
+ # Transaction Info class
15
15
  class TransactionInfo
16
16
  def initialize
17
17
  self.current = nil
18
18
  end
19
19
 
20
20
  def current
21
- # puts '[Instrumenter] [lib/instrumenter.rb] TransactionInfo.current()'
22
21
  Thread.current[KEY]
23
22
  end
24
23
 
@@ -48,20 +47,17 @@ module StackifyRubyAPM
48
47
  end
49
48
 
50
49
  def current_transaction
51
- @transaction_info.current
50
+ @transaction_info.current
52
51
  end
53
52
 
54
53
  def current_transaction=(transaction)
55
54
  @transaction_info.current = transaction
56
55
  end
57
56
 
58
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
59
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
60
- #
61
57
  # Creates a new transaction or return the currently running
62
58
  #
63
59
  def transaction(*args)
64
- debug "[Instrumenter] transaction(*args)"
60
+ debug '[Instrumenter] transaction(*args)'
65
61
  unless config.instrument
66
62
  yield if block_given?
67
63
  return
@@ -83,17 +79,14 @@ module StackifyRubyAPM
83
79
  self.current_transaction = nil
84
80
  transaction.done
85
81
  end
86
-
87
- #puts transaction.inspect
88
82
 
89
83
  transaction
90
84
  end
91
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
93
85
 
94
86
  def span(*args, &block)
95
87
  unless current_transaction
96
88
  return yield if block_given?
89
+
97
90
  return
98
91
  end
99
92
 
@@ -114,5 +107,5 @@ module StackifyRubyAPM
114
107
  "current_transaction=#{current_transaction.inspect}" \
115
108
  '>'
116
109
  end
117
- end
118
- end
110
+ end
111
+ end
File without changes
File without changes
@@ -8,6 +8,20 @@
8
8
  module StackifyRubyAPM
9
9
  # @api private
10
10
  class LogDevice < Logger::LogDevice
11
+ alias_method 'write_without_apm', 'write'
12
+ def write(message)
13
+ if @filename
14
+ if FileTest.exist?(@filename)
15
+ else
16
+ @dev = create_logfile(@filename)
17
+ File.chmod(0o777, @filename)
18
+
19
+ return @dev
20
+ end
21
+ end
22
+ write_without_apm(message)
23
+ end
24
+
11
25
  def add_log_header(file)
12
26
  # Make it only empty.
13
27
  end
@@ -21,10 +35,7 @@ module StackifyRubyAPM
21
35
  # set a temporary filename that doesn't have the increment prefix and .log format.
22
36
  temp_filename = @filename.gsub(/\-(d*\.?\d*).log/, '')
23
37
  # set a temporary file increment while stripping the current filename and retain the number
24
- txttemp_fileincrement = @filename.gsub(temp_filename,'')
25
- txttemp_fileincrement = txttemp_fileincrement.gsub('-','')
26
- txttemp_fileincrement = txttemp_fileincrement.gsub('.log','')
27
-
38
+ txttemp_fileincrement = @filename.gsub(temp_filename, '').delete('^0-9')
28
39
  # convert the string value to integer
29
40
  current_fileprefix = txttemp_fileincrement.to_i
30
41
  # assign as filename counter
@@ -33,12 +44,12 @@ module StackifyRubyAPM
33
44
  filename_counter = current_fileprefix + 1
34
45
  else
35
46
  ctr_flagger = 0
36
- # a loop that check if the number of filenames if exists or not
37
- (1).upto(current_fileprefix) do |i|
47
+ # a loop that check if the number of filenames if exists or not
48
+ 1.upto(current_fileprefix) do |i|
38
49
  temp_oldfile = "#{temp_filename}-#{i}.log"
39
- ctr_flagger = ctr_flagger + 1 if FileTest.exist?(temp_oldfile)
50
+ ctr_flagger += 1 if FileTest.exist?(temp_oldfile)
40
51
  end
41
- # if the counter is 0 then set the filename counter to 1
52
+ # if the counter is 0 then set the filename counter to 1
42
53
  filename_counter = 1 if ctr_flagger < 0
43
54
  end
44
55
 
@@ -47,14 +58,13 @@ module StackifyRubyAPM
47
58
  temp_newfilename = "#{temp_filename}-#{filename_counter}.log"
48
59
  @filename = temp_newfilename
49
60
  @dev = create_logfile(@filename)
61
+ File.chmod(0o777, @filename)
50
62
 
51
63
  true
52
64
  end
53
65
 
54
66
  # This is the monkeypatch of core Logger method where reformats the file name when creating the file log.
55
67
  def shift_log_period(period_end)
56
- puts "shift_log_period(period_end) mao ni"
57
-
58
68
  suffix = period_end.strftime(@shift_period_suffix)
59
69
  age_file = "#{@filename}.#{suffix}"
60
70
  if FileTest.exist?(age_file)
@@ -70,10 +80,11 @@ module StackifyRubyAPM
70
80
  @dev.close rescue nil
71
81
  File.rename(@filename.to_s, age_file)
72
82
  @dev = create_logfile(@filename)
83
+ File.chmod(0o777, @filename)
73
84
 
74
85
  true
75
86
  end
76
87
  # rubocop:enable Style/RescueModifier
77
88
  # rubocop:enable Lint/RescueWithoutErrorClass
78
89
  end
79
- end
90
+ end
@@ -28,12 +28,7 @@ module StackifyRubyAPM
28
28
  shift_period_suffix: shift_period_suffix)
29
29
  end
30
30
  end
31
- require 'stackify/logger/log_device'
32
- # rubocop:enable Style/NumericLiterals
33
- # rubocop:enable Style/GuardClause
34
- # rubocop:enable Metrics/ParameterLists
35
- # rubocop:enable Lint/UnusedMethodArgument
36
-
37
31
 
32
+ require 'stackify_apm/logger/log_device'
38
33
  end
39
34
  end
@@ -23,6 +23,7 @@ module StackifyRubyAPM
23
23
  shift_size: shift_size)
24
24
  end
25
25
  end
26
- require 'stackify/logger/log_device'
26
+
27
+ require 'stackify_apm/logger/log_device'
27
28
  end
28
29
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This class monkey patches the Rack Middleware method: e.g., call(env)
5
+ # It also handles the submission of transactions to Agent.
6
+ #
7
+ # If Agent is running, build_transaction(env) is called:
8
+ #
9
+ # -> BUILDS transaction: /stackify_ruby_apm.rb (#self.transaction)
10
+ # -> builds a [Context] from a Rack `env`: /stackify_ruby_apm.rb (#self.build_context)
11
+ # -> context may include information about the request,
12
+ # response, current user and more
13
+ #
14
+ # -> SUBMITS transaction: stackify_apm/transaction.rb (#submit)
15
+ # -> instrumenter submits transaction
16
+ # -> worker will run (if != running), responsible for sending transactions to APM
17
+ # for every 10 secs (based on config.flush_interval)
18
+ # -> transaction will be stored in queue by Agent
19
+ # -> worker will constantly execute transaction sending to APM
20
+ #
21
+
22
+ module StackifyRubyAPM
23
+ # @api private
24
+ class Middleware
25
+ def initialize(app)
26
+ @app = app
27
+ end
28
+
29
+ # This is where the requests are received, built into a transaction, and then submitted
30
+ #
31
+ def call(env)
32
+ begin
33
+ # if running? && !path_ignored?(env)
34
+ transaction = build_transaction(env) if running?
35
+
36
+ resp = @app.call env
37
+
38
+ submit_transaction(transaction, *resp) if transaction
39
+ rescue InternalError
40
+ raise # Don't report StackifyRubyAPM errors
41
+ rescue StandardError
42
+ transaction.submit('500', status: 500) if transaction
43
+ raise
44
+ ensure
45
+ transaction.release if transaction
46
+ end
47
+
48
+ resp
49
+ end
50
+
51
+ def submit_transaction(transaction, status, headers, _body)
52
+ result = status.to_i
53
+ transaction.submit(result, status: status, headers: headers)
54
+ end
55
+
56
+ # Start of transaction building with params: name, type, context
57
+ #
58
+ def build_transaction(env)
59
+ StackifyRubyAPM.transaction 'Rack', 'request', context: StackifyRubyAPM.build_context(env)
60
+ end
61
+
62
+ def running?
63
+ StackifyRubyAPM.running?
64
+ end
65
+
66
+ def config
67
+ StackifyRubyAPM.agent.config
68
+ end
69
+ end
70
+ end