appsignal 3.5.4-java → 3.5.6-java
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/.semaphore/semaphore.yml +147 -9
- data/CHANGELOG.md +23 -0
- data/README.md +2 -0
- data/build_matrix.yml +5 -9
- data/ext/Rakefile +7 -1
- data/ext/agent.rb +27 -27
- data/gemfiles/redis-4.gemfile +5 -0
- data/gemfiles/redis-5.gemfile +6 -0
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/config.rb +10 -5
- data/lib/appsignal/demo.rb +1 -1
- data/lib/appsignal/environment.rb +24 -13
- data/lib/appsignal/event_formatter.rb +1 -1
- data/lib/appsignal/extension/jruby.rb +4 -3
- data/lib/appsignal/extension.rb +1 -1
- data/lib/appsignal/helpers/instrumentation.rb +7 -7
- data/lib/appsignal/helpers/metrics.rb +3 -3
- data/lib/appsignal/hooks/redis.rb +1 -0
- data/lib/appsignal/hooks/redis_client.rb +27 -0
- data/lib/appsignal/hooks.rb +3 -2
- data/lib/appsignal/integrations/hanami.rb +1 -1
- data/lib/appsignal/integrations/padrino.rb +1 -1
- data/lib/appsignal/integrations/railtie.rb +1 -1
- data/lib/appsignal/integrations/redis_client.rb +20 -0
- data/lib/appsignal/integrations/sidekiq.rb +1 -1
- data/lib/appsignal/integrations/sinatra.rb +1 -1
- 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 +5 -5
- 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 +2 -2
- data/lib/appsignal/rack/streaming_listener.rb +1 -1
- data/lib/appsignal/span.rb +2 -2
- data/lib/appsignal/transaction.rb +11 -11
- data/lib/appsignal/utils/deprecation_message.rb +2 -2
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +37 -31
- data/spec/lib/appsignal/config_spec.rb +2 -2
- data/spec/lib/appsignal/hooks/activejob_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/redis_client_spec.rb +222 -0
- data/spec/lib/appsignal/hooks/redis_spec.rb +98 -76
- data/spec/lib/appsignal/hooks_spec.rb +4 -4
- data/spec/lib/appsignal/integrations/railtie_spec.rb +2 -2
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +3 -3
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
- data/spec/lib/appsignal/minutely_spec.rb +2 -2
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +1 -1
- data/spec/lib/appsignal/transaction_spec.rb +4 -4
- data/spec/lib/appsignal_spec.rb +34 -32
- data/spec/spec_helper.rb +1 -1
- 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 +9 -1
- data/spec/support/helpers/log_helpers.rb +2 -2
- metadata +8 -3
data/lib/appsignal.rb
CHANGED
@@ -46,7 +46,11 @@ module Appsignal
|
|
46
46
|
# @see extension_loaded?
|
47
47
|
attr_accessor :extension_loaded
|
48
48
|
# @!attribute [rw] logger
|
49
|
-
# Accessor for the AppSignal logger.
|
49
|
+
# Accessor for the internal AppSignal logger.
|
50
|
+
#
|
51
|
+
# Not to be confused with our logging feature.
|
52
|
+
# This is part of our private internal API. Do not call this method
|
53
|
+
# directly.
|
50
54
|
#
|
51
55
|
# If no logger has been set, it will return a "in memory logger", using
|
52
56
|
# `in_memory_log`. Once AppSignal is started (using {.start}) the
|
@@ -57,7 +61,7 @@ module Appsignal
|
|
57
61
|
# @api private
|
58
62
|
# @return [Logger]
|
59
63
|
# @see start_logger
|
60
|
-
attr_writer :
|
64
|
+
attr_writer :internal_logger
|
61
65
|
|
62
66
|
# @api private
|
63
67
|
def testing?
|
@@ -91,11 +95,11 @@ module Appsignal
|
|
91
95
|
# @since 0.7.0
|
92
96
|
def start
|
93
97
|
unless extension_loaded?
|
94
|
-
|
98
|
+
internal_logger.info("Not starting appsignal, extension is not loaded")
|
95
99
|
return
|
96
100
|
end
|
97
101
|
|
98
|
-
|
102
|
+
internal_logger.debug("Starting appsignal")
|
99
103
|
|
100
104
|
@config ||= Config.new(
|
101
105
|
Dir.pwd,
|
@@ -103,9 +107,9 @@ module Appsignal
|
|
103
107
|
)
|
104
108
|
|
105
109
|
if config.valid?
|
106
|
-
|
110
|
+
internal_logger.level = config.log_level
|
107
111
|
if config.active?
|
108
|
-
|
112
|
+
internal_logger.info "Starting AppSignal #{Appsignal::VERSION} " \
|
109
113
|
"(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
|
110
114
|
config.write_to_environment
|
111
115
|
Appsignal::Extension.start
|
@@ -120,10 +124,10 @@ module Appsignal
|
|
120
124
|
|
121
125
|
collect_environment_metadata
|
122
126
|
else
|
123
|
-
|
127
|
+
internal_logger.info("Not starting, not active for #{config.env}")
|
124
128
|
end
|
125
129
|
else
|
126
|
-
|
130
|
+
internal_logger.error("Not starting, no valid config for this environment")
|
127
131
|
end
|
128
132
|
end
|
129
133
|
|
@@ -143,9 +147,9 @@ module Appsignal
|
|
143
147
|
# @since 1.0.0
|
144
148
|
def stop(called_by = nil)
|
145
149
|
if called_by
|
146
|
-
|
150
|
+
internal_logger.debug("Stopping appsignal (#{called_by})")
|
147
151
|
else
|
148
|
-
|
152
|
+
internal_logger.debug("Stopping appsignal")
|
149
153
|
end
|
150
154
|
Appsignal::Extension.stop
|
151
155
|
end
|
@@ -154,7 +158,7 @@ module Appsignal
|
|
154
158
|
return unless active?
|
155
159
|
|
156
160
|
Appsignal.start_logger
|
157
|
-
|
161
|
+
internal_logger.debug("Forked process, resubscribing and restarting extension")
|
158
162
|
Appsignal::Extension.start
|
159
163
|
end
|
160
164
|
|
@@ -162,7 +166,8 @@ module Appsignal
|
|
162
166
|
Appsignal::Extension.get_server_state(key)
|
163
167
|
end
|
164
168
|
|
165
|
-
# In memory logger used before any logger is started with
|
169
|
+
# In memory internal logger used before any internal logger is started with
|
170
|
+
# {.start_logger}.
|
166
171
|
#
|
167
172
|
# The contents of this logger are flushed to the logger in {.start_logger}.
|
168
173
|
#
|
@@ -176,11 +181,12 @@ module Appsignal
|
|
176
181
|
end
|
177
182
|
end
|
178
183
|
|
179
|
-
def
|
180
|
-
@
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
+
def internal_logger
|
185
|
+
@internal_logger ||=
|
186
|
+
Appsignal::Utils::IntegrationLogger.new(in_memory_log).tap do |l|
|
187
|
+
l.level = ::Logger::INFO
|
188
|
+
l.formatter = log_formatter("appsignal")
|
189
|
+
end
|
184
190
|
end
|
185
191
|
|
186
192
|
# @api private
|
@@ -192,7 +198,7 @@ module Appsignal
|
|
192
198
|
end
|
193
199
|
end
|
194
200
|
|
195
|
-
# Start the AppSignal logger.
|
201
|
+
# Start the AppSignal internal logger.
|
196
202
|
#
|
197
203
|
# Sets the log level and sets the logger. Uses a file-based logger or the
|
198
204
|
# STDOUT-based logger. See the `:log` configuration option.
|
@@ -201,18 +207,18 @@ module Appsignal
|
|
201
207
|
# @since 0.7.0
|
202
208
|
def start_logger
|
203
209
|
if config && config[:log] == "file" && config.log_file_path
|
204
|
-
|
210
|
+
start_internal_file_logger(config.log_file_path)
|
205
211
|
else
|
206
|
-
|
212
|
+
start_internal_stdout_logger
|
207
213
|
end
|
208
214
|
|
209
|
-
|
215
|
+
internal_logger.level =
|
210
216
|
if config
|
211
217
|
config.log_level
|
212
218
|
else
|
213
219
|
Appsignal::Config::DEFAULT_LOG_LEVEL
|
214
220
|
end
|
215
|
-
|
221
|
+
internal_logger << @in_memory_log.string if @in_memory_log
|
216
222
|
end
|
217
223
|
|
218
224
|
# Returns if the C-extension was loaded properly.
|
@@ -255,18 +261,18 @@ module Appsignal
|
|
255
261
|
|
256
262
|
private
|
257
263
|
|
258
|
-
def
|
259
|
-
@
|
260
|
-
|
264
|
+
def start_internal_stdout_logger
|
265
|
+
@internal_logger = Appsignal::Utils::IntegrationLogger.new($stdout)
|
266
|
+
internal_logger.formatter = log_formatter("appsignal")
|
261
267
|
end
|
262
268
|
|
263
|
-
def
|
264
|
-
@
|
265
|
-
|
269
|
+
def start_internal_file_logger(path)
|
270
|
+
@internal_logger = Appsignal::Utils::IntegrationLogger.new(path)
|
271
|
+
internal_logger.formatter = log_formatter
|
266
272
|
rescue SystemCallError => error
|
267
|
-
|
268
|
-
|
269
|
-
|
273
|
+
start_internal_stdout_logger
|
274
|
+
internal_logger.warn "Unable to start internal logger with log path '#{path}'."
|
275
|
+
internal_logger.warn error
|
270
276
|
end
|
271
277
|
|
272
278
|
def collect_environment_metadata
|
@@ -265,7 +265,7 @@ describe Appsignal::Config do
|
|
265
265
|
|
266
266
|
context "with an overriden config file" do
|
267
267
|
let(:config) do
|
268
|
-
project_fixture_config("production", {}, Appsignal.
|
268
|
+
project_fixture_config("production", {}, Appsignal.internal_logger,
|
269
269
|
File.join(project_fixture_path, "config", "appsignal.yml"))
|
270
270
|
end
|
271
271
|
|
@@ -276,7 +276,7 @@ describe Appsignal::Config do
|
|
276
276
|
|
277
277
|
context "with an invalid overriden config file" do
|
278
278
|
let(:config) do
|
279
|
-
project_fixture_config("production", {}, Appsignal.
|
279
|
+
project_fixture_config("production", {}, Appsignal.internal_logger,
|
280
280
|
File.join(project_fixture_path, "config", "missing.yml"))
|
281
281
|
end
|
282
282
|
|
@@ -76,7 +76,7 @@ if DependencyHelper.active_job_present?
|
|
76
76
|
ActiveJob::Base.queue_adapter = :inline
|
77
77
|
|
78
78
|
start_agent
|
79
|
-
Appsignal.
|
79
|
+
Appsignal.internal_logger = test_logger(log)
|
80
80
|
class ActiveJobTestJob < ActiveJob::Base
|
81
81
|
def perform(*_args)
|
82
82
|
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
describe Appsignal::Hooks::RedisClientHook do
|
2
|
+
before do
|
3
|
+
Appsignal.config = project_fixture_config
|
4
|
+
end
|
5
|
+
|
6
|
+
if DependencyHelper.redis_client_present?
|
7
|
+
context "with redis_client" do
|
8
|
+
context "with instrumentation enabled" do
|
9
|
+
describe "#dependencies_present?" do
|
10
|
+
subject { described_class.new.dependencies_present? }
|
11
|
+
|
12
|
+
it { is_expected.to be_truthy }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with rest-client gem" do
|
16
|
+
describe "integration" do
|
17
|
+
before do
|
18
|
+
Appsignal.config.config_hash[:instrument_redis] = true
|
19
|
+
end
|
20
|
+
|
21
|
+
context "install" do
|
22
|
+
before do
|
23
|
+
Appsignal::Hooks.load_hooks
|
24
|
+
end
|
25
|
+
|
26
|
+
it "includes the integration for the ruby connection" do
|
27
|
+
# Test if the last included module (prepended module) was our
|
28
|
+
# integration. That's not certain with the assertions below
|
29
|
+
# because we have to overwrite the `process` method for the test.
|
30
|
+
expect(RedisClient::RubyConnection.included_modules.first)
|
31
|
+
.to eql(Appsignal::Integrations::RedisClientIntegration)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "requirements" do
|
36
|
+
it "driver should have the write method" do
|
37
|
+
# Since we stub the driver class below, to make sure that we don't
|
38
|
+
# create a real connection, the test won't fail if the method definition
|
39
|
+
# is changed.
|
40
|
+
method = RedisClient::RubyConnection.instance_method(:write)
|
41
|
+
expect(method.arity).to eql(1)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "instrumentation" do
|
46
|
+
before do
|
47
|
+
start_agent
|
48
|
+
# Stub RedisClient::RubyConnection class so that it doesn't perform an actual
|
49
|
+
# Redis query. This class will be included (prepended) with the
|
50
|
+
# AppSignal Redis integration.
|
51
|
+
stub_const("RedisClient::RubyConnection", Class.new do
|
52
|
+
def initialize(config)
|
53
|
+
@config = config
|
54
|
+
end
|
55
|
+
|
56
|
+
def write(_commands)
|
57
|
+
"stub_write"
|
58
|
+
end
|
59
|
+
end)
|
60
|
+
# Load the integration again for the stubbed RedisClient::RubyConnection class.
|
61
|
+
# Call it directly because {Appsignal::Hooks.load_hooks} keeps
|
62
|
+
# track if it was installed already or not.
|
63
|
+
Appsignal::Hooks::RedisClientHook.new.install
|
64
|
+
end
|
65
|
+
let!(:transaction) do
|
66
|
+
Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
|
67
|
+
end
|
68
|
+
let!(:client_config) { RedisClient::Config.new(:id => "stub_id") }
|
69
|
+
around { |example| keep_transactions { example.run } }
|
70
|
+
|
71
|
+
it "instrument a redis call" do
|
72
|
+
connection = RedisClient::RubyConnection.new client_config
|
73
|
+
expect(connection.write([:get, "key"])).to eql("stub_write")
|
74
|
+
|
75
|
+
transaction_hash = transaction.to_h
|
76
|
+
expect(transaction_hash["events"]).to include(
|
77
|
+
hash_including(
|
78
|
+
"name" => "query.redis",
|
79
|
+
"body" => "get ?",
|
80
|
+
"title" => "stub_id"
|
81
|
+
)
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "instrument a redis script call" do
|
86
|
+
connection = ::RedisClient::RubyConnection.new client_config
|
87
|
+
script = "return redis.call('set',KEYS[1],ARGV[1])"
|
88
|
+
keys = ["foo"]
|
89
|
+
argv = ["bar"]
|
90
|
+
expect(connection.write([:eval, script, keys.size, keys,
|
91
|
+
argv])).to eql("stub_write")
|
92
|
+
|
93
|
+
transaction_hash = transaction.to_h
|
94
|
+
expect(transaction_hash["events"]).to include(
|
95
|
+
hash_including(
|
96
|
+
"name" => "query.redis",
|
97
|
+
"body" => "#{script} ? ?",
|
98
|
+
"title" => "stub_id"
|
99
|
+
)
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if DependencyHelper.hiredis_client_present?
|
107
|
+
context "with hiredis driver" do
|
108
|
+
describe "integration" do
|
109
|
+
before do
|
110
|
+
Appsignal.config.config_hash[:instrument_redis] = true
|
111
|
+
end
|
112
|
+
|
113
|
+
context "install" do
|
114
|
+
before do
|
115
|
+
Appsignal::Hooks.load_hooks
|
116
|
+
end
|
117
|
+
|
118
|
+
it "includes the integration in the HiredisConnection class" do
|
119
|
+
# Test if the last included module (prepended module) was our
|
120
|
+
# integration. That's not certain with the assertions below
|
121
|
+
# because we have to overwrite the `process` method for the test.
|
122
|
+
expect(RedisClient::HiredisConnection.included_modules.first)
|
123
|
+
.to eql(Appsignal::Integrations::RedisClientIntegration)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "requirements" do
|
128
|
+
it "driver should have the write method" do
|
129
|
+
# Since we stub the driver class below, to make sure that we don't
|
130
|
+
# create a real connection, the test won't fail if the method definition
|
131
|
+
# is changed.
|
132
|
+
method = RedisClient::HiredisConnection.instance_method(:write)
|
133
|
+
expect(method.arity).to eql(1)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "instrumentation" do
|
138
|
+
before do
|
139
|
+
start_agent
|
140
|
+
# Stub RedisClient::HiredisConnection class so that it doesn't perform an actual
|
141
|
+
# Redis query. This class will be included (prepended) with the
|
142
|
+
# AppSignal Redis integration.
|
143
|
+
stub_const("RedisClient::HiredisConnection", Class.new do
|
144
|
+
def initialize(config)
|
145
|
+
@config = config
|
146
|
+
end
|
147
|
+
|
148
|
+
def write(_commands)
|
149
|
+
"stub_write"
|
150
|
+
end
|
151
|
+
end)
|
152
|
+
# Load the integration again for the stubbed RedisClient::HiredisConnection class.
|
153
|
+
# Call it directly because {Appsignal::Hooks.load_hooks} keeps
|
154
|
+
# track if it was installed already or not.
|
155
|
+
Appsignal::Hooks::RedisClientHook.new.install
|
156
|
+
end
|
157
|
+
let!(:transaction) do
|
158
|
+
Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST,
|
159
|
+
"test")
|
160
|
+
end
|
161
|
+
let!(:client_config) { RedisClient::Config.new(:id => "stub_id") }
|
162
|
+
around { |example| keep_transactions { example.run } }
|
163
|
+
|
164
|
+
it "instrument a redis call" do
|
165
|
+
connection = RedisClient::HiredisConnection.new client_config
|
166
|
+
expect(connection.write([:get, "key"])).to eql("stub_write")
|
167
|
+
|
168
|
+
transaction_hash = transaction.to_h
|
169
|
+
expect(transaction_hash["events"]).to include(
|
170
|
+
hash_including(
|
171
|
+
"name" => "query.redis",
|
172
|
+
"body" => "get ?",
|
173
|
+
"title" => "stub_id"
|
174
|
+
)
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "instrument a redis script call" do
|
179
|
+
connection = ::RedisClient::HiredisConnection.new client_config
|
180
|
+
script = "return redis.call('set',KEYS[1],ARGV[1])"
|
181
|
+
keys = ["foo"]
|
182
|
+
argv = ["bar"]
|
183
|
+
expect(connection.write([:eval, script, keys.size, keys,
|
184
|
+
argv])).to eql("stub_write")
|
185
|
+
|
186
|
+
transaction_hash = transaction.to_h
|
187
|
+
expect(transaction_hash["events"]).to include(
|
188
|
+
hash_including(
|
189
|
+
"name" => "query.redis",
|
190
|
+
"body" => "#{script} ? ?",
|
191
|
+
"title" => "stub_id"
|
192
|
+
)
|
193
|
+
)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "with instrumentation disabled" do
|
202
|
+
before do
|
203
|
+
Appsignal.config.config_hash[:instrument_redis] = false
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "#dependencies_present?" do
|
207
|
+
subject { described_class.new.dependencies_present? }
|
208
|
+
|
209
|
+
it { is_expected.to be_falsy }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
context "without redis" do
|
215
|
+
describe "#dependencies_present?" do
|
216
|
+
subject { described_class.new.dependencies_present? }
|
217
|
+
|
218
|
+
it { is_expected.to be_falsy }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -5,100 +5,122 @@ describe Appsignal::Hooks::RedisHook do
|
|
5
5
|
|
6
6
|
if DependencyHelper.redis_present?
|
7
7
|
context "with redis" do
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
if DependencyHelper.redis_client_present?
|
9
|
+
context "with redis-client" do
|
10
|
+
context "with instrumentation enabled" do
|
11
|
+
describe "#dependencies_present?" do
|
12
|
+
subject { described_class.new.dependencies_present? }
|
11
13
|
|
12
|
-
|
14
|
+
it { is_expected.to be_falsey }
|
15
|
+
end
|
16
|
+
end
|
13
17
|
end
|
18
|
+
else
|
19
|
+
context "with instrumentation enabled" do
|
20
|
+
describe "#dependencies_present?" do
|
21
|
+
subject { described_class.new.dependencies_present? }
|
14
22
|
|
15
|
-
|
16
|
-
before do
|
17
|
-
Appsignal.config.config_hash[:instrument_redis] = true
|
23
|
+
it { is_expected.to be_truthy }
|
18
24
|
end
|
19
25
|
|
20
|
-
|
26
|
+
describe "integration" do
|
21
27
|
before do
|
22
|
-
Appsignal
|
28
|
+
Appsignal.config.config_hash[:instrument_redis] = true
|
23
29
|
end
|
24
30
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
expect(Redis::Client.included_modules.first)
|
30
|
-
.to eql(Appsignal::Integrations::RedisIntegration)
|
31
|
-
end
|
32
|
-
end
|
31
|
+
context "install" do
|
32
|
+
before do
|
33
|
+
Appsignal::Hooks.load_hooks
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def id
|
42
|
-
"stub_id"
|
43
|
-
end
|
44
|
-
|
45
|
-
def write(_commands)
|
46
|
-
"stub_write"
|
47
|
-
end
|
48
|
-
end)
|
49
|
-
# Load the integration again for the stubbed Redis::Client class.
|
50
|
-
# Call it directly because {Appsignal::Hooks.load_hooks} keeps
|
51
|
-
# track if it was installed already or not.
|
52
|
-
Appsignal::Hooks::RedisHook.new.install
|
53
|
-
end
|
54
|
-
let!(:transaction) do
|
55
|
-
Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
|
36
|
+
it "prepends instrumentation module" do
|
37
|
+
# Test if the last included module (prepended module) was our
|
38
|
+
# integration. That's not certain with the assertions below
|
39
|
+
# because we have to overwrite the `process` method for the test.
|
40
|
+
expect(Redis::Client.included_modules.first)
|
41
|
+
.to eql(Appsignal::Integrations::RedisIntegration)
|
42
|
+
end
|
56
43
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
"name" => "query.redis",
|
67
|
-
"body" => "get ?",
|
68
|
-
"title" => "stub_id"
|
69
|
-
)
|
70
|
-
)
|
44
|
+
|
45
|
+
context "requirements" do
|
46
|
+
it "driver should have the write method" do
|
47
|
+
# Since we stub the client class below, to make sure that we don't
|
48
|
+
# create a real connection, the test won't fail if the method definition
|
49
|
+
# is changed.
|
50
|
+
method = Redis::Client.instance_method(:call)
|
51
|
+
expect(method.arity).to eql(1)
|
52
|
+
end
|
71
53
|
end
|
72
54
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
55
|
+
context "instrumentation" do
|
56
|
+
before do
|
57
|
+
start_agent
|
58
|
+
# Stub Redis::Client class so that it doesn't perform an actual
|
59
|
+
# Redis query. This class will be included (prepended) with the
|
60
|
+
# AppSignal Redis integration.
|
61
|
+
stub_const("Redis::Client", Class.new do
|
62
|
+
def id
|
63
|
+
"stub_id"
|
64
|
+
end
|
65
|
+
|
66
|
+
def write(_commands)
|
67
|
+
"stub_write"
|
68
|
+
end
|
69
|
+
end)
|
70
|
+
# Load the integration again for the stubbed Redis::Client class.
|
71
|
+
# Call it directly because {Appsignal::Hooks.load_hooks} keeps
|
72
|
+
# track if it was installed already or not.
|
73
|
+
Appsignal::Hooks::RedisHook.new.install
|
74
|
+
end
|
75
|
+
let!(:transaction) do
|
76
|
+
Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
|
77
|
+
end
|
78
|
+
around { |example| keep_transactions { example.run } }
|
79
|
+
|
80
|
+
it "instrument a redis call" do
|
81
|
+
client = Redis::Client.new
|
82
|
+
expect(client.write([:get, "key"])).to eql("stub_write")
|
83
|
+
|
84
|
+
transaction_hash = transaction.to_h
|
85
|
+
expect(transaction_hash["events"]).to include(
|
86
|
+
hash_including(
|
87
|
+
"name" => "query.redis",
|
88
|
+
"body" => "get ?",
|
89
|
+
"title" => "stub_id"
|
90
|
+
)
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "instrument a redis script call" do
|
95
|
+
client = Redis::Client.new
|
96
|
+
script = "return redis.call('set',KEYS[1],ARGV[1])"
|
97
|
+
keys = ["foo"]
|
98
|
+
argv = ["bar"]
|
99
|
+
expect(client.write([:eval, script, keys.size, keys, argv])).to eql("stub_write")
|
100
|
+
|
101
|
+
transaction_hash = transaction.to_h
|
102
|
+
expect(transaction_hash["events"]).to include(
|
103
|
+
hash_including(
|
104
|
+
"name" => "query.redis",
|
105
|
+
"body" => "#{script} ? ?",
|
106
|
+
"title" => "stub_id"
|
107
|
+
)
|
108
|
+
)
|
109
|
+
end
|
88
110
|
end
|
89
111
|
end
|
90
112
|
end
|
91
|
-
end
|
92
113
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
114
|
+
context "with instrumentation disabled" do
|
115
|
+
before do
|
116
|
+
Appsignal.config.config_hash[:instrument_redis] = false
|
117
|
+
end
|
97
118
|
|
98
|
-
|
99
|
-
|
119
|
+
describe "#dependencies_present?" do
|
120
|
+
subject { described_class.new.dependencies_present? }
|
100
121
|
|
101
|
-
|
122
|
+
it { is_expected.to be_falsy }
|
123
|
+
end
|
102
124
|
end
|
103
125
|
end
|
104
126
|
end
|
@@ -67,12 +67,12 @@ describe Appsignal::Hooks do
|
|
67
67
|
expect(Appsignal::Hooks.hooks[:mock_error_hook]).to be_instance_of(MockErrorHook)
|
68
68
|
expect(Appsignal::Hooks.hooks[:mock_error_hook].installed?).to be_falsy
|
69
69
|
|
70
|
-
expect(Appsignal.
|
70
|
+
expect(Appsignal.internal_logger).to receive(:error)
|
71
71
|
.with("Error while installing mock_error_hook hook: error").once
|
72
|
-
expect(Appsignal.
|
72
|
+
expect(Appsignal.internal_logger).to receive(:debug).ordered do |message|
|
73
73
|
expect(message).to eq("Installing mock_error_hook hook")
|
74
74
|
end
|
75
|
-
expect(Appsignal.
|
75
|
+
expect(Appsignal.internal_logger).to receive(:debug).ordered do |message|
|
76
76
|
# Start of the error backtrace as debug log
|
77
77
|
expect(message).to start_with(File.expand_path("../../..", __dir__))
|
78
78
|
end
|
@@ -89,7 +89,7 @@ describe Appsignal::Hooks do
|
|
89
89
|
let(:log_stream) { std_stream }
|
90
90
|
let(:log) { log_contents(log_stream) }
|
91
91
|
before do
|
92
|
-
Appsignal.
|
92
|
+
Appsignal.internal_logger = test_logger(log_stream)
|
93
93
|
end
|
94
94
|
|
95
95
|
def call_constant(&block)
|
@@ -18,9 +18,9 @@ if DependencyHelper.rails_present?
|
|
18
18
|
describe "#initialize_appsignal" do
|
19
19
|
let(:app) { MyApp::Application.new }
|
20
20
|
|
21
|
-
describe ".
|
21
|
+
describe ".internal_logger" do
|
22
22
|
before { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
|
23
|
-
subject { Appsignal.
|
23
|
+
subject { Appsignal.internal_logger }
|
24
24
|
|
25
25
|
it { is_expected.to be_a Logger }
|
26
26
|
end
|