stackify-ruby-apm 0.9.0 → 1.0.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +18 -0
  3. data/Gemfile.lock +181 -8
  4. data/LICENSE.md +66 -0
  5. data/README.md +5 -3
  6. data/Rakefile +4 -0
  7. data/docker-compose.yml +46 -0
  8. data/lib/stackify/agent.rb +22 -9
  9. data/lib/stackify/config.rb +65 -29
  10. data/lib/stackify/context/response.rb +13 -0
  11. data/lib/stackify/context_builder.rb +0 -2
  12. data/lib/stackify/error.rb +2 -2
  13. data/lib/stackify/error_builder.rb +1 -1
  14. data/lib/stackify/instrumenter.rb +4 -4
  15. data/lib/stackify/logger/logger_high_version.rb +65 -0
  16. data/lib/stackify/logger/logger_lower_version.rb +63 -0
  17. data/lib/stackify/middleware.rb +0 -4
  18. data/lib/stackify/normalizers.rb +0 -1
  19. data/lib/stackify/normalizers/active_record.rb +3 -1
  20. data/lib/stackify/railtie.rb +0 -1
  21. data/lib/stackify/serializers/errors.rb +8 -7
  22. data/lib/stackify/serializers/transactions.rb +3 -2
  23. data/lib/stackify/span.rb +1 -2
  24. data/lib/stackify/span/context.rb +5 -1
  25. data/lib/stackify/spies/action_dispatch.rb +8 -1
  26. data/lib/stackify/spies/httpclient.rb +14 -5
  27. data/lib/stackify/spies/httprb.rb +45 -0
  28. data/lib/stackify/spies/mongo.rb +13 -1
  29. data/lib/stackify/spies/net_http.rb +14 -3
  30. data/lib/stackify/spies/redis.rb +66 -0
  31. data/lib/stackify/spies/sinatra.rb +3 -1
  32. data/lib/stackify/spies/sinatra_activerecord/mysql_adapter.rb +177 -0
  33. data/lib/stackify/spies/sinatra_activerecord/postgresql_adapter.rb +96 -0
  34. data/lib/stackify/spies/sinatra_activerecord/sqlite_adapter.rb +48 -0
  35. data/lib/stackify/spies/tilt.rb +8 -1
  36. data/lib/stackify/stacktrace_builder.rb +4 -3
  37. data/lib/stackify/subscriber.rb +4 -4
  38. data/lib/stackify/trace_logger.rb +6 -23
  39. data/lib/stackify/transaction.rb +10 -1
  40. data/lib/stackify/util.rb +0 -13
  41. data/lib/stackify/version.rb +1 -1
  42. data/lib/stackify/worker.rb +5 -5
  43. data/lib/stackify_ruby_apm.rb +2 -6
  44. data/run-test.sh +75 -0
  45. data/stackify-ruby-apm.gemspec +0 -1
  46. metadata +12 -17
  47. data/lib/stackify/logger.rb +0 -10
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+ # Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
3
+ #
4
+ module StackifyRubyAPM
5
+ # @api private
6
+ module Spies
7
+ # @api private
8
+ class PostgresqlAdapterSpy
9
+ TYPE = 'db.sinatra_active_record.sql'.freeze
10
+
11
+ # rubocop:disable Metrics/MethodLength
12
+ def install
13
+ ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements.class_eval do
14
+ alias exec_query_without_apm exec_query
15
+ alias exec_delete_without_apm exec_delete
16
+ alias exec_update_without_apm exec_update
17
+
18
+ def exec_update(sql, name = nil, binds = [])
19
+ result = nil
20
+
21
+ unless StackifyRubyAPM.current_transaction
22
+ exec_update_without_apm(sql, name, binds)
23
+ end
24
+
25
+ ctx = Span::Context.new(
26
+ CATEGORY: 'Database',
27
+ SUBCATEGORY: 'Execute',
28
+ COMPONENT_CATEGORY: 'DB Query',
29
+ COMPONENT_DETAIL: 'Execute SQL Query',
30
+ SQL: sql,
31
+ PROVIDER: "mysql"
32
+ )
33
+
34
+ result = exec_update_without_apm(sql, name, binds)
35
+
36
+ StackifyRubyAPM.span name, TYPE, context: ctx do
37
+ return result
38
+ end
39
+ end
40
+
41
+ def exec_delete(sql, name = nil, binds = [])
42
+ result = nil
43
+
44
+ unless StackifyRubyAPM.current_transaction
45
+ exec_delete_without_apm(sql, name, binds)
46
+ end
47
+
48
+ ctx = Span::Context.new(
49
+ CATEGORY: 'Database',
50
+ SUBCATEGORY: 'Execute',
51
+ COMPONENT_CATEGORY: 'DB Query',
52
+ COMPONENT_DETAIL: 'Execute SQL Query',
53
+ SQL: sql,
54
+ PROVIDER: "postgresql"
55
+ )
56
+
57
+ result = exec_delete_without_apm(sql, name, binds)
58
+
59
+ StackifyRubyAPM.span name, TYPE, context: ctx do
60
+ return result
61
+ end
62
+ end
63
+
64
+
65
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
66
+ result = nil
67
+
68
+ unless StackifyRubyAPM.current_transaction
69
+ exec_query_without_apm(sql, name, binds)
70
+ end
71
+
72
+ ctx = Span::Context.new(
73
+ CATEGORY: 'Database',
74
+ SUBCATEGORY: 'Execute',
75
+ COMPONENT_CATEGORY: 'DB Query',
76
+ COMPONENT_DETAIL: 'Execute SQL Query',
77
+ SQL: sql,
78
+ PROVIDER: "postgresql"
79
+ )
80
+
81
+ result = exec_query_without_apm(sql, name, binds)
82
+
83
+ StackifyRubyAPM.span name, TYPE, context: ctx do
84
+ return result
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ # rubocop:enable Metrics/MethodLength
91
+ end
92
+
93
+ register 'ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements', 'active_record/connection_adapters/postgresql/database_statements', PostgresqlAdapterSpy.new
94
+ end
95
+ end
96
+
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ # Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
3
+ #
4
+ module StackifyRubyAPM
5
+ # @api private
6
+ module Spies
7
+ # @api private
8
+ class SqliteAdapterSpy
9
+ TYPE = 'db.sinatra_active_record.sql'.freeze
10
+
11
+ # rubocop:disable Metrics/MethodLength
12
+ def install
13
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.class_eval do
14
+ alias exec_query_without_apm exec_query
15
+
16
+
17
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
18
+ result = nil
19
+
20
+ unless StackifyRubyAPM.current_transaction
21
+ exec_query_without_apm(sql, name, binds)
22
+ end
23
+
24
+ ctx = Span::Context.new(
25
+ CATEGORY: 'Database',
26
+ SUBCATEGORY: 'Execute',
27
+ COMPONENT_CATEGORY: 'DB Query',
28
+ COMPONENT_DETAIL: 'Execute SQL Query',
29
+ SQL: sql,
30
+ PROVIDER: "sqlite"
31
+ )
32
+
33
+ result = exec_query_without_apm(sql, name, binds)
34
+
35
+ StackifyRubyAPM.span name, TYPE, context: ctx do
36
+ return result
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ # rubocop:enable Metrics/MethodLength
43
+ end
44
+
45
+ register 'ActiveRecord::ConnectionAdapters::SQLite3Adapter', 'active_record/connection_adapters/sqlite3_adapter', SqliteAdapterSpy.new
46
+ end
47
+ end
48
+
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
+ #
3
+ # Monkey patch for the Tilt::Template class on template rendering and span creation.
4
+ #
2
5
 
