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,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