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.
- checksums.yaml +4 -4
- data/.rubocop.yml +38 -0
- data/.ruby-version +1 -0
- data/Gemfile +17 -11
- data/Gemfile.lock +98 -95
- data/Rakefile +7 -5
- data/docker/stackify-ruby +8 -0
- data/docker/stackify-ruby-rvm +10 -0
- data/docker/stackify-ruby-test +28 -0
- data/lib/{stackify → stackify_apm}/agent.rb +42 -33
- data/lib/{stackify → stackify_apm}/config.rb +56 -39
- data/lib/{stackify → stackify_apm}/context.rb +5 -6
- data/lib/{stackify → stackify_apm}/context/request.rb +0 -0
- data/lib/{stackify → stackify_apm}/context/request/socket.rb +0 -0
- data/lib/{stackify → stackify_apm}/context/request/url.rb +2 -6
- data/lib/stackify_apm/context/response.rb +33 -0
- data/lib/{stackify → stackify_apm}/context_builder.rb +2 -5
- data/lib/{stackify → stackify_apm}/error.rb +7 -6
- data/lib/stackify_apm/error/exception.rb +37 -0
- data/lib/stackify_apm/error/log.rb +24 -0
- data/lib/stackify_apm/error_builder.rb +61 -0
- data/lib/stackify_apm/helper/database_helper.rb +27 -0
- data/lib/{stackify → stackify_apm}/instrumenter.rb +12 -19
- data/lib/{stackify → stackify_apm}/internal_error.rb +0 -0
- data/lib/{stackify → stackify_apm}/log.rb +0 -0
- data/lib/{stackify → stackify_apm}/logger/log_device.rb +22 -11
- data/lib/{stackify → stackify_apm}/logger/logger_high_version.rb +1 -6
- data/lib/{stackify → stackify_apm}/logger/logger_lower_version.rb +2 -1
- data/lib/stackify_apm/middleware.rb +70 -0
- data/lib/{stackify → stackify_apm}/naively_hashable.rb +1 -3
- data/lib/{stackify → stackify_apm}/normalizers.rb +3 -2
- data/lib/{stackify → stackify_apm}/normalizers/action_controller.rb +0 -0
- data/lib/{stackify → stackify_apm}/normalizers/action_mailer.rb +0 -0
- data/lib/{stackify → stackify_apm}/normalizers/action_view.rb +0 -0
- data/lib/{stackify → stackify_apm}/normalizers/active_record.rb +3 -25
- data/lib/{stackify → stackify_apm}/railtie.rb +5 -7
- data/lib/{stackify → stackify_apm}/root_info.rb +2 -6
- data/lib/{stackify → stackify_apm}/serializers.rb +3 -2
- data/lib/{stackify → stackify_apm}/serializers/errors.rb +7 -10
- data/lib/{stackify → stackify_apm}/serializers/transactions.rb +11 -18
- data/lib/{stackify → stackify_apm}/span.rb +8 -9
- data/lib/{stackify → stackify_apm}/span/context.rb +3 -1
- data/lib/{stackify → stackify_apm}/spies.rb +3 -2
- data/lib/{stackify → stackify_apm}/spies/action_dispatch.rb +3 -4
- data/lib/stackify_apm/spies/curb.rb +49 -0
- data/lib/stackify_apm/spies/curb/easy.rb +157 -0
- data/lib/stackify_apm/spies/curb/multi.rb +43 -0
- data/lib/{stackify → stackify_apm}/spies/httpclient.rb +10 -8
- data/lib/{stackify → stackify_apm}/spies/httprb.rb +7 -9
- data/lib/{stackify → stackify_apm}/spies/mongo.rb +5 -3
- data/lib/{stackify → stackify_apm}/spies/net_http.rb +4 -5
- data/lib/{stackify → stackify_apm}/spies/redis.rb +19 -18
- data/lib/stackify_apm/spies/sequel.rb +65 -0
- data/lib/{stackify → stackify_apm}/spies/sinatra.rb +7 -10
- data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +201 -0
- data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +94 -0
- data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +46 -0
- data/lib/stackify_apm/spies/stackify_logger.rb +60 -0
- data/lib/{stackify → stackify_apm}/spies/tilt.rb +3 -3
- data/lib/stackify_apm/stacktrace.rb +18 -0
- data/lib/stackify_apm/stacktrace/frame.rb +47 -0
- data/lib/{stackify → stackify_apm}/stacktrace_builder.rb +10 -11
- data/lib/{stackify → stackify_apm}/subscriber.rb +20 -14
- data/lib/{stackify → stackify_apm}/trace_logger.rb +10 -16
- data/lib/stackify_apm/transaction.rb +127 -0
- data/lib/{stackify → stackify_apm}/util.rb +3 -1
- data/lib/{stackify → stackify_apm}/util/dig.rb +1 -1
- data/lib/{stackify → stackify_apm}/util/inflector.rb +0 -0
- data/lib/{stackify → stackify_apm}/util/inspector.rb +1 -3
- data/lib/stackify_apm/util/lru_cache.rb +49 -0
- data/lib/stackify_apm/util/trace_log_watcher.rb +37 -0
- data/lib/stackify_apm/version.rb +6 -0
- data/lib/{stackify → stackify_apm}/worker.rb +8 -7
- data/lib/stackify_ruby_apm.rb +18 -15
- data/run-test-docker.sh +50 -0
- data/run-test.sh +1 -3
- data/stackify-ruby-apm.gemspec +14 -11
- metadata +86 -59
- data/lib/stackify/context/response.rb +0 -37
- data/lib/stackify/error/exception.rb +0 -36
- data/lib/stackify/error/log.rb +0 -25
- data/lib/stackify/error_builder.rb +0 -65
- data/lib/stackify/middleware.rb +0 -74
- data/lib/stackify/spies/sinatra_activerecord/mysql_adapter.rb +0 -177
- data/lib/stackify/spies/sinatra_activerecord/postgresql_adapter.rb +0 -96
- data/lib/stackify/spies/sinatra_activerecord/sqlite_adapter.rb +0 -48
- data/lib/stackify/stacktrace.rb +0 -19
- data/lib/stackify/stacktrace/frame.rb +0 -50
- data/lib/stackify/transaction.rb +0 -132
- data/lib/stackify/util/lru_cache.rb +0 -49
- 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
|
-
|
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: /
|
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 '
|
6
|
-
require '
|
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 =
|
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 '
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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 '
|
7
|
-
require '
|
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
|
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(
|
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
|
-
|
42
|
-
|
43
|
-
|
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 '
|
12
|
+
require 'stackify_apm/util/inspector'
|
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
|