3
6
  module StackifyRubyAPM
4
7
  # @api private
5
8
  module Spies
6
9
  # @api private
7
10
  class TiltSpy
8
- # This class monkeypatch the Tilt::Template class with render method.
11
+
9
12
  TYPE = 'template.tilt'.freeze
10
13
 
11
14
  def install
@@ -15,6 +18,8 @@ module StackifyRubyAPM
15
18
  def render(*args, &block)
16
19
  name = options[:__stackify_apm_template_name] || 'Unknown template'
17
20
 
21
+ # Creates new span for Tilt templating
22
+ #
18
23
  StackifyRubyAPM.span name, TYPE do
19
24
  render_without_apm(*args, &block)
20
25
  end
@@ -23,6 +28,8 @@ module StackifyRubyAPM
23
28
  end
24
29
  end
25
30
 
31
+ # Registers Tilt spy, go to: /stackify/spies.rb
32
+ #
26
33
  register 'Tilt::Template', 'tilt/template', TiltSpy.new
27
34
  end
28
35
  end
@@ -21,7 +21,7 @@ module StackifyRubyAPM
21
21
 
22
22
  attr_reader :config
23
23
 
24
- def build(backtrace, type:)
24
+ def build(backtrace, type)
25
25
  Stacktrace.new.tap do |s|
26
26
  s.frames = backtrace.map do |line|
