stackify-ruby-apm 1.10.0 → 1.12.3
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.
- checksums.yaml +4 -4
- data/lib/stackify_apm/agent.rb +0 -2
- data/lib/stackify_apm/config.rb +33 -5
- data/lib/stackify_apm/context.rb +4 -1
- data/lib/stackify_apm/context/prefix.rb +30 -0
- data/lib/stackify_apm/context/request/headers.rb +30 -0
- data/lib/stackify_apm/context_builder.rb +1 -0
- data/lib/stackify_apm/helper/database_helper.rb +39 -1
- data/lib/stackify_apm/instrumenter_helper.rb +90 -4
- data/lib/stackify_apm/logger/log_device.rb +7 -5
- data/lib/stackify_apm/logger/logger_high_version.rb +5 -1
- data/lib/stackify_apm/logger/logger_lower_version.rb +5 -1
- data/lib/stackify_apm/middleware.rb +21 -3
- data/lib/stackify_apm/normalizers/active_record.rb +16 -8
- data/lib/stackify_apm/root_info.rb +7 -1
- data/lib/stackify_apm/serializers/transactions.rb +5 -0
- data/lib/stackify_apm/span/context.rb +39 -1
- data/lib/stackify_apm/spies.rb +4 -2
- data/lib/stackify_apm/spies/action_dispatch.rb +6 -1
- data/lib/stackify_apm/spies/curb.rb +41 -20
- data/lib/stackify_apm/spies/curb/easy.rb +220 -92
- data/lib/stackify_apm/spies/curb/multi.rb +26 -12
- data/lib/stackify_apm/spies/custom_instrumenter.rb +25 -4
- data/lib/stackify_apm/spies/delayed_job.rb +49 -0
- data/lib/stackify_apm/spies/httparty.rb +45 -24
- data/lib/stackify_apm/spies/httpclient.rb +41 -20
- data/lib/stackify_apm/spies/httprb.rb +39 -18
- data/lib/stackify_apm/spies/log4r.rb +59 -0
- data/lib/stackify_apm/spies/logger.rb +116 -0
- data/lib/stackify_apm/spies/logging.rb +65 -0
- data/lib/stackify_apm/spies/net_http.rb +38 -20
- data/lib/stackify_apm/spies/redis.rb +36 -30
- data/lib/stackify_apm/spies/sequel.rb +28 -11
- data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +27 -25
- data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +27 -24
- data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +18 -8
- data/lib/stackify_apm/spies/stackify_logger.rb +28 -16
- data/lib/stackify_apm/spies/yell.rb +64 -0
- data/lib/stackify_apm/util.rb +10 -9
- data/lib/stackify_apm/version.rb +1 -1
- data/stackify-ruby-apm.gemspec +1 -0
- metadata +23 -2
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Monkey patch for the Logging::Logger class for logging log messages.
|
5
|
+
#
|
6
|
+
module StackifyRubyAPM
|
7
|
+
module Spies
|
8
|
+
class LoggingSpy
|
9
|
+
LVL_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY).freeze
|
10
|
+
|
11
|
+
def install
|
12
|
+
Logging::Logger.class_eval do
|
13
|
+
alias_method 'log_event_without_apm', 'log_event'
|
14
|
+
|
15
|
+
# Log message formatted to an event object
|
16
|
+
def log_event(event)
|
17
|
+
return log_event_without_apm(event) unless StackifyRubyAPM.current_transaction
|
18
|
+
|
19
|
+
begin
|
20
|
+
name = 'logging'
|
21
|
+
type = 'ext.logging'
|
22
|
+
log_message = ''
|
23
|
+
exception = nil
|
24
|
+
|
25
|
+
obj = event.data
|
26
|
+
|
27
|
+
case obj
|
28
|
+
when String;
|
29
|
+
log_message = obj
|
30
|
+
when Exception
|
31
|
+
log_message = obj.message
|
32
|
+
exception = "(#{ obj.class.name })\n#{ obj.backtrace.join("\n") if obj.backtrace }"
|
33
|
+
when nil;
|
34
|
+
log_message = "(#{obj.class.name}) nil"
|
35
|
+
else
|
36
|
+
log_message = obj.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
ctx = Span::Context.new(
|
40
|
+
CATEGORY: 'Log',
|
41
|
+
SUBCATEGORY: 'Logging',
|
42
|
+
LEVEL: LVL_LABEL[event.level] || 'ANY',
|
43
|
+
MESSAGE: log_message
|
44
|
+
)
|
45
|
+
|
46
|
+
if exception
|
47
|
+
ctx.EXCEPTION = exception
|
48
|
+
end
|
49
|
+
rescue Exception => e
|
50
|
+
StackifyRubyAPM.agent.error "[LoggingSpy] Error: creating span context."
|
51
|
+
StackifyRubyAPM.agent.error "[LoggingSpy] #{e.inspect}"
|
52
|
+
return log_event_without_apm(event)
|
53
|
+
end
|
54
|
+
|
55
|
+
StackifyRubyAPM.span name, type, context: ctx do
|
56
|
+
log_event_without_apm(event)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
register 'Logging::Logger', 'logging', LoggingSpy.new
|
64
|
+
end
|
65
|
+
end
|
@@ -15,28 +15,34 @@ module StackifyRubyAPM
|
|
15
15
|
return request_without_apm(req, body, &block) unless StackifyRubyAPM.current_transaction
|
16
16
|
return request_without_apm(req, body, &block) if started? && req['span']
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
begin
|
19
|
+
# Data configuration
|
20
|
+
#
|
21
|
+
host, = req['host'] && req['host'].split(':')
|
22
|
+
method = req.method
|
22
23
|
|
23
|
-
|
24
|
+
host ||= address
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
# For Ruby version 2.2.x, 2.3.x, 2.4.x we need to parse it and get the <scheme> & <host>/<path>
|
27
|
+
updated_uri = req.uri.scheme + '://' + req.uri.host + req.uri.path if defined?(req.uri.host)
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
name = "#{method} #{host}"
|
30
|
+
type = "ext.net_http.#{method}"
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
# Builds span context
|
33
|
+
#
|
34
|
+
ctx = Span::Context.new(
|
35
|
+
CATEGORY: 'Web External',
|
36
|
+
SUBCATEGORY: 'Execute',
|
37
|
+
URL: req.uri.nil? ? host : updated_uri,
|
38
|
+
STATUS: '',
|
39
|
+
METHOD: method
|
40
|
+
)
|
41
|
+
rescue Exception => e
|
42
|
+
StackifyRubyAPM.agent.error "[NetHTTPSpy] Error: creating span context."
|
43
|
+
StackifyRubyAPM.agent.error "[NetHTTPSpy] #{e.inspect}"
|
44
|
+
return request_without_apm(req, body, &block)
|
45
|
+
end
|
40
46
|
|
41
47
|
# Creates new span from HTTP result
|
42
48
|
#
|
@@ -45,8 +51,20 @@ module StackifyRubyAPM
|
|
45
51
|
#
|
46
52
|
req['span'] = true
|
47
53
|
result = request_without_apm(req, body, &block)
|
48
|
-
|
49
|
-
|
54
|
+
begin
|
55
|
+
status_code = result.code.to_i
|
56
|
+
ctx.update_status(status_code)
|
57
|
+
|
58
|
+
if StackifyRubyAPM.agent.config.prefix_enabled
|
59
|
+
ctx.update_request_body(req.body || "")
|
60
|
+
ctx.update_request_headers(req.each_header || Hash.new)
|
61
|
+
ctx.update_response_body(result.body || "")
|
62
|
+
ctx.update_response_headers(result.each_header || Hash.new)
|
63
|
+
end
|
64
|
+
rescue Exception => e
|
65
|
+
StackifyRubyAPM.agent.error '[NetHTTPSpy] Error: getting status code or updating request/response context.'
|
66
|
+
StackifyRubyAPM.agent.error "[NetHTTPSpy] #{e.inspect}"
|
67
|
+
end
|
50
68
|
return result
|
51
69
|
end
|
52
70
|
end
|
@@ -14,41 +14,47 @@ module StackifyRubyAPM
|
|
14
14
|
alias_method 'call_without_apm', 'call'
|
15
15
|
|
16
16
|
def call(command, &block)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
begin
|
18
|
+
name = command[0].upcase.to_s
|
19
|
+
type = 'db.redis'
|
20
|
+
redis_details = command[1].is_a?(String) ? command[1].split(':') : []
|
21
|
+
redis_nspace = !redis_details.blank? ? redis_details[0] : ''
|
22
|
+
redis_key = ''
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
# Checks CACHEKEY value
|
25
|
+
if !redis_details.blank? && redis_details[1]
|
26
|
+
# Initially sets the CACHEKEY value
|
27
|
+
args = redis_details[1].split('/')
|
28
|
+
redis_key = args[0]
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
# If command has passed __method__, it will be included in the CACHEKEY value
|
31
|
+
# Possible formats:
|
32
|
+
# `<namespace:key/method_name/expires_in=300/ttl=60/>`
|
33
|
+
# `<namespace:key/expires_in=300/ttl=60/>`
|
34
|
+
redis_key = args[0..1].join('/') if args.length > 1 && !args[1].include?('=')
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
+
return call_without_apm(command, &block) if command[0] == :auth
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
context = {
|
40
|
+
PROVIDER: 'Redis',
|
41
|
+
CATEGORY: 'Cache',
|
42
|
+
SUBCATEGORY: 'Execute',
|
43
|
+
COMPONENT_CATEGORY: 'Cache',
|
44
|
+
COMPONENT_DETAIL: 'Execute',
|
45
|
+
THREAD_ID: Thread.current.object_id,
|
46
|
+
OPERATION: name
|
47
|
+
}.tap do |hash|
|
48
|
+
hash[:CACHEKEY] = redis_key unless redis_key.empty?
|
49
|
+
hash[:CACHENAME] = redis_nspace unless redis_nspace.empty?
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
+
ctx = Span::Context.new(context)
|
53
|
+
rescue Exception => e
|
54
|
+
StackifyRubyAPM.agent.error "[RedisSpy] Error: creating span context."
|
55
|
+
StackifyRubyAPM.agent.error "[RedisSpy] #{e.inspect}"
|
56
|
+
return call_without_apm(command, &block)
|
57
|
+
end
|
52
58
|
|
53
59
|
StackifyRubyAPM.span name, type, context: ctx do
|
54
60
|
call_without_apm(command, &block)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'stackify_apm/helper/database_helper'
|
4
|
+
|
3
5
|
#
|
4
6
|
# This class monkey patch the sequel.rb gem with the request method.
|
5
7
|
#
|
@@ -33,17 +35,12 @@ module StackifyRubyAPM
|
|
33
35
|
name = summarize sql
|
34
36
|
db_adapter = ''
|
35
37
|
db_adapter = args[0].class.to_s.gsub('::Database', '').downcase if args[0]
|
38
|
+
payload = {sql: sql, db_adapter: db_adapter, binds: defined?(args[1]) ? args[1]: []}
|
39
|
+
statement = query_variables(payload)
|
40
|
+
check_prepared_stmt(statement, payload)
|
41
|
+
ctx = Span::Context.new(statement)
|
36
42
|
|
37
|
-
context
|
38
|
-
CATEGORY: 'Database',
|
39
|
-
SUBCATEGORY: 'Execute',
|
40
|
-
COMPONENT_CATEGORY: 'DB Query',
|
41
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
42
|
-
SQL: sql,
|
43
|
-
PROVIDER: get_profiler(db_adapter)
|
44
|
-
)
|
45
|
-
|
46
|
-
StackifyRubyAPM.span(name, TYPE, context: context, &block)
|
43
|
+
StackifyRubyAPM.span(name, TYPE, context: ctx, &block)
|
47
44
|
end
|
48
45
|
|
49
46
|
# rubocop:disable Lint/UselessAssignment
|
@@ -55,7 +52,27 @@ module StackifyRubyAPM
|
|
55
52
|
end
|
56
53
|
end || DEFAULT
|
57
54
|
end
|
58
|
-
|
55
|
+
|
56
|
+
def query_variables(payload)
|
57
|
+
props = get_common_db_properties
|
58
|
+
props[:PROVIDER] = get_profiler(payload[:db_adapter])
|
59
|
+
props[:SQL] = payload[:sql]
|
60
|
+
props
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_prepared_stmt(statement, payload)
|
64
|
+
if StackifyRubyAPM.agent.config.prefix_enabled
|
65
|
+
case get_profiler(payload[:db_adapter])
|
66
|
+
when 'generic', 'mysql', 'sqlite', 'oracle', 'db2'
|
67
|
+
# We check the placeholder of sequel mysql by ? and sometimes sequel sqlite is using : or ?
|
68
|
+
if check_prepared_stmt_by_placeholder(payload[:sql].include?('?'), statement, payload)
|
69
|
+
else check_prepared_stmt_by_placeholder(payload[:sql].include?(':'), statement, payload)
|
70
|
+
end
|
71
|
+
when 'postgresql'
|
72
|
+
check_prepared_stmt_by_placeholder(!!payload[:sql].match(/\$\d/), statement, payload)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
59
76
|
end
|
60
77
|
# rubocop:enable Lint/UselessAssignment
|
61
78
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
|
4
4
|
|
5
|
+
require 'stackify_apm/helper/database_helper'
|
6
|
+
|
5
7
|
module StackifyRubyAPM
|
6
8
|
# @api private
|
7
9
|
module Spies
|
@@ -22,14 +24,10 @@ module StackifyRubyAPM
|
|
22
24
|
exec_update_without_apm(sql, name, binds)
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
30
|
-
SQL: sql,
|
31
|
-
PROVIDER: 'mysql'
|
32
|
-
)
|
27
|
+
payload = {sql: sql, binds: binds}
|
28
|
+
statement = query_variables(payload)
|
29
|
+
check_prepared_stmt(statement, payload)
|
30
|
+
ctx = Span::Context.new(statement)
|
33
31
|
|
34
32
|
result = exec_update_without_apm(sql, name, binds)
|
35
33
|
|
@@ -45,14 +43,10 @@ module StackifyRubyAPM
|
|
45
43
|
exec_delete_without_apm(sql, name, binds)
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
53
|
-
SQL: sql,
|
54
|
-
PROVIDER: 'mysql'
|
55
|
-
)
|
46
|
+
payload = {sql: sql, binds: binds}
|
47
|
+
statement = query_variables(payload)
|
48
|
+
check_prepared_stmt(statement, payload)
|
49
|
+
ctx = Span::Context.new(statement)
|
56
50
|
|
57
51
|
result = exec_delete_without_apm(sql, name, binds)
|
58
52
|
|
@@ -69,22 +63,30 @@ module StackifyRubyAPM
|
|
69
63
|
exec_query_without_apm(sql, name, binds)
|
70
64
|
end
|
71
65
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
77
|
-
SQL: sql,
|
78
|
-
PROVIDER: 'mysql'
|
79
|
-
)
|
66
|
+
payload = {sql: sql, binds: binds}
|
67
|
+
statement = query_variables(payload)
|
68
|
+
check_prepared_stmt(statement, payload)
|
69
|
+
ctx = Span::Context.new(statement)
|
80
70
|
|
81
71
|
result = exec_query_without_apm(sql, name, binds)
|
82
|
-
|
83
72
|
StackifyRubyAPM.span name, TYPE, context: ctx do
|
84
73
|
return result
|
85
74
|
end
|
86
75
|
end
|
87
76
|
# rubocop:enable Lint/UnusedMethodArgument
|
77
|
+
|
78
|
+
def query_variables(payload)
|
79
|
+
props = get_common_db_properties
|
80
|
+
props[:PROVIDER] = 'mysql'
|
81
|
+
props[:SQL] = payload[:sql]
|
82
|
+
props
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_prepared_stmt(statement, payload)
|
86
|
+
if StackifyRubyAPM.agent.config.prefix_enabled
|
87
|
+
check_prepared_stmt_by_placeholder(payload[:sql].include?('?'), statement, payload)
|
88
|
+
end
|
89
|
+
end
|
88
90
|
end
|
89
91
|
end
|
90
92
|
else
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
|
4
4
|
|
5
|
+
require 'stackify_apm/helper/database_helper'
|
6
|
+
|
5
7
|
module StackifyRubyAPM
|
6
8
|
# @api private
|
7
9
|
module Spies
|
@@ -22,15 +24,11 @@ module StackifyRubyAPM
|
|
22
24
|
exec_update_without_apm(sql, name, binds)
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
COMPONENT_CATEGORY: 'DB Query',
|
29
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
30
|
-
SQL: sql,
|
31
|
-
PROVIDER: 'postgresql'
|
32
|
-
)
|
27
|
+
payload = {sql: sql, binds: binds}
|
28
|
+
statement = query_variables(payload)
|
29
|
+
check_prepared_stmt(statement, payload)
|
33
30
|
|
31
|
+
ctx = Span::Context.new(statement)
|
34
32
|
result = exec_update_without_apm(sql, name, binds)
|
35
33
|
|
36
34
|
StackifyRubyAPM.span name, TYPE, context: ctx do
|
@@ -45,15 +43,11 @@ module StackifyRubyAPM
|
|
45
43
|
exec_delete_without_apm(sql, name, binds)
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
COMPONENT_CATEGORY: 'DB Query',
|
52
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
53
|
-
SQL: sql,
|
54
|
-
PROVIDER: 'postgresql'
|
55
|
-
)
|
46
|
+
payload = {sql: sql, binds: binds}
|
47
|
+
statement = query_variables(payload)
|
48
|
+
check_prepared_stmt(statement, payload)
|
56
49
|
|
50
|
+
ctx = Span::Context.new(statement)
|
57
51
|
result = exec_delete_without_apm(sql, name, binds)
|
58
52
|
|
59
53
|
StackifyRubyAPM.span name, TYPE, context: ctx do
|
@@ -69,15 +63,11 @@ module StackifyRubyAPM
|
|
69
63
|
exec_query_without_apm(sql, name, binds)
|
70
64
|
end
|
71
65
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
COMPONENT_CATEGORY: 'DB Query',
|
76
|
-
COMPONENT_DETAIL: 'Execute SQL Query',
|
77
|
-
SQL: sql,
|
78
|
-
PROVIDER: 'postgresql'
|
79
|
-
)
|
66
|
+
payload = {sql: sql, binds: binds}
|
67
|
+
statement = query_variables(payload)
|
68
|
+
check_prepared_stmt(statement, payload)
|
80
69
|
|
70
|
+
ctx = Span::Context.new(statement)
|
81
71
|
result = exec_query_without_apm(sql, name, binds)
|
82
72
|
|
83
73
|
StackifyRubyAPM.span name, TYPE, context: ctx do
|
@@ -85,6 +75,19 @@ module StackifyRubyAPM
|
|
85
75
|
end
|
86
76
|
end
|
87
77
|
# rubocop:enable Lint/UnusedMethodArgument
|
78
|
+
|
79
|
+
def query_variables(payload)
|
80
|
+
props = get_common_db_properties
|
81
|
+
props[:PROVIDER] = 'postgresql'
|
82
|
+
props[:SQL] = payload[:sql]
|
83
|
+
props
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_prepared_stmt(statement, payload)
|
87
|
+
if StackifyRubyAPM.agent.config.prefix_enabled
|
88
|
+
check_prepared_stmt_by_placeholder(!!payload[:sql].match(/\$\d/), statement, payload)
|
89
|
+
end
|
90
|
+
end
|
88
91
|
end
|
89
92
|
end
|
90
93
|
end
|