appsignal 3.6.5 → 3.7.1
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 +417 -0
- data/CHANGELOG.md +57 -0
- data/build_matrix.yml +8 -0
- data/ext/agent.rb +27 -27
- data/ext/base.rb +3 -0
- data/lib/appsignal/config.rb +20 -7
- data/lib/appsignal/event_formatter.rb +0 -2
- data/lib/appsignal/heartbeat.rb +71 -0
- data/lib/appsignal/helpers/instrumentation.rb +5 -5
- data/lib/appsignal/helpers/metrics.rb +2 -2
- data/lib/appsignal/hooks/gvl.rb +1 -1
- data/lib/appsignal/hooks/mri.rb +1 -1
- data/lib/appsignal/hooks/sidekiq.rb +1 -1
- data/lib/appsignal/hooks.rb +1 -1
- data/lib/appsignal/integrations/railtie.rb +1 -1
- data/lib/appsignal/probes.rb +268 -0
- data/lib/appsignal/utils/stdout_and_logger_message.rb +17 -0
- data/lib/appsignal/utils.rb +1 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +24 -4
- data/spec/lib/appsignal/config_spec.rb +37 -10
- data/spec/lib/appsignal/heartbeat_spec.rb +89 -0
- data/spec/lib/appsignal/hooks/gvl_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/mri_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/puma_spec.rb +1 -1
- data/spec/lib/appsignal/{minutely_spec.rb → probes_spec.rb} +206 -57
- data/spec/lib/appsignal_spec.rb +31 -5
- data/spec/lib/puma/appsignal_spec.rb +8 -2
- data/spec/spec_helper.rb +2 -2
- metadata +7 -6
- data/lib/appsignal/minutely.rb +0 -206
- data/lib/appsignal/utils/deprecation_message.rb +0 -16
data/lib/appsignal/probes.rb
CHANGED
@@ -2,6 +2,274 @@
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Probes
|
5
|
+
class ProbeCollection
|
6
|
+
def initialize
|
7
|
+
@probes = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Integer] Number of probes that are registered.
|
11
|
+
def count
|
12
|
+
probes.count
|
13
|
+
end
|
14
|
+
|
15
|
+
# Clears all probes from the list.
|
16
|
+
# @return [void]
|
17
|
+
def clear
|
18
|
+
probes.clear
|
19
|
+
end
|
20
|
+
|
21
|
+
# Fetch a probe using its name.
|
22
|
+
# @param key [Symbol/String] The name of the probe to fetch.
|
23
|
+
# @return [Object] Returns the registered probe.
|
24
|
+
def [](key)
|
25
|
+
probes[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
# @deprecated Use {Appsignal::Probes.register} instead.
|
29
|
+
def register(name, probe)
|
30
|
+
Appsignal::Utils::StdoutAndLoggerMessage.warning(
|
31
|
+
"The method 'Appsignal::Probes.probes.register' is deprecated. " \
|
32
|
+
"Use 'Appsignal::Probes.register' instead."
|
33
|
+
)
|
34
|
+
Appsignal::Probes.register(name, probe)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
def internal_register(name, probe)
|
39
|
+
if probes.key?(name)
|
40
|
+
logger.debug "A probe with the name `#{name}` is already " \
|
41
|
+
"registered. Overwriting the entry with the new probe."
|
42
|
+
end
|
43
|
+
probes[name] = probe
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def unregister(name)
|
48
|
+
probes.delete(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def each(&block)
|
53
|
+
probes.each(&block)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :probes
|
59
|
+
|
60
|
+
def logger
|
61
|
+
Appsignal.internal_logger
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
# @api private
|
67
|
+
def mutex
|
68
|
+
@mutex ||= Thread::Mutex.new
|
69
|
+
end
|
70
|
+
|
71
|
+
# @see ProbeCollection
|
72
|
+
# @return [ProbeCollection] Returns list of probes.
|
73
|
+
def probes
|
74
|
+
@probes ||= ProbeCollection.new
|
75
|
+
end
|
76
|
+
|
77
|
+
# Register a new minutely probe.
|
78
|
+
#
|
79
|
+
# Supported probe types are:
|
80
|
+
#
|
81
|
+
# - Lambda - A lambda is an object that listens to a `call` method call.
|
82
|
+
# This `call` method is called every minute.
|
83
|
+
# - Class - A class object is an object that listens to a `new` and
|
84
|
+
# `call` method call. The `new` method is called when the minutely
|
85
|
+
# probe thread is started to initialize all probes. This allows probes
|
86
|
+
# to load dependencies once beforehand. Their `call` method is called
|
87
|
+
# every minute.
|
88
|
+
# - Class instance - A class instance object is an object that listens to
|
89
|
+
# a `call` method call. The `call` method is called every minute.
|
90
|
+
#
|
91
|
+
# @example Register a new probe
|
92
|
+
# Appsignal::Probes.register :my_probe, lambda {}
|
93
|
+
#
|
94
|
+
# @example Overwrite an existing registered probe
|
95
|
+
# Appsignal::Probes.register :my_probe, lambda {}
|
96
|
+
# Appsignal::Probes.register :my_probe, lambda { puts "hello" }
|
97
|
+
#
|
98
|
+
# @example Add a lambda as a probe
|
99
|
+
# Appsignal::Probes.register :my_probe, lambda { puts "hello" }
|
100
|
+
# # "hello" # printed every minute
|
101
|
+
#
|
102
|
+
# @example Add a probe instance
|
103
|
+
# class MyProbe
|
104
|
+
# def initialize
|
105
|
+
# puts "started"
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# def call
|
109
|
+
# puts "called"
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# Appsignal::Probes.register :my_probe, MyProbe.new
|
114
|
+
# # "started" # printed immediately
|
115
|
+
# # "called" # printed every minute
|
116
|
+
#
|
117
|
+
# @example Add a probe class
|
118
|
+
# class MyProbe
|
119
|
+
# def initialize
|
120
|
+
# # Add things that only need to be done on start up for this probe
|
121
|
+
# require "some/library/dependency"
|
122
|
+
# @cache = {} # initialize a local cache variable
|
123
|
+
# puts "started"
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# def call
|
127
|
+
# puts "called"
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# Appsignal::Probes.register :my_probe, MyProbe
|
132
|
+
# Appsignal::Probes.start # This is called for you
|
133
|
+
# # "started" # Printed on Appsignal::Probes.start
|
134
|
+
# # "called" # Repeated every minute
|
135
|
+
#
|
136
|
+
# @param name [Symbol/String] Name of the probe. Can be used with
|
137
|
+
# {ProbeCollection#[]}. This name will be used in errors in the log and
|
138
|
+
# allows overwriting of probes by registering new ones with the same
|
139
|
+
# name.
|
140
|
+
# @param probe [Object] Any object that listens to the `call` method will
|
141
|
+
# be used as a probe.
|
142
|
+
# @return [void]
|
143
|
+
def register(name, probe)
|
144
|
+
probes.internal_register(name, probe)
|
145
|
+
|
146
|
+
initialize_probe(name, probe) if started?
|
147
|
+
end
|
148
|
+
|
149
|
+
# Unregister a probe that's registered with {register}.
|
150
|
+
# Can also be used to unregister automatically registered probes by the
|
151
|
+
# gem.
|
152
|
+
#
|
153
|
+
# @example Unregister probes
|
154
|
+
# # First register a probe
|
155
|
+
# Appsignal::Probes.register :my_probe, lambda {}
|
156
|
+
#
|
157
|
+
# # Then unregister a probe if needed
|
158
|
+
# Appsignal::Probes.unregister :my_probe
|
159
|
+
#
|
160
|
+
# @param name [Symbol/String] Name of the probe used to {register} the
|
161
|
+
# probe.
|
162
|
+
# @return [void]
|
163
|
+
def unregister(name)
|
164
|
+
probes.unregister(name)
|
165
|
+
|
166
|
+
uninitialize_probe(name)
|
167
|
+
end
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
def start
|
171
|
+
stop
|
172
|
+
@started = true
|
173
|
+
@thread = Thread.new do
|
174
|
+
# Advise multi-threaded app servers to ignore this thread
|
175
|
+
# for the purposes of fork safety warnings
|
176
|
+
if Thread.current.respond_to?(:thread_variable_set)
|
177
|
+
Thread.current.thread_variable_set(:fork_safe, true)
|
178
|
+
end
|
179
|
+
|
180
|
+
sleep initial_wait_time
|
181
|
+
initialize_probes
|
182
|
+
loop do
|
183
|
+
logger = Appsignal.internal_logger
|
184
|
+
mutex.synchronize do
|
185
|
+
logger.debug("Gathering minutely metrics with #{probe_instances.count} probes")
|
186
|
+
probe_instances.each do |name, probe|
|
187
|
+
logger.debug("Gathering minutely metrics with '#{name}' probe")
|
188
|
+
probe.call
|
189
|
+
rescue => ex
|
190
|
+
logger.error "Error in minutely probe '#{name}': #{ex}"
|
191
|
+
logger.debug ex.backtrace.join("\n")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
sleep wait_time
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Returns if the probes thread has been started. If the value is false or
|
200
|
+
# nil, it has not been started yet.
|
201
|
+
#
|
202
|
+
# @return [Boolean, nil]
|
203
|
+
def started?
|
204
|
+
@started
|
205
|
+
end
|
206
|
+
|
207
|
+
# Stop the minutely probes mechanism. Stop the thread and clear all probe
|
208
|
+
# instances.
|
209
|
+
def stop
|
210
|
+
defined?(@thread) && @thread.kill
|
211
|
+
@started = false
|
212
|
+
probe_instances.clear
|
213
|
+
end
|
214
|
+
|
215
|
+
# @api private
|
216
|
+
def wait_time
|
217
|
+
60 - Time.now.sec
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def initial_wait_time
|
223
|
+
remaining_seconds = 60 - Time.now.sec
|
224
|
+
return remaining_seconds if remaining_seconds > 30
|
225
|
+
|
226
|
+
remaining_seconds + 60
|
227
|
+
end
|
228
|
+
|
229
|
+
def initialize_probes
|
230
|
+
probes.each do |name, probe|
|
231
|
+
initialize_probe(name, probe)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def initialize_probe(name, probe)
|
236
|
+
if probe.respond_to? :new
|
237
|
+
instance = probe.new
|
238
|
+
klass = probe
|
239
|
+
else
|
240
|
+
instance = probe
|
241
|
+
klass = instance.class
|
242
|
+
end
|
243
|
+
unless dependencies_present?(klass)
|
244
|
+
Appsignal.internal_logger.debug "Skipping '#{name}' probe, " \
|
245
|
+
"#{klass}.dependency_present? returned falsy"
|
246
|
+
return
|
247
|
+
end
|
248
|
+
mutex.synchronize do
|
249
|
+
probe_instances[name] = instance
|
250
|
+
end
|
251
|
+
rescue => error
|
252
|
+
logger = Appsignal.internal_logger
|
253
|
+
logger.error "Error while initializing minutely probe '#{name}': #{error}"
|
254
|
+
logger.debug error.backtrace.join("\n")
|
255
|
+
end
|
256
|
+
|
257
|
+
def uninitialize_probe(name)
|
258
|
+
mutex.synchronize do
|
259
|
+
probe_instances.delete(name)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def dependencies_present?(probe)
|
264
|
+
return true unless probe.respond_to? :dependencies_present?
|
265
|
+
|
266
|
+
probe.dependencies_present?
|
267
|
+
end
|
268
|
+
|
269
|
+
def probe_instances
|
270
|
+
@probe_instances ||= {}
|
271
|
+
end
|
272
|
+
end
|
5
273
|
end
|
6
274
|
end
|
7
275
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Utils
|
5
|
+
# @api private
|
6
|
+
module StdoutAndLoggerMessage
|
7
|
+
def self.warning(message, logger = Appsignal.internal_logger)
|
8
|
+
Kernel.warn "appsignal WARNING: #{message}"
|
9
|
+
logger.warn message
|
10
|
+
end
|
11
|
+
|
12
|
+
def stdout_and_logger_warning(message, logger = Appsignal.internal_logger)
|
13
|
+
Appsignal::Utils::StdoutAndLoggerMessage.warning(message, logger)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/appsignal/utils.rb
CHANGED
data/lib/appsignal/version.rb
CHANGED
data/lib/appsignal.rb
CHANGED
@@ -5,7 +5,7 @@ require "securerandom"
|
|
5
5
|
require "stringio"
|
6
6
|
|
7
7
|
require "appsignal/logger"
|
8
|
-
require "appsignal/utils/
|
8
|
+
require "appsignal/utils/stdout_and_logger_message"
|
9
9
|
require "appsignal/helpers/instrumentation"
|
10
10
|
require "appsignal/helpers/metrics"
|
11
11
|
|
@@ -120,7 +120,7 @@ module Appsignal
|
|
120
120
|
Appsignal::Environment.report_enabled("allocation_tracking")
|
121
121
|
end
|
122
122
|
|
123
|
-
Appsignal::
|
123
|
+
Appsignal::Probes.start if config[:enable_minutely_probes]
|
124
124
|
|
125
125
|
collect_environment_metadata
|
126
126
|
else
|
@@ -152,6 +152,7 @@ module Appsignal
|
|
152
152
|
internal_logger.debug("Stopping appsignal")
|
153
153
|
end
|
154
154
|
Appsignal::Extension.stop
|
155
|
+
Appsignal::Probes.stop
|
155
156
|
end
|
156
157
|
|
157
158
|
def forked
|
@@ -218,7 +219,10 @@ module Appsignal
|
|
218
219
|
else
|
219
220
|
Appsignal::Config::DEFAULT_LOG_LEVEL
|
220
221
|
end
|
221
|
-
|
222
|
+
return unless @in_memory_log
|
223
|
+
|
224
|
+
internal_logger << @in_memory_log.string
|
225
|
+
@in_memory_log = nil
|
222
226
|
end
|
223
227
|
|
224
228
|
# Returns if the C-extension was loaded properly.
|
@@ -287,6 +291,22 @@ module Appsignal
|
|
287
291
|
end
|
288
292
|
Appsignal::Environment.report_supported_gems
|
289
293
|
end
|
294
|
+
|
295
|
+
# Alias constants that have moved with a warning message that points to the
|
296
|
+
# place to update the reference.
|
297
|
+
def const_missing(name)
|
298
|
+
case name
|
299
|
+
when :Minutely
|
300
|
+
callers = caller
|
301
|
+
Appsignal::Utils::StdoutAndLoggerMessage.warning \
|
302
|
+
"The constant Appsignal::Minutely has been deprecated. " \
|
303
|
+
"Please update the constant name to Appsignal::Probes " \
|
304
|
+
"in the following file to remove this message.\n#{callers.first}"
|
305
|
+
Appsignal::Probes
|
306
|
+
else
|
307
|
+
super
|
308
|
+
end
|
309
|
+
end
|
290
310
|
end
|
291
311
|
end
|
292
312
|
|
@@ -300,10 +320,10 @@ require "appsignal/event_formatter"
|
|
300
320
|
require "appsignal/hooks"
|
301
321
|
require "appsignal/probes"
|
302
322
|
require "appsignal/marker"
|
303
|
-
require "appsignal/minutely"
|
304
323
|
require "appsignal/garbage_collection"
|
305
324
|
require "appsignal/integrations/railtie" if defined?(::Rails)
|
306
325
|
require "appsignal/transaction"
|
307
326
|
require "appsignal/version"
|
308
327
|
require "appsignal/rack/generic_instrumentation"
|
309
328
|
require "appsignal/transmitter"
|
329
|
+
require "appsignal/heartbeat"
|
@@ -172,6 +172,7 @@ describe Appsignal::Config do
|
|
172
172
|
:filter_session_data => [],
|
173
173
|
:ignore_actions => [],
|
174
174
|
:ignore_errors => [],
|
175
|
+
:ignore_logs => [],
|
175
176
|
:ignore_namespaces => [],
|
176
177
|
:instrument_http_rb => true,
|
177
178
|
:instrument_net_http => true,
|
@@ -421,6 +422,7 @@ describe Appsignal::Config do
|
|
421
422
|
:dns_servers => ["8.8.8.8", "8.8.4.4"],
|
422
423
|
:ignore_actions => %w[action1 action2],
|
423
424
|
:ignore_errors => %w[ExampleStandardError AnotherError],
|
425
|
+
:ignore_logs => ["^start$", "^Completed 2.* in .*ms (.*)"],
|
424
426
|
:ignore_namespaces => %w[admin private_namespace],
|
425
427
|
:instrument_net_http => false,
|
426
428
|
:instrument_redis => false,
|
@@ -443,6 +445,7 @@ describe Appsignal::Config do
|
|
443
445
|
ENV["APPSIGNAL_DNS_SERVERS"] = "8.8.8.8,8.8.4.4"
|
444
446
|
ENV["APPSIGNAL_IGNORE_ACTIONS"] = "action1,action2"
|
445
447
|
ENV["APPSIGNAL_IGNORE_ERRORS"] = "ExampleStandardError,AnotherError"
|
448
|
+
ENV["APPSIGNAL_IGNORE_LOGS"] = "^start$,^Completed 2.* in .*ms (.*)"
|
446
449
|
ENV["APPSIGNAL_IGNORE_NAMESPACES"] = "admin,private_namespace"
|
447
450
|
ENV["APPSIGNAL_INSTRUMENT_NET_HTTP"] = "false"
|
448
451
|
ENV["APPSIGNAL_INSTRUMENT_REDIS"] = "false"
|
@@ -639,6 +642,7 @@ describe Appsignal::Config do
|
|
639
642
|
config[:http_proxy] = "http://localhost"
|
640
643
|
config[:ignore_actions] = %w[action1 action2]
|
641
644
|
config[:ignore_errors] = %w[ExampleStandardError AnotherError]
|
645
|
+
config[:ignore_logs] = ["^start$", "^Completed 2.* in .*ms (.*)"]
|
642
646
|
config[:ignore_namespaces] = %w[admin private_namespace]
|
643
647
|
config[:log] = "stdout"
|
644
648
|
config[:log_path] = "/tmp"
|
@@ -672,6 +676,7 @@ describe Appsignal::Config do
|
|
672
676
|
expect(ENV.fetch("_APPSIGNAL_HTTP_PROXY", nil)).to eq "http://localhost"
|
673
677
|
expect(ENV.fetch("_APPSIGNAL_IGNORE_ACTIONS", nil)).to eq "action1,action2"
|
674
678
|
expect(ENV.fetch("_APPSIGNAL_IGNORE_ERRORS", nil)).to eq "ExampleStandardError,AnotherError"
|
679
|
+
expect(ENV.fetch("_APPSIGNAL_IGNORE_LOGS", nil)).to eq "^start$,^Completed 2.* in .*ms (.*)"
|
675
680
|
expect(ENV.fetch("_APPSIGNAL_IGNORE_NAMESPACES", nil)).to eq "admin,private_namespace"
|
676
681
|
expect(ENV.fetch("_APPSIGNAL_RUNNING_IN_CONTAINER", nil)).to eq "false"
|
677
682
|
expect(ENV.fetch("_APPSIGNAL_ENABLE_HOST_METRICS", nil)).to eq "true"
|
@@ -752,7 +757,10 @@ describe Appsignal::Config do
|
|
752
757
|
let(:out_stream) { std_stream }
|
753
758
|
let(:output) { out_stream.read }
|
754
759
|
let(:config) { project_fixture_config("production", :log_path => log_path) }
|
755
|
-
|
760
|
+
|
761
|
+
def log_file_path
|
762
|
+
capture_stdout(out_stream) { config.log_file_path }
|
763
|
+
end
|
756
764
|
|
757
765
|
context "when path is writable" do
|
758
766
|
let(:log_path) { File.join(tmp_dir, "writable-path") }
|
@@ -760,11 +768,11 @@ describe Appsignal::Config do
|
|
760
768
|
after { FileUtils.rm_rf(log_path) }
|
761
769
|
|
762
770
|
it "returns log file path" do
|
763
|
-
expect(
|
771
|
+
expect(log_file_path).to eq File.join(log_path, "appsignal.log")
|
764
772
|
end
|
765
773
|
|
766
774
|
it "prints no warning" do
|
767
|
-
|
775
|
+
log_file_path
|
768
776
|
expect(output).to be_empty
|
769
777
|
end
|
770
778
|
end
|
@@ -778,28 +786,47 @@ describe Appsignal::Config do
|
|
778
786
|
before { FileUtils.chmod(0o777, system_tmp_dir) }
|
779
787
|
|
780
788
|
it "returns returns the tmp location" do
|
781
|
-
expect(
|
789
|
+
expect(log_file_path).to eq(File.join(system_tmp_dir, "appsignal.log"))
|
782
790
|
end
|
783
791
|
|
784
792
|
it "prints a warning" do
|
785
|
-
|
793
|
+
log_file_path
|
786
794
|
expect(output).to include "appsignal: Unable to log to '#{log_path}'. " \
|
787
795
|
"Logging to '#{system_tmp_dir}' instead."
|
788
796
|
end
|
797
|
+
|
798
|
+
it "prints a warning once" do
|
799
|
+
capture_stdout(out_stream) do
|
800
|
+
log_file_path
|
801
|
+
log_file_path
|
802
|
+
end
|
803
|
+
message = "appsignal: Unable to log to '#{log_path}'. " \
|
804
|
+
"Logging to '#{system_tmp_dir}' instead."
|
805
|
+
expect(output.scan(message).count).to eq(1)
|
806
|
+
end
|
789
807
|
end
|
790
808
|
|
791
809
|
context "when the /tmp fallback path is not writable" do
|
792
810
|
before { FileUtils.chmod(0o555, system_tmp_dir) }
|
793
811
|
|
794
812
|
it "returns nil" do
|
795
|
-
expect(
|
813
|
+
expect(log_file_path).to be_nil
|
796
814
|
end
|
797
815
|
|
798
816
|
it "prints a warning" do
|
799
|
-
|
817
|
+
log_file_path
|
800
818
|
expect(output).to include "appsignal: Unable to log to '#{log_path}' " \
|
801
819
|
"or the '#{system_tmp_dir}' fallback."
|
802
820
|
end
|
821
|
+
|
822
|
+
it "prints a warning once" do
|
823
|
+
capture_stdout(out_stream) do
|
824
|
+
log_file_path
|
825
|
+
log_file_path
|
826
|
+
end
|
827
|
+
message = "appsignal: Unable to log to '#{log_path}' or the '#{system_tmp_dir}' fallback."
|
828
|
+
expect(output.scan(message).count).to eq(1)
|
829
|
+
end
|
803
830
|
end
|
804
831
|
end
|
805
832
|
|
@@ -814,11 +841,11 @@ describe Appsignal::Config do
|
|
814
841
|
|
815
842
|
context "when root_path is set" do
|
816
843
|
it "returns returns the project log location" do
|
817
|
-
expect(
|
844
|
+
expect(log_file_path).to eq File.join(config.root_path, "log/appsignal.log")
|
818
845
|
end
|
819
846
|
|
820
847
|
it "prints no warning" do
|
821
|
-
|
848
|
+
log_file_path
|
822
849
|
expect(output).to be_empty
|
823
850
|
end
|
824
851
|
end
|
@@ -878,7 +905,7 @@ describe Appsignal::Config do
|
|
878
905
|
end
|
879
906
|
|
880
907
|
it "returns real path of log path" do
|
881
|
-
expect(
|
908
|
+
expect(log_file_path).to eq(File.join(real_path, "appsignal.log"))
|
882
909
|
end
|
883
910
|
end
|
884
911
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
describe Appsignal::Heartbeat do
|
2
|
+
let(:config) { project_fixture_config }
|
3
|
+
let(:heartbeat) { described_class.new(:name => "heartbeat-name") }
|
4
|
+
let(:transmitter) { Appsignal::Transmitter.new("http://heartbeats/", config) }
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
allow(Appsignal).to receive(:active?).and_return(true)
|
8
|
+
config.logger = Logger.new(StringIO.new)
|
9
|
+
allow(Appsignal::Heartbeat).to receive(:transmitter).and_return(transmitter)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "when Appsignal is not active" do
|
13
|
+
it "should not transmit any events" do
|
14
|
+
allow(Appsignal).to receive(:active?).and_return(false)
|
15
|
+
expect(transmitter).not_to receive(:transmit)
|
16
|
+
|
17
|
+
heartbeat.start
|
18
|
+
heartbeat.finish
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#start" do
|
23
|
+
it "should send a heartbeat start" do
|
24
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
25
|
+
:name => "heartbeat-name",
|
26
|
+
:kind => "start"
|
27
|
+
)).and_return(nil)
|
28
|
+
|
29
|
+
heartbeat.start
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#finish" do
|
34
|
+
it "should send a heartbeat finish" do
|
35
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
36
|
+
:name => "heartbeat-name",
|
37
|
+
:kind => "finish"
|
38
|
+
)).and_return(nil)
|
39
|
+
|
40
|
+
heartbeat.finish
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".heartbeat" do
|
45
|
+
describe "when a block is given" do
|
46
|
+
it "should send a heartbeat start and finish and return the block output" do
|
47
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
48
|
+
:kind => "start",
|
49
|
+
:name => "heartbeat-with-block"
|
50
|
+
)).and_return(nil)
|
51
|
+
|
52
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
53
|
+
:kind => "finish",
|
54
|
+
:name => "heartbeat-with-block"
|
55
|
+
)).and_return(nil)
|
56
|
+
|
57
|
+
output = Appsignal.heartbeat("heartbeat-with-block") { "output" }
|
58
|
+
expect(output).to eq("output")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not send a heartbeat finish event when an error is raised" do
|
62
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
63
|
+
:kind => "start",
|
64
|
+
:name => "heartbeat-with-block"
|
65
|
+
)).and_return(nil)
|
66
|
+
|
67
|
+
expect(transmitter).not_to receive(:transmit).with(hash_including(
|
68
|
+
:kind => "finish",
|
69
|
+
:name => "heartbeat-with-block"
|
70
|
+
))
|
71
|
+
|
72
|
+
expect do
|
73
|
+
Appsignal.heartbeat("heartbeat-with-block") { raise "error" }
|
74
|
+
end.to raise_error(RuntimeError, "error")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "when no block is given" do
|
79
|
+
it "should only send a heartbeat finish event" do
|
80
|
+
expect(transmitter).to receive(:transmit).with(hash_including(
|
81
|
+
:kind => "finish",
|
82
|
+
:name => "heartbeat-without-block"
|
83
|
+
)).and_return(nil)
|
84
|
+
|
85
|
+
Appsignal.heartbeat("heartbeat-without-block")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -93,7 +93,7 @@ describe Appsignal::Hooks::GvlHook do
|
|
93
93
|
it "is added to minutely probes" do
|
94
94
|
Appsignal::Hooks.load_hooks
|
95
95
|
|
96
|
-
expect(Appsignal::
|
96
|
+
expect(Appsignal::Probes.probes[:gvl]).to be Appsignal::Probes::GvlProbe
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
@@ -16,7 +16,7 @@ describe Appsignal::Hooks::MriHook do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should be added to minutely probes" do
|
19
|
-
expect(Appsignal::
|
19
|
+
expect(Appsignal::Probes.probes[:mri]).to be Appsignal::Probes::MriProbe
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -27,7 +27,7 @@ describe Appsignal::Hooks::PumaHook do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
describe "installation" do
|
30
|
-
before { Appsignal::
|
30
|
+
before { Appsignal::Probes.probes.clear }
|
31
31
|
|
32
32
|
context "when not clustered mode" do
|
33
33
|
it "does not add AppSignal stop behavior Puma::Cluster" do
|