appsignal 3.4.4 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/.semaphore/semaphore.yml +683 -52
- data/CHANGELOG.md +353 -4
- data/README.md +3 -0
- data/Rakefile +4 -2
- data/appsignal.gemspec +1 -1
- data/build_matrix.yml +27 -13
- data/ext/Rakefile +8 -1
- data/ext/agent.rb +27 -27
- data/ext/appsignal_extension.c +0 -24
- data/ext/base.rb +5 -2
- data/gemfiles/dry-monitor.gemfile +5 -0
- data/gemfiles/rails-7.1.gemfile +7 -0
- data/gemfiles/redis-4.gemfile +5 -0
- data/gemfiles/redis-5.gemfile +6 -0
- data/lib/appsignal/auth_check.rb +1 -1
- data/lib/appsignal/cli/diagnose/paths.rb +33 -10
- data/lib/appsignal/cli/diagnose.rb +15 -1
- data/lib/appsignal/config.rb +72 -7
- data/lib/appsignal/demo.rb +1 -1
- data/lib/appsignal/environment.rb +24 -13
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +18 -0
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +1 -1
- data/lib/appsignal/event_formatter.rb +2 -2
- data/lib/appsignal/extension/jruby.rb +4 -17
- data/lib/appsignal/extension.rb +1 -1
- data/lib/appsignal/heartbeat.rb +71 -0
- data/lib/appsignal/helpers/instrumentation.rb +10 -10
- data/lib/appsignal/helpers/metrics.rb +15 -13
- data/lib/appsignal/hooks/active_job.rb +9 -1
- data/lib/appsignal/hooks/active_support_notifications.rb +18 -9
- data/lib/appsignal/hooks/dry_monitor.rb +20 -0
- data/lib/appsignal/hooks/redis.rb +1 -0
- data/lib/appsignal/hooks/redis_client.rb +28 -0
- data/lib/appsignal/hooks.rb +4 -2
- data/lib/appsignal/integrations/active_support_notifications.rb +26 -0
- data/lib/appsignal/integrations/dry_monitor.rb +22 -0
- data/lib/appsignal/integrations/hanami.rb +1 -1
- data/lib/appsignal/integrations/padrino.rb +1 -1
- data/lib/appsignal/integrations/railtie.rb +28 -6
- data/lib/appsignal/integrations/redis_client.rb +20 -0
- data/lib/appsignal/integrations/sidekiq.rb +2 -2
- data/lib/appsignal/integrations/sinatra.rb +1 -1
- data/lib/appsignal/logger.rb +7 -5
- data/lib/appsignal/minutely.rb +4 -4
- data/lib/appsignal/probes/gvl.rb +1 -1
- data/lib/appsignal/probes/helpers.rb +1 -1
- data/lib/appsignal/probes/mri.rb +1 -1
- data/lib/appsignal/probes/sidekiq.rb +10 -8
- data/lib/appsignal/rack/generic_instrumentation.rb +1 -1
- data/lib/appsignal/rack/rails_instrumentation.rb +2 -2
- data/lib/appsignal/rack/sinatra_instrumentation.rb +5 -4
- data/lib/appsignal/rack/streaming_listener.rb +1 -1
- data/lib/appsignal/span.rb +2 -2
- data/lib/appsignal/transaction.rb +69 -14
- data/lib/appsignal/utils/deprecation_message.rb +2 -2
- data/lib/appsignal/utils/hash_sanitizer.rb +21 -9
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +38 -31
- data/lib/puma/plugin/appsignal.rb +1 -1
- data/resources/cacert.pem +321 -159
- data/spec/lib/appsignal/capistrano2_spec.rb +2 -2
- data/spec/lib/appsignal/capistrano3_spec.rb +2 -2
- data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +11 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +70 -13
- data/spec/lib/appsignal/config_spec.rb +75 -18
- data/spec/lib/appsignal/environment_spec.rb +3 -3
- data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +1 -1
- data/spec/lib/appsignal/event_formatter/rom/sql_formatter_spec.rb +22 -0
- data/spec/lib/appsignal/heartbeat_spec.rb +89 -0
- data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +6 -0
- data/spec/lib/appsignal/hooks/activejob_spec.rb +26 -1
- data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +104 -0
- data/spec/lib/appsignal/hooks/redis_client_spec.rb +238 -0
- data/spec/lib/appsignal/hooks/redis_spec.rb +98 -76
- data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
- data/spec/lib/appsignal/hooks_spec.rb +5 -5
- data/spec/lib/appsignal/integrations/railtie_spec.rb +128 -59
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +20 -15
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
- data/spec/lib/appsignal/minutely_spec.rb +2 -2
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +29 -6
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +1 -1
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +163 -71
- data/spec/lib/appsignal/rack/streaming_listener_spec.rb +1 -0
- data/spec/lib/appsignal/transaction_spec.rb +139 -10
- data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +42 -4
- data/spec/lib/appsignal_spec.rb +63 -61
- data/spec/lib/puma/appsignal_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -7
- data/spec/support/fixtures/projects/valid/config/appsignal.yml +3 -3
- data/spec/support/helpers/config_helpers.rb +6 -2
- data/spec/support/helpers/dependency_helper.rb +13 -1
- data/spec/support/helpers/log_helpers.rb +2 -2
- data/spec/support/helpers/rails_helper.rb +28 -0
- data/spec/support/matchers/have_colorized_text.rb +1 -1
- metadata +19 -5
- data/ext/._appsignal-agent +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Appsignal.
|
3
|
+
Appsignal.internal_logger.debug("Loading Rails (#{Rails.version}) integration")
|
4
4
|
|
5
5
|
require "appsignal/utils/rails_helper"
|
6
6
|
require "appsignal/rack/rails_instrumentation"
|
@@ -36,9 +36,14 @@ module Appsignal
|
|
36
36
|
|
37
37
|
Appsignal.start
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
initialize_error_reporter
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.initialize_error_reporter
|
43
|
+
return unless Appsignal.config[:enable_rails_error_reporter]
|
44
|
+
return unless Rails.respond_to?(:error)
|
45
|
+
|
46
|
+
Rails.error.subscribe(Appsignal::Integrations::RailsErrorReporterSubscriber)
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
@@ -55,9 +60,14 @@ module Appsignal
|
|
55
60
|
return unless handled
|
56
61
|
|
57
62
|
Appsignal.send_error(error) do |transaction|
|
58
|
-
namespace, action_name, tags =
|
63
|
+
namespace, action_name, path, method, params, tags, custom_data =
|
64
|
+
context_for(context.dup)
|
59
65
|
transaction.set_namespace(namespace) if namespace
|
60
66
|
transaction.set_action(action_name) if action_name
|
67
|
+
transaction.set_metadata("path", path)
|
68
|
+
transaction.set_metadata("method", method)
|
69
|
+
transaction.params = params
|
70
|
+
transaction.set_sample_data("custom_data", custom_data) if custom_data
|
61
71
|
|
62
72
|
tags[:severity] = severity
|
63
73
|
tags[:source] = source.to_s if source
|
@@ -69,14 +79,23 @@ module Appsignal
|
|
69
79
|
|
70
80
|
def context_for(context)
|
71
81
|
tags = {}
|
82
|
+
custom_data = nil
|
72
83
|
|
73
84
|
appsignal_context = context.delete(:appsignal)
|
74
85
|
# Fetch the namespace and action name based on the Rails execution
|
75
86
|
# context.
|
76
87
|
controller = context.delete(:controller)
|
88
|
+
path = nil
|
89
|
+
method = nil
|
90
|
+
params = nil
|
77
91
|
if controller
|
78
92
|
namespace = Appsignal::Transaction::HTTP_REQUEST
|
79
93
|
action_name = "#{controller.class.name}##{controller.action_name}"
|
94
|
+
unless controller.request.nil?
|
95
|
+
path = controller.request.path
|
96
|
+
method = controller.request.method
|
97
|
+
params = controller.request.filtered_parameters
|
98
|
+
end
|
80
99
|
end
|
81
100
|
# ActiveJob transaction naming relies on the current AppSignal
|
82
101
|
# transaction namespace and action name copying done after this.
|
@@ -102,10 +121,13 @@ module Appsignal
|
|
102
121
|
|
103
122
|
context_action_name = appsignal_context[:action]
|
104
123
|
action_name = context_action_name if context_action_name
|
124
|
+
|
125
|
+
context_custom_data = appsignal_context[:custom_data]
|
126
|
+
custom_data = context_custom_data if context_custom_data
|
105
127
|
end
|
106
128
|
tags.merge!(context)
|
107
129
|
|
108
|
-
[namespace, action_name, tags]
|
130
|
+
[namespace, action_name, path, method, params, tags, custom_data]
|
109
131
|
end
|
110
132
|
end
|
111
133
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Integrations
|
5
|
+
module RedisClientIntegration
|
6
|
+
def write(command)
|
7
|
+
sanitized_command =
|
8
|
+
if command[0] == :eval
|
9
|
+
"#{command[1]}#{" ?" * (command.size - 3)}"
|
10
|
+
else
|
11
|
+
"#{command[0]}#{" ?" * (command.size - 1)}"
|
12
|
+
end
|
13
|
+
|
14
|
+
Appsignal.instrument "query.redis", @config.id, sanitized_command do
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -9,7 +9,7 @@ module Appsignal
|
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
class SidekiqErrorHandler
|
12
|
-
def call(exception, sidekiq_context)
|
12
|
+
def call(exception, sidekiq_context, _sidekiq_config = nil)
|
13
13
|
transaction =
|
14
14
|
if Appsignal::Transaction.current?
|
15
15
|
Appsignal::Transaction.current
|
@@ -168,7 +168,7 @@ module Appsignal
|
|
168
168
|
# Sidekiq issue #1761: in dev mode, it's possible to have jobs enqueued
|
169
169
|
# which haven't been loaded into memory yet so the YAML can't be
|
170
170
|
# loaded.
|
171
|
-
Appsignal.
|
171
|
+
Appsignal.internal_logger.warn "Unable to load YAML: #{error.message}"
|
172
172
|
default
|
173
173
|
end
|
174
174
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "appsignal"
|
4
4
|
require "appsignal/rack/sinatra_instrumentation"
|
5
5
|
|
6
|
-
Appsignal.
|
6
|
+
Appsignal.internal_logger.debug("Loading Sinatra (#{Sinatra::VERSION}) integration")
|
7
7
|
|
8
8
|
app_settings = ::Sinatra::Application.settings
|
9
9
|
Appsignal.config = Appsignal::Config.new(
|
data/lib/appsignal/logger.rb
CHANGED
@@ -17,6 +17,8 @@ module Appsignal
|
|
17
17
|
FATAL => 7
|
18
18
|
}.freeze
|
19
19
|
|
20
|
+
attr_reader :level
|
21
|
+
|
20
22
|
# Create a new logger instance
|
21
23
|
#
|
22
24
|
# @param group Name of the group for this logger.
|
@@ -62,7 +64,7 @@ module Appsignal
|
|
62
64
|
alias log add
|
63
65
|
|
64
66
|
# Log a debug level message
|
65
|
-
# @param message
|
67
|
+
# @param message Message to log
|
66
68
|
# @param attributes Attributes to tag the log with
|
67
69
|
# @return [void]
|
68
70
|
def debug(message = nil, attributes = {})
|
@@ -75,7 +77,7 @@ module Appsignal
|
|
75
77
|
end
|
76
78
|
|
77
79
|
# Log an info level message
|
78
|
-
# @param message
|
80
|
+
# @param message Message to log
|
79
81
|
# @param attributes Attributes to tag the log with
|
80
82
|
# @return [void]
|
81
83
|
def info(message = nil, attributes = {})
|
@@ -88,7 +90,7 @@ module Appsignal
|
|
88
90
|
end
|
89
91
|
|
90
92
|
# Log a warn level message
|
91
|
-
# @param message
|
93
|
+
# @param message Message to log
|
92
94
|
# @param attributes Attributes to tag the log with
|
93
95
|
# @return [void]
|
94
96
|
def warn(message = nil, attributes = {})
|
@@ -101,7 +103,7 @@ module Appsignal
|
|
101
103
|
end
|
102
104
|
|
103
105
|
# Log an error level message
|
104
|
-
# @param message
|
106
|
+
# @param message Message to log
|
105
107
|
# @param attributes Attributes to tag the log with
|
106
108
|
# @return [void]
|
107
109
|
def error(message = nil, attributes = {})
|
@@ -114,7 +116,7 @@ module Appsignal
|
|
114
116
|
end
|
115
117
|
|
116
118
|
# Log a fatal level message
|
117
|
-
# @param message
|
119
|
+
# @param message Message to log
|
118
120
|
# @param attributes Attributes to tag the log with
|
119
121
|
# @return [void]
|
120
122
|
def fatal(message = nil, attributes = {})
|
data/lib/appsignal/minutely.rb
CHANGED
@@ -108,7 +108,7 @@ module Appsignal
|
|
108
108
|
attr_reader :probes
|
109
109
|
|
110
110
|
def logger
|
111
|
-
Appsignal.
|
111
|
+
Appsignal.internal_logger
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
@@ -132,7 +132,7 @@ module Appsignal
|
|
132
132
|
sleep initial_wait_time
|
133
133
|
initialize_probes
|
134
134
|
loop do
|
135
|
-
logger = Appsignal.
|
135
|
+
logger = Appsignal.internal_logger
|
136
136
|
logger.debug("Gathering minutely metrics with #{probe_instances.count} probes")
|
137
137
|
probe_instances.each do |name, probe|
|
138
138
|
logger.debug("Gathering minutely metrics with '#{name}' probe")
|
@@ -181,13 +181,13 @@ module Appsignal
|
|
181
181
|
klass = instance.class
|
182
182
|
end
|
183
183
|
unless dependencies_present?(klass)
|
184
|
-
Appsignal.
|
184
|
+
Appsignal.internal_logger.debug "Skipping '#{name}' probe, " \
|
185
185
|
"#{klass}.dependency_present? returned falsy"
|
186
186
|
return
|
187
187
|
end
|
188
188
|
probe_instances[name] = instance
|
189
189
|
rescue => error
|
190
|
-
logger = Appsignal.
|
190
|
+
logger = Appsignal.internal_logger
|
191
191
|
logger.error "Error while initializing minutely probe '#{name}': #{error}"
|
192
192
|
logger.debug error.backtrace.join("\n")
|
193
193
|
end
|
data/lib/appsignal/probes/gvl.rb
CHANGED
@@ -22,7 +22,7 @@ module Appsignal
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def initialize(appsignal: Appsignal, gvl_tools: ::GVLTools)
|
25
|
-
Appsignal.
|
25
|
+
Appsignal.internal_logger.debug("Initializing GVL probe")
|
26
26
|
@appsignal = appsignal
|
27
27
|
@gvl_tools = gvl_tools
|
28
28
|
end
|
@@ -47,7 +47,7 @@ module Appsignal
|
|
47
47
|
# Auto detect hostname as fallback. May be inaccurate.
|
48
48
|
@hostname =
|
49
49
|
config[:hostname] || Socket.gethostname
|
50
|
-
Appsignal.
|
50
|
+
Appsignal.internal_logger.debug "Probe helper: Using hostname config " \
|
51
51
|
"option '#{@hostname.inspect}' as hostname"
|
52
52
|
|
53
53
|
@hostname
|
data/lib/appsignal/probes/mri.rb
CHANGED
@@ -11,7 +11,7 @@ module Appsignal
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollection.profiler)
|
14
|
-
Appsignal.
|
14
|
+
Appsignal.internal_logger.debug("Initializing VM probe")
|
15
15
|
@appsignal = appsignal
|
16
16
|
@gc_profiler = gc_profiler
|
17
17
|
end
|
@@ -59,7 +59,7 @@ module Appsignal
|
|
59
59
|
@adapter = is_sidekiq7 ? Sidekiq7Adapter : Sidekiq6Adapter
|
60
60
|
|
61
61
|
config_string = " with config: #{config}" unless config.empty?
|
62
|
-
Appsignal.
|
62
|
+
Appsignal.internal_logger.debug("Initializing Sidekiq probe#{config_string}")
|
63
63
|
require "sidekiq/api"
|
64
64
|
end
|
65
65
|
|
@@ -78,9 +78,9 @@ module Appsignal
|
|
78
78
|
redis_info = adapter.redis_info
|
79
79
|
return unless redis_info
|
80
80
|
|
81
|
-
gauge "connection_count", redis_info
|
82
|
-
gauge "memory_usage", redis_info
|
83
|
-
gauge "memory_usage_rss", redis_info
|
81
|
+
gauge "connection_count", redis_info["connected_clients"]
|
82
|
+
gauge "memory_usage", redis_info["used_memory"]
|
83
|
+
gauge "memory_usage_rss", redis_info["used_memory_rss"]
|
84
84
|
end
|
85
85
|
|
86
86
|
def track_stats
|
@@ -112,6 +112,8 @@ module Appsignal
|
|
112
112
|
|
113
113
|
# Track a gauge metric with the `sidekiq_` prefix
|
114
114
|
def gauge(key, value, tags = {})
|
115
|
+
return if value.nil?
|
116
|
+
|
115
117
|
tags[:hostname] = hostname if hostname
|
116
118
|
Appsignal.set_gauge "sidekiq_#{key}", value, tags
|
117
119
|
end
|
@@ -121,14 +123,14 @@ module Appsignal
|
|
121
123
|
|
122
124
|
if config.key?(:hostname)
|
123
125
|
@hostname = config[:hostname]
|
124
|
-
Appsignal.
|
125
|
-
"option #{@hostname.inspect} as hostname"
|
126
|
+
Appsignal.internal_logger.debug "Sidekiq probe: Using hostname " \
|
127
|
+
"config option #{@hostname.inspect} as hostname"
|
126
128
|
return @hostname
|
127
129
|
end
|
128
130
|
|
129
131
|
host = adapter.hostname
|
130
|
-
Appsignal.
|
131
|
-
"#{host.inspect} as hostname"
|
132
|
+
Appsignal.internal_logger.debug "Sidekiq probe: Using Redis server " \
|
133
|
+
"hostname #{host.inspect} as hostname"
|
132
134
|
@hostname = host
|
133
135
|
end
|
134
136
|
end
|
@@ -7,7 +7,7 @@ module Appsignal
|
|
7
7
|
module Rack
|
8
8
|
class GenericInstrumentation
|
9
9
|
def initialize(app, options = {})
|
10
|
-
Appsignal.
|
10
|
+
Appsignal.internal_logger.debug "Initializing Appsignal::Rack::GenericInstrumentation"
|
11
11
|
@app = app
|
12
12
|
@options = options
|
13
13
|
end
|
@@ -7,7 +7,7 @@ module Appsignal
|
|
7
7
|
module Rack
|
8
8
|
class RailsInstrumentation
|
9
9
|
def initialize(app, options = {})
|
10
|
-
Appsignal.
|
10
|
+
Appsignal.internal_logger.debug "Initializing Appsignal::Rack::RailsInstrumentation"
|
11
11
|
@app = app
|
12
12
|
@options = options
|
13
13
|
end
|
@@ -43,7 +43,7 @@ module Appsignal
|
|
43
43
|
begin
|
44
44
|
transaction.set_metadata("method", request.request_method)
|
45
45
|
rescue => error
|
46
|
-
Appsignal.
|
46
|
+
Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
|
47
47
|
end
|
48
48
|
Appsignal::Transaction.complete_current!
|
49
49
|
end
|
@@ -15,7 +15,7 @@ module Appsignal
|
|
15
15
|
def initialize(app, options = {})
|
16
16
|
@app = app
|
17
17
|
@options = options
|
18
|
-
Appsignal.
|
18
|
+
Appsignal.internal_logger.warn "Please remove Appsignal::Rack::SinatraInstrumentation " \
|
19
19
|
"from your Sinatra::Base class. This is no longer needed."
|
20
20
|
end
|
21
21
|
|
@@ -32,7 +32,7 @@ module Appsignal
|
|
32
32
|
attr_reader :raise_errors_on
|
33
33
|
|
34
34
|
def initialize(app, options = {})
|
35
|
-
Appsignal.
|
35
|
+
Appsignal.internal_logger.debug "Initializing Appsignal::Rack::SinatraBaseInstrumentation"
|
36
36
|
@app = app
|
37
37
|
@options = options
|
38
38
|
@raise_errors_on = raise_errors?(@app)
|
@@ -47,13 +47,14 @@ module Appsignal
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def call_with_appsignal_monitoring(env)
|
50
|
-
|
50
|
+
options = { :force => @options.include?(:force) && @options[:force] }
|
51
|
+
options.merge!(:params_method => @options[:params_method]) if @options[:params_method]
|
51
52
|
request = @options.fetch(:request_class, Sinatra::Request).new(env)
|
52
53
|
transaction = Appsignal::Transaction.create(
|
53
54
|
SecureRandom.uuid,
|
54
55
|
Appsignal::Transaction::HTTP_REQUEST,
|
55
56
|
request,
|
56
|
-
|
57
|
+
options
|
57
58
|
)
|
58
59
|
begin
|
59
60
|
Appsignal.instrument("process_action.sinatra") do
|
@@ -7,7 +7,7 @@ module Appsignal
|
|
7
7
|
# @api private
|
8
8
|
class StreamingListener
|
9
9
|
def initialize(app, options = {})
|
10
|
-
Appsignal.
|
10
|
+
Appsignal.internal_logger.debug "Initializing Appsignal::Rack::StreamingListener"
|
11
11
|
@app = app
|
12
12
|
@options = options
|
13
13
|
end
|
data/lib/appsignal/span.rb
CHANGED
@@ -16,8 +16,8 @@ module Appsignal
|
|
16
16
|
|
17
17
|
def add_error(error)
|
18
18
|
unless error.is_a?(Exception)
|
19
|
-
Appsignal.
|
20
|
-
"The given value is not an exception: #{error.inspect}"
|
19
|
+
Appsignal.internal_logger.error "Appsignal::Span#add_error: Cannot " \
|
20
|
+
"add error. The given value is not an exception: #{error.inspect}"
|
21
21
|
return
|
22
22
|
end
|
23
23
|
return unless error
|
@@ -12,6 +12,7 @@ module Appsignal
|
|
12
12
|
ALLOWED_TAG_KEY_TYPES = [Symbol, String].freeze
|
13
13
|
ALLOWED_TAG_VALUE_TYPES = [Symbol, String, Integer].freeze
|
14
14
|
BREADCRUMB_LIMIT = 20
|
15
|
+
ERROR_CAUSES_LIMIT = 10
|
15
16
|
|
16
17
|
class << self
|
17
18
|
def create(id, namespace, request, options = {})
|
@@ -25,7 +26,7 @@ module Appsignal
|
|
25
26
|
Appsignal::Transaction.new(id, namespace, request, options)
|
26
27
|
else
|
27
28
|
# Otherwise, log the issue about trying to start another transaction
|
28
|
-
Appsignal.
|
29
|
+
Appsignal.internal_logger.warn_once_then_debug(
|
29
30
|
:transaction_id,
|
30
31
|
"Trying to start new transaction with id " \
|
31
32
|
"'#{id}', but a transaction with id '#{current.transaction_id}' " \
|
@@ -58,7 +59,7 @@ module Appsignal
|
|
58
59
|
def complete_current!
|
59
60
|
current.complete
|
60
61
|
rescue => e
|
61
|
-
Appsignal.
|
62
|
+
Appsignal.internal_logger.error(
|
62
63
|
"Failed to complete transaction ##{current.transaction_id}. #{e.message}"
|
63
64
|
)
|
64
65
|
ensure
|
@@ -113,7 +114,7 @@ module Appsignal
|
|
113
114
|
|
114
115
|
def complete
|
115
116
|
if discarded?
|
116
|
-
Appsignal.
|
117
|
+
Appsignal.internal_logger.debug "Skipping transaction '#{transaction_id}' " \
|
117
118
|
"because it was manually discarded."
|
118
119
|
return
|
119
120
|
end
|
@@ -186,6 +187,12 @@ module Appsignal
|
|
186
187
|
# @see https://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html
|
187
188
|
# Breadcrumb reference
|
188
189
|
def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
|
190
|
+
unless metadata.is_a? Hash
|
191
|
+
Appsignal.internal_logger.error "add_breadcrumb: Cannot add breadcrumb. " \
|
192
|
+
"The given metadata argument is not a Hash."
|
193
|
+
return
|
194
|
+
end
|
195
|
+
|
189
196
|
@breadcrumbs.push(
|
190
197
|
:time => time.to_i,
|
191
198
|
:category => category,
|
@@ -280,7 +287,7 @@ module Appsignal
|
|
280
287
|
|
281
288
|
@ext.set_queue_start(start)
|
282
289
|
rescue RangeError
|
283
|
-
Appsignal.
|
290
|
+
Appsignal.internal_logger.warn("Queue start value #{start} is too big")
|
284
291
|
end
|
285
292
|
|
286
293
|
# Set the queue time based on the HTTP header or `:queue_start` env key
|
@@ -308,12 +315,20 @@ module Appsignal
|
|
308
315
|
|
309
316
|
def set_metadata(key, value)
|
310
317
|
return unless key && value
|
318
|
+
return if Appsignal.config[:filter_metadata].include?(key.to_s)
|
311
319
|
|
312
320
|
@ext.set_metadata(key, value)
|
313
321
|
end
|
314
322
|
|
315
323
|
def set_sample_data(key, data)
|
316
|
-
return unless key && data
|
324
|
+
return unless key && data
|
325
|
+
|
326
|
+
if !data.is_a?(Array) && !data.is_a?(Hash)
|
327
|
+
Appsignal.internal_logger.error(
|
328
|
+
"Invalid sample data for '#{key}'. Value is not an Array or Hash: '#{data.inspect}'"
|
329
|
+
)
|
330
|
+
return
|
331
|
+
end
|
317
332
|
|
318
333
|
@ext.set_sample_data(
|
319
334
|
key.to_s,
|
@@ -322,11 +337,11 @@ module Appsignal
|
|
322
337
|
rescue RuntimeError => e
|
323
338
|
begin
|
324
339
|
inspected_data = data.inspect
|
325
|
-
Appsignal.
|
340
|
+
Appsignal.internal_logger.error(
|
326
341
|
"Error generating data (#{e.class}: #{e.message}) for '#{inspected_data}'"
|
327
342
|
)
|
328
343
|
rescue => e
|
329
|
-
Appsignal.
|
344
|
+
Appsignal.internal_logger.error(
|
330
345
|
"Error generating data (#{e.class}: #{e.message}). Can't inspect data."
|
331
346
|
)
|
332
347
|
end
|
@@ -337,7 +352,7 @@ module Appsignal
|
|
337
352
|
:params => sanitized_params,
|
338
353
|
:environment => sanitized_environment,
|
339
354
|
:session_data => sanitized_session_data,
|
340
|
-
:metadata =>
|
355
|
+
:metadata => sanitized_metadata,
|
341
356
|
:tags => sanitized_tags,
|
342
357
|
:breadcrumbs => breadcrumbs
|
343
358
|
}.each do |key, data|
|
@@ -347,7 +362,7 @@ module Appsignal
|
|
347
362
|
|
348
363
|
def set_error(error)
|
349
364
|
unless error.is_a?(Exception)
|
350
|
-
Appsignal.
|
365
|
+
Appsignal.internal_logger.error "Appsignal::Transaction#set_error: Cannot set error. " \
|
351
366
|
"The given value is not an exception: #{error.inspect}"
|
352
367
|
return
|
353
368
|
end
|
@@ -360,6 +375,41 @@ module Appsignal
|
|
360
375
|
cleaned_error_message(error),
|
361
376
|
backtrace ? Appsignal::Utils::Data.generate(backtrace) : Appsignal::Extension.data_array_new
|
362
377
|
)
|
378
|
+
|
379
|
+
root_cause_missing = false
|
380
|
+
|
381
|
+
causes = []
|
382
|
+
while error
|
383
|
+
error = error.cause
|
384
|
+
|
385
|
+
break unless error
|
386
|
+
|
387
|
+
if causes.length >= ERROR_CAUSES_LIMIT
|
388
|
+
Appsignal.internal_logger.debug "Appsignal::Transaction#set_error: Error has more " \
|
389
|
+
"than #{ERROR_CAUSES_LIMIT} error causes. Only the first #{ERROR_CAUSES_LIMIT} " \
|
390
|
+
"will be reported."
|
391
|
+
root_cause_missing = true
|
392
|
+
break
|
393
|
+
end
|
394
|
+
|
395
|
+
causes << error
|
396
|
+
end
|
397
|
+
|
398
|
+
return if causes.empty?
|
399
|
+
|
400
|
+
causes_sample_data = causes.map do |e|
|
401
|
+
{
|
402
|
+
:name => e.class.name,
|
403
|
+
:message => cleaned_error_message(e)
|
404
|
+
}
|
405
|
+
end
|
406
|
+
|
407
|
+
causes_sample_data.last[:is_root_cause] = false if root_cause_missing
|
408
|
+
|
409
|
+
set_sample_data(
|
410
|
+
"error_causes",
|
411
|
+
causes_sample_data
|
412
|
+
)
|
363
413
|
end
|
364
414
|
alias_method :add_exception, :set_error
|
365
415
|
|
@@ -479,7 +529,7 @@ module Appsignal
|
|
479
529
|
request.send options[:params_method]
|
480
530
|
rescue => e
|
481
531
|
# Getting params from the request has been know to fail.
|
482
|
-
Appsignal.
|
532
|
+
Appsignal.internal_logger.debug "Exception while getting params: #{e}"
|
483
533
|
nil
|
484
534
|
end
|
485
535
|
end
|
@@ -522,12 +572,17 @@ module Appsignal
|
|
522
572
|
)
|
523
573
|
end
|
524
574
|
|
525
|
-
# Returns metadata from the
|
575
|
+
# Returns sanitized metadata set by {#set_metadata} and from the
|
576
|
+
# {#environment}.
|
526
577
|
#
|
527
|
-
# @return [nil] if no `:metadata` key is present in the {#environment}.
|
528
578
|
# @return [Hash<String, Object>]
|
529
|
-
def
|
530
|
-
environment[:metadata]
|
579
|
+
def sanitized_metadata
|
580
|
+
metadata = environment[:metadata]
|
581
|
+
return unless metadata
|
582
|
+
|
583
|
+
metadata
|
584
|
+
.transform_keys(&:to_s)
|
585
|
+
.reject { |key, _value| Appsignal.config[:filter_metadata].include?(key) }
|
531
586
|
end
|
532
587
|
|
533
588
|
# Returns the environment for a transaction.
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module Appsignal
|
4
4
|
module Utils
|
5
5
|
module DeprecationMessage
|
6
|
-
def self.message(message, logger = Appsignal.
|
6
|
+
def self.message(message, logger = Appsignal.internal_logger)
|
7
7
|
Kernel.warn "appsignal WARNING: #{message}"
|
8
8
|
logger.warn message
|
9
9
|
end
|
10
10
|
|
11
|
-
def deprecation_message(message, logger = Appsignal.
|
11
|
+
def deprecation_message(message, logger = Appsignal.internal_logger)
|
12
12
|
Appsignal::Utils::DeprecationMessage.message(message, logger)
|
13
13
|
end
|
14
14
|
end
|
@@ -5,20 +5,21 @@ module Appsignal
|
|
5
5
|
# @api private
|
6
6
|
class HashSanitizer
|
7
7
|
FILTERED = "[FILTERED]"
|
8
|
+
RECURSIVE = "[RECURSIVE VALUE]"
|
8
9
|
|
9
10
|
class << self
|
10
11
|
def sanitize(value, filter_keys = [])
|
11
|
-
sanitize_value(value, filter_keys)
|
12
|
+
sanitize_value(value, filter_keys, [])
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
|
-
def sanitize_value(value, filter_keys)
|
17
|
+
def sanitize_value(value, filter_keys, seen)
|
17
18
|
case value
|
18
19
|
when Hash
|
19
|
-
sanitize_hash(value, filter_keys)
|
20
|
+
sanitize_hash(value, filter_keys, seen)
|
20
21
|
when Array
|
21
|
-
sanitize_array(value, filter_keys)
|
22
|
+
sanitize_array(value, filter_keys, seen)
|
22
23
|
when TrueClass, FalseClass, NilClass, Integer, String, Symbol, Float
|
23
24
|
unmodified(value)
|
24
25
|
else
|
@@ -26,23 +27,34 @@ module Appsignal
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
|
-
def sanitize_hash(source, filter_keys)
|
30
|
+
def sanitize_hash(source, filter_keys, seen)
|
31
|
+
seen = seen.clone << source.object_id
|
32
|
+
|
30
33
|
{}.tap do |hash|
|
31
34
|
source.each_pair do |key, value|
|
32
35
|
hash[key] =
|
33
|
-
if
|
36
|
+
if seen.include?(value.object_id)
|
37
|
+
RECURSIVE
|
38
|
+
elsif filter_keys.include?(key.to_s)
|
34
39
|
FILTERED
|
35
40
|
else
|
36
|
-
sanitize_value(value, filter_keys)
|
41
|
+
sanitize_value(value, filter_keys, seen)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
42
|
-
def sanitize_array(source, filter_keys)
|
47
|
+
def sanitize_array(source, filter_keys, seen)
|
48
|
+
seen = seen.clone << source.object_id
|
49
|
+
|
43
50
|
[].tap do |array|
|
44
51
|
source.each_with_index do |item, index|
|
45
|
-
array[index] =
|
52
|
+
array[index] =
|
53
|
+
if seen.include?(item.object_id)
|
54
|
+
RECURSIVE
|
55
|
+
else
|
56
|
+
sanitize_value(item, filter_keys, seen)
|
57
|
+
end
|
46
58
|
end
|
47
59
|
end
|
48
60
|
end
|
data/lib/appsignal/version.rb
CHANGED