appsignal 2.8.4-java → 2.9.0-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/.gitignore +1 -1
- data/.rubocop_todo.yml +7 -16
- data/.travis.yml +4 -1
- data/CHANGELOG.md +16 -0
- data/README.md +23 -0
- data/Rakefile +10 -7
- data/appsignal.gemspec +3 -0
- data/build_matrix.yml +5 -1
- data/ext/Rakefile +23 -16
- data/ext/agent.yml +37 -37
- data/ext/base.rb +86 -24
- data/ext/extconf.rb +33 -26
- data/gemfiles/rails-6.0.gemfile +5 -0
- data/lib/appsignal.rb +14 -489
- data/lib/appsignal/cli/diagnose.rb +84 -4
- data/lib/appsignal/cli/diagnose/paths.rb +0 -5
- data/lib/appsignal/cli/diagnose/utils.rb +17 -0
- data/lib/appsignal/cli/helpers.rb +6 -0
- data/lib/appsignal/cli/install.rb +13 -7
- data/lib/appsignal/config.rb +1 -2
- data/lib/appsignal/event_formatter.rb +4 -5
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +60 -59
- data/lib/appsignal/extension.rb +2 -2
- data/lib/appsignal/helpers/instrumentation.rb +485 -0
- data/lib/appsignal/helpers/metrics.rb +55 -0
- data/lib/appsignal/hooks.rb +9 -8
- data/lib/appsignal/hooks/puma.rb +65 -9
- data/lib/appsignal/hooks/sidekiq.rb +90 -0
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +7 -0
- data/lib/appsignal/integrations/railtie.rb +2 -1
- data/lib/appsignal/marker.rb +2 -3
- data/lib/appsignal/minutely.rb +164 -14
- data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -1
- data/lib/appsignal/system.rb +16 -18
- data/lib/appsignal/utils/rails_helper.rb +16 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +129 -22
- data/spec/lib/appsignal/cli/install_spec.rb +6 -1
- data/spec/lib/appsignal/config_spec.rb +3 -3
- data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +6 -0
- data/spec/lib/appsignal/event_formatter_spec.rb +168 -69
- data/spec/lib/appsignal/hooks/puma_spec.rb +129 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +147 -0
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +24 -1
- data/spec/lib/appsignal/minutely_spec.rb +251 -21
- data/spec/lib/appsignal/system_spec.rb +0 -35
- data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +39 -31
- data/spec/lib/appsignal/utils/json_spec.rb +7 -3
- data/spec/lib/appsignal_spec.rb +27 -2
- data/spec/spec_helper.rb +13 -0
- data/spec/support/helpers/log_helpers.rb +6 -0
- data/spec/support/project_fixture/config/appsignal.yml +1 -0
- data/spec/support/stubs/sidekiq/api.rb +4 -0
- metadata +8 -2
@@ -32,7 +32,7 @@ module Appsignal
|
|
32
32
|
attr_reader :raise_errors_on
|
33
33
|
|
34
34
|
def initialize(app, options = {})
|
35
|
-
Appsignal.logger.debug "Initializing Appsignal::Rack::
|
35
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::SinatraBaseInstrumentation"
|
36
36
|
@app = app
|
37
37
|
@options = options
|
38
38
|
@raise_errors_on = raise_errors?(@app)
|
data/lib/appsignal/system.rb
CHANGED
@@ -16,21 +16,6 @@ module Appsignal
|
|
16
16
|
ENV.key? "DYNO".freeze
|
17
17
|
end
|
18
18
|
|
19
|
-
# Returns the architecture for which the agent was installed.
|
20
|
-
#
|
21
|
-
# This value is saved when the gem is installed in `ext/extconf.rb`.
|
22
|
-
# We use this value to build the diagnose report with the installed
|
23
|
-
# CPU type and platform, rather than the detected architecture in
|
24
|
-
# {.agent_platform} during the diagnose run.
|
25
|
-
#
|
26
|
-
# @api private
|
27
|
-
# @return [String]
|
28
|
-
def self.installed_agent_architecture
|
29
|
-
architecture_file = File.join(GEM_EXT_PATH, "appsignal.architecture")
|
30
|
-
return unless File.exist?(architecture_file)
|
31
|
-
File.read(architecture_file)
|
32
|
-
end
|
33
|
-
|
34
19
|
# Detect agent and extension platform build
|
35
20
|
#
|
36
21
|
# Used by `ext/extconf.rb` to select which build it should download and
|
@@ -42,7 +27,7 @@ module Appsignal
|
|
42
27
|
# @api private
|
43
28
|
# @return [String]
|
44
29
|
def self.agent_platform
|
45
|
-
return MUSL_TARGET if
|
30
|
+
return MUSL_TARGET if force_musl_build?
|
46
31
|
|
47
32
|
host_os = RbConfig::CONFIG["host_os"].downcase
|
48
33
|
local_os =
|
@@ -59,8 +44,8 @@ module Appsignal
|
|
59
44
|
if local_os =~ /linux/
|
60
45
|
ldd_output = ldd_version_output
|
61
46
|
return MUSL_TARGET if ldd_output.include? "musl"
|
62
|
-
ldd_version = ldd_output
|
63
|
-
if ldd_version && versionify(ldd_version
|
47
|
+
ldd_version = extract_ldd_version(ldd_output)
|
48
|
+
if ldd_version && versionify(ldd_version) < versionify("2.15")
|
64
49
|
return MUSL_TARGET
|
65
50
|
end
|
66
51
|
end
|
@@ -68,6 +53,13 @@ module Appsignal
|
|
68
53
|
local_os
|
69
54
|
end
|
70
55
|
|
56
|
+
# Returns whether or not the musl build was forced by the user.
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def self.force_musl_build?
|
60
|
+
%w[true 1].include?(ENV["APPSIGNAL_BUILD_FOR_MUSL"])
|
61
|
+
end
|
62
|
+
|
71
63
|
# @api private
|
72
64
|
def self.versionify(version)
|
73
65
|
Gem::Version.new(version)
|
@@ -78,6 +70,12 @@ module Appsignal
|
|
78
70
|
`ldd --version 2>&1`
|
79
71
|
end
|
80
72
|
|
73
|
+
# @api private
|
74
|
+
def self.extract_ldd_version(string)
|
75
|
+
ldd_version = string.match(/\d+\.\d+/)
|
76
|
+
ldd_version && ldd_version[0]
|
77
|
+
end
|
78
|
+
|
81
79
|
def self.jruby?
|
82
80
|
RUBY_PLATFORM == "java"
|
83
81
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Utils
|
5
|
+
module RailsHelper
|
6
|
+
def self.detected_rails_app_name
|
7
|
+
rails_class = Rails.application.class
|
8
|
+
if rails_class.respond_to? :module_parent_name # Rails 6
|
9
|
+
rails_class.module_parent_name
|
10
|
+
else # Older Rails versions
|
11
|
+
rails_class.parent_name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/appsignal/version.rb
CHANGED
@@ -190,8 +190,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
190
190
|
it "outputs version numbers" do
|
191
191
|
expect(output).to include \
|
192
192
|
"Gem version: #{Appsignal::VERSION}",
|
193
|
-
"Agent version: #{Appsignal::Extension.agent_version}"
|
194
|
-
"Agent architecture: #{Appsignal::System.installed_agent_architecture}"
|
193
|
+
"Agent version: #{Appsignal::Extension.agent_version}"
|
195
194
|
end
|
196
195
|
|
197
196
|
it "transmits version numbers in report" do
|
@@ -200,7 +199,6 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
200
199
|
"language" => "ruby",
|
201
200
|
"package_version" => Appsignal::VERSION,
|
202
201
|
"agent_version" => Appsignal::Extension.agent_version,
|
203
|
-
"agent_architecture" => Appsignal::System.installed_agent_architecture,
|
204
202
|
"extension_loaded" => true
|
205
203
|
}
|
206
204
|
)
|
@@ -238,6 +236,131 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
238
236
|
end
|
239
237
|
end
|
240
238
|
|
239
|
+
describe "installation report" do
|
240
|
+
let(:rbconfig) { RbConfig::CONFIG }
|
241
|
+
|
242
|
+
it "adds the installation report to the diagnostics report" do
|
243
|
+
run
|
244
|
+
jruby = DependencyHelper.running_jruby?
|
245
|
+
expect(received_report["installation"]).to match(
|
246
|
+
"result" => {
|
247
|
+
"status" => "success"
|
248
|
+
},
|
249
|
+
"language" => {
|
250
|
+
"name" => "ruby",
|
251
|
+
"version" => "#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}",
|
252
|
+
"implementation" => jruby ? "jruby" : "ruby"
|
253
|
+
},
|
254
|
+
"download" => {
|
255
|
+
"download_url" => kind_of(String),
|
256
|
+
"checksum" => "verified"
|
257
|
+
},
|
258
|
+
"build" => {
|
259
|
+
"time" => kind_of(String),
|
260
|
+
"package_path" => File.expand_path("../../../../../", __FILE__),
|
261
|
+
"architecture" => rbconfig["host_cpu"],
|
262
|
+
"target" => Appsignal::System.agent_platform,
|
263
|
+
"musl_override" => false,
|
264
|
+
"library_type" => jruby ? "dynamic" : "static",
|
265
|
+
"source" => "remote",
|
266
|
+
"dependencies" => kind_of(Hash),
|
267
|
+
"flags" => kind_of(Hash),
|
268
|
+
"agent_version" => Appsignal::Extension.agent_version
|
269
|
+
},
|
270
|
+
"host" => {
|
271
|
+
"root_user" => false,
|
272
|
+
"dependencies" => kind_of(Hash)
|
273
|
+
}
|
274
|
+
)
|
275
|
+
end
|
276
|
+
|
277
|
+
it "prints the extension installation report" do
|
278
|
+
run
|
279
|
+
jruby = Appsignal::System.jruby?
|
280
|
+
expect(output).to include(
|
281
|
+
"Extension installation report",
|
282
|
+
"Language details",
|
283
|
+
" Implementation: #{jruby ? "jruby" : "ruby"}",
|
284
|
+
" Ruby version: #{"#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"}",
|
285
|
+
"Download details",
|
286
|
+
" Download URL: https://",
|
287
|
+
" Checksum: verified",
|
288
|
+
"Build details",
|
289
|
+
" Install time: 20",
|
290
|
+
" Architecture: #{rbconfig["host_cpu"]}",
|
291
|
+
" Target: #{Appsignal::System.agent_platform}",
|
292
|
+
" Musl override: false",
|
293
|
+
" Library type: #{jruby ? "dynamic" : "static"}",
|
294
|
+
" Dependencies: {",
|
295
|
+
" Flags: {",
|
296
|
+
"Host details",
|
297
|
+
" Root user: false",
|
298
|
+
" Dependencies: {"
|
299
|
+
)
|
300
|
+
end
|
301
|
+
|
302
|
+
context "without install report" do
|
303
|
+
let(:error) { RuntimeError.new("foo") }
|
304
|
+
before do
|
305
|
+
allow(File).to receive(:read).and_call_original
|
306
|
+
expect(File).to receive(:read)
|
307
|
+
.with(File.expand_path("../../../../../ext/install.report", __FILE__))
|
308
|
+
.and_raise(error)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "sends an error" do
|
312
|
+
run
|
313
|
+
expect(received_report["installation"]).to match(
|
314
|
+
"parsing_error" => {
|
315
|
+
"error" => "RuntimeError: foo",
|
316
|
+
"backtrace" => error.backtrace
|
317
|
+
}
|
318
|
+
)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "prints the error" do
|
322
|
+
run
|
323
|
+
expect(output).to include(
|
324
|
+
"Extension installation report",
|
325
|
+
" Error found while parsing the report.",
|
326
|
+
" Error: RuntimeError: foo"
|
327
|
+
)
|
328
|
+
expect(output).to_not include("Raw report:")
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "when report is invalid YAML" do
|
333
|
+
let(:raw_report) { "foo:\nbar" }
|
334
|
+
before do
|
335
|
+
allow(File).to receive(:read).and_call_original
|
336
|
+
expect(File).to receive(:read)
|
337
|
+
.with(File.expand_path("../../../../../ext/install.report", __FILE__))
|
338
|
+
.and_return(raw_report)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "sends an error" do
|
342
|
+
run
|
343
|
+
expect(received_report["installation"]).to match(
|
344
|
+
"parsing_error" => {
|
345
|
+
"error" => kind_of(String),
|
346
|
+
"backtrace" => kind_of(Array),
|
347
|
+
"raw" => raw_report
|
348
|
+
}
|
349
|
+
)
|
350
|
+
end
|
351
|
+
|
352
|
+
it "prints the error" do
|
353
|
+
run
|
354
|
+
expect(output).to include(
|
355
|
+
"Extension installation report",
|
356
|
+
" Error found while parsing the report.",
|
357
|
+
" Error: ",
|
358
|
+
" Raw report:\n#{raw_report}"
|
359
|
+
)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
241
364
|
describe "agent diagnostics" do
|
242
365
|
let(:working_directory_stat) { File.stat("/tmp/appsignal") }
|
243
366
|
|
@@ -471,7 +594,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
471
594
|
context "when not root user" do
|
472
595
|
it "outputs false" do
|
473
596
|
run
|
474
|
-
expect(output).to include "
|
597
|
+
expect(output).to include "Root user: false"
|
475
598
|
end
|
476
599
|
|
477
600
|
it "transmits root: false in report" do
|
@@ -487,7 +610,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
487
610
|
end
|
488
611
|
|
489
612
|
it "outputs true, with warning" do
|
490
|
-
expect(output).to include "
|
613
|
+
expect(output).to include "Root user: true (not recommended)"
|
491
614
|
end
|
492
615
|
|
493
616
|
it "transmits root: true in report" do
|
@@ -834,7 +957,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
834
957
|
expect(received_report["paths"].keys).to match_array(
|
835
958
|
%w[
|
836
959
|
package_install_path root_path working_dir log_dir_path
|
837
|
-
ext/
|
960
|
+
ext/mkmf.log appsignal.log
|
838
961
|
]
|
839
962
|
)
|
840
963
|
end
|
@@ -1138,22 +1261,6 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
1138
1261
|
end
|
1139
1262
|
end
|
1140
1263
|
|
1141
|
-
describe "install.log" do
|
1142
|
-
it_behaves_like "diagnose file" do
|
1143
|
-
let(:filename) { File.join("ext", "install.log") }
|
1144
|
-
before do
|
1145
|
-
expect_any_instance_of(Appsignal::CLI::Diagnose::Paths).to receive(:gem_path)
|
1146
|
-
.at_least(:once)
|
1147
|
-
.and_return(parent_directory)
|
1148
|
-
end
|
1149
|
-
end
|
1150
|
-
|
1151
|
-
it "outputs header" do
|
1152
|
-
run
|
1153
|
-
expect(output).to include("Extension install log")
|
1154
|
-
end
|
1155
|
-
end
|
1156
|
-
|
1157
1264
|
describe "mkmf.log" do
|
1158
1265
|
it_behaves_like "diagnose file" do
|
1159
1266
|
let(:filename) { File.join("ext", "mkmf.log") }
|
@@ -243,7 +243,12 @@ describe Appsignal::CLI::Install do
|
|
243
243
|
|
244
244
|
if rails_present?
|
245
245
|
context "with rails" do
|
246
|
-
let(:installation_instructions)
|
246
|
+
let(:installation_instructions) do
|
247
|
+
[
|
248
|
+
"Installing for Ruby on Rails",
|
249
|
+
"Your app's name is: 'MyApp'"
|
250
|
+
]
|
251
|
+
end
|
247
252
|
let(:app_name) { "MyApp" }
|
248
253
|
let(:config_dir) { File.join(tmp_dir, "config") }
|
249
254
|
let(:environments_dir) { File.join(config_dir, "environments") }
|
@@ -130,7 +130,7 @@ describe Appsignal::Config do
|
|
130
130
|
:enable_allocation_tracking => true,
|
131
131
|
:enable_gc_instrumentation => false,
|
132
132
|
:enable_host_metrics => true,
|
133
|
-
:enable_minutely_probes =>
|
133
|
+
:enable_minutely_probes => true,
|
134
134
|
:ca_file_path => File.join(resources_dir, "cacert.pem"),
|
135
135
|
:dns_servers => [],
|
136
136
|
:files_world_accessible => true,
|
@@ -218,7 +218,8 @@ describe Appsignal::Config do
|
|
218
218
|
:active => true,
|
219
219
|
:push_api_key => "abc",
|
220
220
|
:name => "TestApp",
|
221
|
-
:request_headers => kind_of(Array)
|
221
|
+
:request_headers => kind_of(Array),
|
222
|
+
:enable_minutely_probes => false
|
222
223
|
)
|
223
224
|
end
|
224
225
|
|
@@ -492,7 +493,6 @@ describe Appsignal::Config do
|
|
492
493
|
expect(ENV["_APPSIGNAL_IGNORE_NAMESPACES"]).to eq "admin,private_namespace"
|
493
494
|
expect(ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"]).to eq "false"
|
494
495
|
expect(ENV["_APPSIGNAL_ENABLE_HOST_METRICS"]).to eq "true"
|
495
|
-
expect(ENV["_APPSIGNAL_ENABLE_MINUTELY_PROBES"]).to eq "false"
|
496
496
|
expect(ENV["_APPSIGNAL_HOSTNAME"]).to eq ""
|
497
497
|
expect(ENV["_APPSIGNAL_PROCESS_NAME"]).to include "rspec"
|
498
498
|
expect(ENV["_APPSIGNAL_CA_FILE_PATH"]).to eq File.join(resources_dir, "cacert.pem")
|
@@ -16,6 +16,12 @@ describe Appsignal::EventFormatter::Moped::QueryFormatter do
|
|
16
16
|
it { is_expected.to be_nil }
|
17
17
|
end
|
18
18
|
|
19
|
+
context "when ops is nil in the payload" do
|
20
|
+
let(:payload) { { :ops => nil } }
|
21
|
+
|
22
|
+
it { is_expected.to be_nil }
|
23
|
+
end
|
24
|
+
|
19
25
|
context "Moped::Protocol::Command" do
|
20
26
|
let(:op) do
|
21
27
|
double(
|
@@ -10,9 +10,10 @@ class MockFormatter < Appsignal::EventFormatter
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
class
|
14
|
-
|
15
|
-
|
13
|
+
class MockFormatterDouble < MockFormatter
|
14
|
+
end
|
15
|
+
|
16
|
+
class MissingFormatMockFormatter
|
16
17
|
end
|
17
18
|
|
18
19
|
class IncorrectFormatMockFormatter < Appsignal::EventFormatter
|
@@ -20,109 +21,207 @@ class IncorrectFormatMockFormatter < Appsignal::EventFormatter
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
class IncorrectFormatMock2Formatter < Appsignal::EventFormatter
|
25
|
+
def format(_payload, _foo = nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
23
29
|
class MockDependentFormatter < Appsignal::EventFormatter
|
24
30
|
def initialize
|
25
31
|
NonsenseDependency.something
|
26
32
|
end
|
27
|
-
end
|
28
33
|
|
29
|
-
|
30
|
-
|
34
|
+
def format(_payload)
|
35
|
+
end
|
36
|
+
end
|
31
37
|
|
32
38
|
describe Appsignal::EventFormatter do
|
33
|
-
|
34
|
-
|
39
|
+
let(:klass) { described_class }
|
40
|
+
around do |example|
|
41
|
+
original_formatters = described_class.formatters
|
42
|
+
example.run
|
43
|
+
described_class.class_variable_set(:@@formatters, original_formatters)
|
35
44
|
end
|
36
45
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Class.new(Appsignal::EventFormatter) do
|
41
|
-
register "mock.deprecated"
|
42
|
-
|
43
|
-
def format(_payload)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
46
|
+
describe ".register" do
|
47
|
+
it "registers a formatter" do
|
48
|
+
expect(klass.registered?("mock")).to be_falsy
|
47
49
|
|
48
|
-
|
49
|
-
let(:out_stream) { std_stream }
|
50
|
-
let(:output) { out_stream.read }
|
50
|
+
klass.register "mock", MockFormatter
|
51
51
|
|
52
|
-
it "registers a formatter" do
|
53
52
|
expect(klass.formatters["mock"]).to be_instance_of(MockFormatter)
|
53
|
+
expect(klass.formatter_classes["mock"]).to eq MockFormatter
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
context "when a formatter with the name already exists" do
|
57
|
+
it "does not register the formatter again" do
|
58
|
+
logs = capture_logs do
|
59
|
+
klass.register("mock.twice", MockFormatter)
|
60
|
+
klass.register("mock.twice", MockFormatter)
|
61
|
+
end
|
62
|
+
expect(klass.registered?("mock.twice")).to be_truthy
|
63
|
+
expect(logs).to contains_log :warn, \
|
64
|
+
"Formatter for 'mock.twice' already registered, not registering 'MockFormatter'"
|
65
|
+
end
|
61
66
|
end
|
62
67
|
|
63
|
-
|
64
|
-
|
68
|
+
context "when there is an error initializing the formatter" do
|
69
|
+
it "does not register the formatter and logs an error" do
|
70
|
+
logs = capture_logs do
|
71
|
+
described_class.register "mock.dependent", MockDependentFormatter
|
72
|
+
end
|
73
|
+
expect(klass.registered?("mock.dependent")).to be_falsy
|
74
|
+
expect(logs).to contains_log :error, \
|
75
|
+
"'uninitialized constant MockDependentFormatter::NonsenseDependency' " \
|
76
|
+
"when initializing mock.dependent event formatter"
|
77
|
+
end
|
65
78
|
end
|
66
79
|
|
67
|
-
|
68
|
-
|
80
|
+
context "when formatter has no format/1 method" do
|
81
|
+
context "when the formatter has no format method" do
|
82
|
+
it "does not register the formatter and logs an error" do
|
83
|
+
logs = capture_logs do
|
84
|
+
described_class.register "mock.missing", MissingFormatMockFormatter
|
85
|
+
end
|
86
|
+
expect(klass.registered?("mock.missing")).to be_falsy
|
87
|
+
expect(logs).to contains_log :error, \
|
88
|
+
"'MissingFormatMockFormatter does not have a format(payload) " \
|
89
|
+
"method' when initializing mock.missing event formatter"
|
90
|
+
end
|
91
|
+
end
|
69
92
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
93
|
+
context "when the formatter has an format/0 method" do
|
94
|
+
it "does not register the formatter and logs an error" do
|
95
|
+
logs = capture_logs do
|
96
|
+
described_class.register "mock.incorrect", IncorrectFormatMockFormatter
|
97
|
+
end
|
98
|
+
expect(klass.registered?("mock.incorrect")).to be_falsy
|
99
|
+
expect(logs).to contains_log :error, \
|
100
|
+
"'IncorrectFormatMockFormatter does not have a format(payload) " \
|
101
|
+
"method' when initializing mock.incorrect event formatter"
|
102
|
+
end
|
103
|
+
end
|
75
104
|
|
76
|
-
|
77
|
-
|
105
|
+
context "when formatter has an format/2 method" do
|
106
|
+
it "does not register the formatter and logs an error" do
|
107
|
+
logs = capture_logs do
|
108
|
+
described_class.register "mock.incorrect", IncorrectFormatMock2Formatter
|
109
|
+
end
|
110
|
+
expect(klass.registered?("mock.incorrect")).to be_falsy
|
111
|
+
expect(logs).to contains_log :error, \
|
112
|
+
"'IncorrectFormatMock2Formatter does not have a format(payload) " \
|
113
|
+
"method' when initializing mock.incorrect event formatter"
|
114
|
+
end
|
115
|
+
end
|
78
116
|
end
|
79
117
|
|
80
|
-
|
81
|
-
|
118
|
+
context "when registering deprecated formatters" do
|
119
|
+
let(:stdout_stream) { std_stream }
|
120
|
+
let(:deprecated_formatter) do
|
121
|
+
Class.new(Appsignal::EventFormatter) do
|
122
|
+
register "mock.deprecated"
|
82
123
|
|
83
|
-
|
84
|
-
|
124
|
+
def format(_payload)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
85
128
|
|
86
|
-
|
87
|
-
|
88
|
-
|
129
|
+
it "registers deprecated formatters and logs & prints a warning" do
|
130
|
+
message = "Formatter for 'mock.deprecated' is using a deprecated registration method. " \
|
131
|
+
"This event formatter will not be loaded. " \
|
132
|
+
"Please update the formatter according to the documentation at: " \
|
133
|
+
"https://docs.appsignal.com/ruby/instrumentation/event-formatters.html"
|
134
|
+
|
135
|
+
logs = capture_logs do
|
136
|
+
capture_stdout(stdout_stream) { deprecated_formatter }
|
137
|
+
end
|
138
|
+
expect(logs).to contains_log :warn, message
|
139
|
+
expect(stdout_stream.read).to include "appsignal WARNING: #{message}"
|
140
|
+
|
141
|
+
expect(klass.deprecated_formatter_classes.keys).to include("mock.deprecated")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "initializes deprecated formatters" do
|
145
|
+
capture_stdout(stdout_stream) { deprecated_formatter }
|
146
|
+
klass.initialize_deprecated_formatters
|
89
147
|
|
90
|
-
|
91
|
-
|
92
|
-
.
|
93
|
-
|
94
|
-
klass.register("mock.twice", MockFormatter)
|
148
|
+
expect(klass.registered?("mock.deprecated")).to be_truthy
|
149
|
+
expect(klass.formatters["mock.deprecated"]).to be_instance_of(deprecated_formatter)
|
150
|
+
expect(klass.deprecated_formatter_classes["mock.deprecated"]).to eq(deprecated_formatter)
|
151
|
+
end
|
95
152
|
end
|
153
|
+
end
|
96
154
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
"
|
101
|
-
|
102
|
-
|
155
|
+
describe ".registered?" do
|
156
|
+
context "when checking by name" do
|
157
|
+
context "when there is a formatter with that name" do
|
158
|
+
it "returns true" do
|
159
|
+
klass.register "mock", MockFormatter
|
160
|
+
expect(klass.registered?("mock")).to be_truthy
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when there is no formatter with that name" do
|
165
|
+
it "returns false" do
|
166
|
+
expect(klass.registered?("nonsense")).to be_falsy
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
103
170
|
|
104
|
-
|
105
|
-
|
171
|
+
context "when checking by name and class" do
|
172
|
+
context "when there is a formatter with that name and class" do
|
173
|
+
it "returns true" do
|
174
|
+
klass.register "mock", MockFormatter
|
175
|
+
expect(klass.registered?("mock", MockFormatter)).to be_truthy
|
176
|
+
end
|
177
|
+
end
|
106
178
|
|
107
|
-
|
179
|
+
context "when there is no formatter with that name and class" do
|
180
|
+
it "returns false" do
|
181
|
+
klass.register "mock", MockFormatterDouble
|
182
|
+
expect(klass.registered?("mock", MockFormatter)).to be_falsy
|
183
|
+
end
|
184
|
+
end
|
108
185
|
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe ".unregister" do
|
189
|
+
context "when a formatter with the name is registered" do
|
190
|
+
it "unregisters the formatter has the same class" do
|
191
|
+
klass.register("mock.unregister", MockFormatter)
|
192
|
+
expect(klass.registered?("mock.unregister")).to be_truthy
|
109
193
|
|
110
|
-
|
111
|
-
|
112
|
-
capture_stdout(out_stream) { deprecated_formatter }
|
113
|
-
Appsignal::EventFormatter.initialize_deprecated_formatters
|
194
|
+
klass.unregister("mock.unregister", Hash)
|
195
|
+
expect(klass.registered?("mock.unregister")).to be_truthy
|
114
196
|
|
115
|
-
|
197
|
+
klass.unregister("mock.unregister", MockFormatter)
|
198
|
+
expect(klass.registered?("mock.unregister")).to be_falsy
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "when a formatter with the same name and class is not registered" do
|
203
|
+
it "unregisters nothing" do
|
204
|
+
expect do
|
205
|
+
expect do
|
206
|
+
klass.unregister("nonse.unregister", MockFormatter)
|
207
|
+
end.to_not(change { klass.formatters })
|
208
|
+
end.to_not(change { klass.formatter_classes })
|
209
|
+
end
|
116
210
|
end
|
117
211
|
end
|
118
212
|
|
119
|
-
|
120
|
-
|
121
|
-
|
213
|
+
describe ".format" do
|
214
|
+
context "when no formatter with the name is registered" do
|
215
|
+
it "returns nil" do
|
216
|
+
expect(klass.format("nonsense", {})).to be_nil
|
217
|
+
end
|
122
218
|
end
|
123
219
|
|
124
|
-
|
125
|
-
|
220
|
+
context "when a formatter with the name is registered" do
|
221
|
+
it "calls the formatter and use a value set in the initializer" do
|
222
|
+
klass.register "mock", MockFormatter
|
223
|
+
expect(klass.format("mock", {})).to eq ["title", "some value"]
|
224
|
+
end
|
126
225
|
end
|
127
226
|
end
|
128
227
|
end
|