27
27
  @cache[[line, type]]
@@ -36,6 +36,7 @@ module StackifyRubyAPM
36
36
  line, type = keys
37
37
  abs_path, lineno, function, _module_name = parse_line(line)
38
38
  current_filename = strip_load_path(abs_path)
39
+ library_frame = library_frame?(config, abs_path)
39
40
 
40
41
  frame = Stacktrace::Frame.new
41
42
  #frame.abs_path = abs_path
@@ -45,7 +46,7 @@ module StackifyRubyAPM
45
46
  #frame.library_frame = library_frame?(config, abs_path)
46
47
 
47
48
  line_count =
48
- context_lines_for(config, type, library_frame: frame.library_frame)
49
+ context_lines_for(config, type, library_frame)
49
50
  frame.build_context line_count
50
51
 
51
52
  cache[[line, type]] = frame
@@ -93,7 +94,7 @@ module StackifyRubyAPM
93
94
  prefix ? path[prefix.chomp(File::SEPARATOR).length + 1..-1] : path
94
95
  end
95
96
 
96
- def context_lines_for(config, type, library_frame:)
97
+ def context_lines_for(config, type, library_frame)
97
98
  key = "source_lines_#{type}_#{library_frame ? 'library' : 'app'}_frames"
98
99
  config.send(key.to_sym)
99
100
  end
@@ -13,7 +13,7 @@ module StackifyRubyAPM
13
13
  include Log
14
14
 
15
15
  def initialize(agent)
16
- debug '@stackify_ruby [Subscriber] [lib/subscriber.rb] initialize()'
16
+ debug '[Subscriber] initialize()'
17
17
  debug agent.inspect
18
18
  @agent = agent
19
19
  @normalizers = Normalizers.build(agent.config)
@@ -38,7 +38,7 @@ module StackifyRubyAPM
38
38
  # [call] Called when the rails version is 3.x
39
39
  def call(name, started, finished, id, payload)
40
40
  return unless (transaction = @agent.current_transaction)
41
- debug '@stackify_ruby [Subscriber] [lib/subscriber.rb] call():'
41
+ debug '[Subscriber] call():'
42
42
  debug id
43
43
  debug name
44
44
  debug transaction
@@ -71,7 +71,7 @@ module StackifyRubyAPM
71
71
  # [start] Called when the rails version is NOT 3.x
72
72
  def start(name, id, payload)
73
73
  return unless (transaction = @agent.current_transaction)
74
- debug '@stackify_ruby [Subscriber] [lib/subscriber.rb] start():'
74
+ debug '[Subscriber] start():'
75
75
  debug id
76
76
  debug name
77
77
  debug transaction
@@ -91,7 +91,7 @@ module StackifyRubyAPM
91
91
  # [finish] Called when the rails version is NOT 3.x
92
92
  def finish(_name, id, _payload)
93
93
  # debug "AS::Notification#finish:#{name}:#{id}"
94
- debug '@stackify_ruby [Subscriber] [lib/subscriber.rb] finish():'
94
+ debug '[Subscriber] finish():'
95
95
  return unless (transaction = @agent.current_transaction)
96
96
 
97
97
  while (notification = transaction.notifications.pop)
@@ -22,7 +22,7 @@ module StackifyRubyAPM
22
22
  attr_accessor :trace_file_counter, :process_id
23
23
 
24
24
  def post(transactions = [])
25
-
25
+
26
26
  # convert transactions to json
27
27
  json_traces = []
28
28
  transactions.each do |transaction|
@@ -36,31 +36,14 @@ module StackifyRubyAPM
36
36
  pid = $PID || Process.pid
37
37
  host_name = @config.hostname || `hostname`
38
38
  date_now = Time.now
39
- current_time = date_now.strftime("%Y-%m-%d, %H:%M:%S.%6N")
40
- trace_path = @config.log_trace_path
41
- trace_file_result = @config.check_lastlog_needs_new(trace_path)
42
- current_trace_file = trace_file_result['latest_file']
43
-
44
- if trace_file_result['new_flagger'] == true
45
- @trace_file_counter = @trace_file_counter + 1
46
- file_ctr = @trace_file_counter
47
- current_trace_file = trace_path + host_name + "#" + pid.to_s + "-" + file_ctr.to_s + ".log"
48
- else
49
- if @process_id != pid
50
- @trace_file_counter = 1
51
- @process_id = pid
52
- file_ctr = @trace_file_counter
53
- current_trace_file = trace_path + host_name + "#" + pid.to_s + "-" + file_ctr.to_s + ".log"
54
- end
55
- end
39
+ current_datetime = date_now.strftime("%Y-%m-%d, %H:%M:%S.%6N")
56
40
 
57
- current_trace_file = current_trace_file.gsub(/[[:space:]]/, '')
58
- open(current_trace_file, 'a') {|f|
41
+ if(ENV['STACKIFY_RUBY_ENV'] != 'rspec')
59
42
  json_traces.each do |json_trace|
60
- f.puts current_time + "> " + json_trace + "\n"
43
+ update_json = current_datetime + " > " + json_trace + "\n"
44
+ @config.tracer_logger.<<(update_json)
61
45
  end
62
- }
63
-
46
+ end
64
47
  end
65
48
  end
66
49
  end
@@ -24,6 +24,7 @@ module StackifyRubyAPM
24
24
 
25
25
  @notifications = [] # for AS::Notifications
26
26
  @context = context || Context.new
27
+ @exceptions = []
27
28
 
28
29
  yield self if block_given?
29
30
  end
@@ -31,7 +32,11 @@ module StackifyRubyAPM
31
32
 
32
33
  attr_accessor :name, :type, :http_status
33
34
  attr_reader :id, :context, :duration, :dropped_spans,
34
- :timestamp, :spans, :result, :notifications, :instrumenter
35
+ :timestamp, :spans, :result, :notifications, :instrumenter, :exceptions
36
+
37
+ def add_exception exception
38
+ @exceptions << exception
39
+ end
35
40
 
36
41
  def release
37
42
  @instrumenter.current_transaction = nil
@@ -60,6 +65,10 @@ module StackifyRubyAPM
60
65
 
61
66
  self
62
67
  end
68
+ # This method is being used in unit testing
69
+ def running_spans
70
+ spans.select(&:running?)
71
+ end
63
72
  # rubocop:disable Metrics/MethodLength
64
73
  def span name, type = nil, backtrace: nil, context: nil
65
74
  span = build_and_start_span(name, type, context, backtrace)
data/lib/stackify/util.rb CHANGED
@@ -2,22 +2,9 @@
2
2
  module StackifyRubyAPM
3
3
  # @api private
4
4
  module Util
5
- def self.nearest_minute(target = Time.now.utc)
6
- target - target.to_i % 60
7
- end
8
-
9
5
  def self.micros(target = Time.now.utc)
10
6
  target.to_i * 1_000_000 + target.usec
11
7
  end
12
-
13
- def self.inspect_transaction(transaction)
14
- Inspector.new.transaction transaction
15
- end
16
-
17
- def self.git_sha
18
- sha = `git rev-parse --verify HEAD 2>&1`.chomp
19
- $? && $?.success? ? sha : nil # rubocop:disable Style/SpecialGlobalVars
20
- end
21
8
  end
22
9
  end
23
10
  require 'stackify/util/inspector'
@@ -1,4 +1,4 @@
1
1
  # Sets the version of the APM
2
2
  module StackifyRubyAPM
3
- VERSION = '0.9.0'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
@@ -83,13 +83,13 @@ module StackifyRubyAPM
83
83
  def collect_and_send_transactions
84
84
  return if pending_transactions.empty?
85
85
  transactions = collect_batched_transactions
86
- debug '@stackify_ruby [lib/worker.rb] Successfully collect and send transaction to the to trace logger.'
86
+ debug '[Worker] Successfully collect and send transaction to the to trace logger.'
87
87
  begin
88
88
  @trace_logger.post(transactions)
89
89
  rescue ::Exception => e
90
- debug 'Failed posting: '
91
- debug e.inspect
92
- debug e.backtrace.join("\n")
90
+ error '[Worker] Collect_and_send_transactions error:'
91
+ error e.inspect
92
+ error e.backtrace.join("\n")
93
93
  nil
94
94
  end
95
95
  end
@@ -98,7 +98,7 @@ module StackifyRubyAPM
98
98
  return if pending_transactions.empty?
99
99
  transactions = collect_batched_transactions
100
100
  payload = @serializers.errors.build_all([msg.error])
101
- debug '@stackify_ruby [lib/worker.rb] error'
101
+ debug '[Worker] post_error()'
102
102
  # @trace_logger.post(payload, transactions[0])
103
103
  end
104
104
 
@@ -15,10 +15,10 @@
15
15
  require 'stackify/log'
16
16
  require 'stackify/version'
17
17
  require 'stackify/util/dig'
18
-
19
18
  require 'stackify/agent'
19
+ # Checks ruby version
20
+ require (RUBY_VERSION.to_f >= 2.4) ? 'stackify/logger/logger_high_version' : 'stackify/logger/logger_lower_version'
20
21
  require 'stackify/config'
21
- require 'stackify/logger'
22
22
  require 'stackify/context'
23
23
  require 'stackify/instrumenter'
24
24
  require 'stackify/internal_error'
@@ -35,8 +35,6 @@ module StackifyRubyAPM
35
35
  # @param config [Config] An instance of Config
36
36
  # @return [Agent] The resulting [Agent]
37
37
  def self.start(config = {})
38
- # debug "Starts the StackifyRubyAPM Agent self.start()"
39
-
40
38
  Agent.start config
41
39
  end
42
40
 
@@ -73,7 +71,6 @@ module StackifyRubyAPM
73
71
  # @yield [Transaction] Optional block encapsulating transaction
74
72
  # @return [Transaction] Unless block given
75
73
  def self.transaction(name = nil, type = nil, context: nil, &block)
76
- # debug "@stackify_ruby [lib/stackify_ruby_apm.rb] loads self.transaction(name = nil, type = nil, context: nil, &block)"
77
74
  return (block_given? ? yield : nil) unless agent
78
75
 
79
76
  agent.transaction(name, type, context: context, &block)
@@ -105,7 +102,6 @@ module StackifyRubyAPM
105
102
  # @param rack_env [Rack::Env] A Rack env
106
103
  # @return [Context] The built context
107
104
  def self.build_context(rack_env)
108
- # debug "@stackify_ruby [lib/stackify_ruby_apm.rb] self.build_context(rack_env)"
109
105
  agent && agent.build_context(rack_env)
110
106
  end
111
107
 
data/run-test.sh ADDED
@@ -0,0 +1,75 @@
1
+ #!/bin/bash
2
+
3
+ function startContainers() {
4
+ echo "Starting up containers..."
5
+ docker-compose up -d -V --remove-orphans --force-recreate
6
+ }
7
+
8
+ function waitForContainers() {
9
+ echo 'Waiting for container startup 10s...'
10
+ sleep 5
11
+ echo 'Waiting for container startup 5s...'
12
+ sleep 5
13
+ }
14
+
15
+ function stopContainers() {
16
+ echo "Stopping Containers..."
17
+ docker-compose down --remove-orphans
18
+
19
+ # delete volumes
20
+ echo "Delete Volumes..."
21
+ docker volume rm stackify-ruby-apm_postgresdata &> /dev/null
22
+ docker volume rm stackify-ruby-apm_mysqldata &> /dev/null
23
+ docker volume rm stackify-ruby-apm_mongodata &> /dev/null
24
+ docker volume rm stackify-ruby-apm_mongodata_config &> /dev/null
25
+ echo "Deleted Volumes"
26
+ }
27
+
28
+ function rspec_on_multiple_versions(){
29
+ # test against multiple ruby versions
30
+ set -e
31
+
32
+ RUBY_VERSIONS=('2.0.0-p648' '2.1.10' '2.2.10' '2.3.7' '2.4.0' '2.5.1' '2.6.0-preview3')
33
+ # BUNDLER ERROR
34
+ # '2.0.0-p648' 2.1.10
35
+
36
+ for ver in "${RUBY_VERSIONS[@]}"
37
+ do
38
+ if ! rbenv versions | grep -w $ver; then
39
+ # remove stale rbenv shim before rehashing
40
+ SHIM=/home/$USER/.rbenv/shims/.rbenv-shim
41
+ if [ -f $SHIM ]; then
42
+ rm $SHIM
43
+ fi
44
+
45
+ rbenv install --verbose $ver
46
+ rbenv rehash
47
+ rbenv local $ver
48
+ gem install bundler -v '~> 1.16'
49
+ fi
50
+
51
+ rbenv local $ver
52
+ rbenv exec bundle update
53
+
54
+ echo "====================================================="
55
+ echo "$ver: Start Test"
56
+ echo "====================================================="
57
+
58
+ if ! bundle exec rspec spec/ --format documentation --fail-fast; then
59
+ echo ">>>Unit Test Error on Version: $ver<<<"
60
+ stopContainers
61
+ exit 1
62
+ else
63
+ echo "====================================================="
64
+ echo "$ver: End Test"
65
+ echo "====================================================="
66
+ fi
67
+ done
68
+ }
69
+
70
+
71
+ startContainers
72
+ waitForContainers
73
+
74
+ rspec_on_multiple_versions
75
+ stopContainers