appsignal 3.1.5-java → 3.2.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/.semaphore/semaphore.yml +90 -0
- data/CHANGELOG.md +13 -0
- data/README.md +1 -0
- data/build_matrix.yml +1 -0
- data/ext/agent.yml +27 -27
- data/ext/appsignal_extension.c +22 -0
- data/gemfiles/http5.gemfile +5 -0
- data/lib/appsignal/config.rb +14 -7
- data/lib/appsignal/extension/jruby.rb +14 -0
- data/lib/appsignal/hooks/http.rb +21 -0
- data/lib/appsignal/hooks.rb +1 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -1
- data/lib/appsignal/integrations/http.rb +21 -0
- data/lib/appsignal/logger.rb +126 -9
- data/lib/appsignal/probes/sidekiq.rb +46 -5
- data/lib/appsignal/utils/integration_logger.rb +19 -0
- data/lib/appsignal/utils.rb +1 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +4 -4
- data/spec/lib/appsignal/config_spec.rb +16 -0
- data/spec/lib/appsignal/hooks/http_spec.rb +39 -0
- data/spec/lib/appsignal/integrations/http_spec.rb +103 -0
- data/spec/lib/appsignal/logger_spec.rb +84 -15
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +212 -106
- data/spec/lib/appsignal/utils/integration_logger_spec.rb +25 -0
- data/spec/lib/appsignal_spec.rb +4 -4
- data/spec/support/helpers/dependency_helper.rb +4 -0
- data/spec/support/helpers/log_helpers.rb +1 -1
- metadata +13 -3
|
@@ -3,17 +3,59 @@ module Appsignal
|
|
|
3
3
|
class SidekiqProbe
|
|
4
4
|
include Helpers
|
|
5
5
|
|
|
6
|
+
class Sidekiq7Adapter
|
|
7
|
+
def self.redis_info
|
|
8
|
+
redis_info = nil
|
|
9
|
+
::Sidekiq.redis { |c| redis_info = c.info }
|
|
10
|
+
redis_info
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.hostname
|
|
14
|
+
host = nil
|
|
15
|
+
::Sidekiq.redis do |c|
|
|
16
|
+
host = c.config.host
|
|
17
|
+
end
|
|
18
|
+
host
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Sidekiq6Adapter
|
|
23
|
+
def self.redis_info
|
|
24
|
+
return unless ::Sidekiq.respond_to?(:redis_info)
|
|
25
|
+
|
|
26
|
+
::Sidekiq.redis_info
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.hostname
|
|
30
|
+
host = nil
|
|
31
|
+
::Sidekiq.redis do |c|
|
|
32
|
+
host = c.connection[:host] if c.respond_to? :connection
|
|
33
|
+
end
|
|
34
|
+
host
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
6
38
|
# @api private
|
|
7
39
|
attr_reader :config
|
|
8
40
|
|
|
41
|
+
def self.sidekiq7_and_greater?
|
|
42
|
+
Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("7.0.0")
|
|
43
|
+
end
|
|
44
|
+
|
|
9
45
|
# @api private
|
|
10
46
|
def self.dependencies_present?
|
|
47
|
+
return true if sidekiq7_and_greater?
|
|
48
|
+
return unless defined?(::Redis::VERSION) # Sidekiq <= 6
|
|
49
|
+
|
|
11
50
|
Gem::Version.new(::Redis::VERSION) >= Gem::Version.new("3.3.5")
|
|
12
51
|
end
|
|
13
52
|
|
|
14
53
|
def initialize(config = {})
|
|
15
54
|
@config = config
|
|
16
55
|
@cache = {}
|
|
56
|
+
is_sidekiq7 = self.class.sidekiq7_and_greater?
|
|
57
|
+
@adapter = is_sidekiq7 ? Sidekiq7Adapter : Sidekiq6Adapter
|
|
58
|
+
|
|
17
59
|
config_string = " with config: #{config}" unless config.empty?
|
|
18
60
|
Appsignal.logger.debug("Initializing Sidekiq probe#{config_string}")
|
|
19
61
|
require "sidekiq/api"
|
|
@@ -28,11 +70,11 @@ module Appsignal
|
|
|
28
70
|
|
|
29
71
|
private
|
|
30
72
|
|
|
31
|
-
attr_reader :cache
|
|
73
|
+
attr_reader :adapter, :cache
|
|
32
74
|
|
|
33
75
|
def track_redis_info
|
|
34
|
-
|
|
35
|
-
|
|
76
|
+
redis_info = adapter.redis_info
|
|
77
|
+
return unless redis_info
|
|
36
78
|
|
|
37
79
|
gauge "connection_count", redis_info.fetch("connected_clients")
|
|
38
80
|
gauge "memory_usage", redis_info.fetch("used_memory")
|
|
@@ -81,8 +123,7 @@ module Appsignal
|
|
|
81
123
|
return @hostname
|
|
82
124
|
end
|
|
83
125
|
|
|
84
|
-
host =
|
|
85
|
-
::Sidekiq.redis { |c| host = c.connection[:host] }
|
|
126
|
+
host = adapter.hostname
|
|
86
127
|
Appsignal.logger.debug "Sidekiq probe: Using Redis server hostname " \
|
|
87
128
|
"#{host.inspect} as hostname"
|
|
88
129
|
@hostname = host
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Appsignal
|
|
2
|
+
module Utils
|
|
3
|
+
# Subclass of logger with method to only log a warning once
|
|
4
|
+
# prevents the local log from filling up with repeated messages.
|
|
5
|
+
class IntegrationLogger < ::Logger
|
|
6
|
+
def seen_keys
|
|
7
|
+
@seen_keys ||= Set.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def warn_once_then_debug(key, message)
|
|
11
|
+
if !seen_keys.add?(key).nil?
|
|
12
|
+
warn message
|
|
13
|
+
else
|
|
14
|
+
debug message
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/appsignal/utils.rb
CHANGED
data/lib/appsignal/version.rb
CHANGED
data/lib/appsignal.rb
CHANGED
|
@@ -175,8 +175,8 @@ module Appsignal
|
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
def logger
|
|
178
|
-
@logger ||= Appsignal::
|
|
179
|
-
l.level = Logger::INFO
|
|
178
|
+
@logger ||= Appsignal::Utils::IntegrationLogger.new(in_memory_log).tap do |l|
|
|
179
|
+
l.level = ::Logger::INFO
|
|
180
180
|
l.formatter = log_formatter("appsignal")
|
|
181
181
|
end
|
|
182
182
|
end
|
|
@@ -254,12 +254,12 @@ module Appsignal
|
|
|
254
254
|
private
|
|
255
255
|
|
|
256
256
|
def start_stdout_logger
|
|
257
|
-
@logger = Appsignal::
|
|
257
|
+
@logger = Appsignal::Utils::IntegrationLogger.new($stdout)
|
|
258
258
|
logger.formatter = log_formatter("appsignal")
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
def start_file_logger(path)
|
|
262
|
-
@logger = Appsignal::
|
|
262
|
+
@logger = Appsignal::Utils::IntegrationLogger.new(path)
|
|
263
263
|
logger.formatter = log_formatter
|
|
264
264
|
rescue SystemCallError => error
|
|
265
265
|
start_stdout_logger
|
|
@@ -166,10 +166,12 @@ describe Appsignal::Config do
|
|
|
166
166
|
:ignore_actions => [],
|
|
167
167
|
:ignore_errors => [],
|
|
168
168
|
:ignore_namespaces => [],
|
|
169
|
+
:instrument_http_rb => true,
|
|
169
170
|
:instrument_net_http => true,
|
|
170
171
|
:instrument_redis => true,
|
|
171
172
|
:instrument_sequel => true,
|
|
172
173
|
:log => "file",
|
|
174
|
+
:logging_endpoint => "https://appsignal-endpoint.net",
|
|
173
175
|
:name => "TestApp",
|
|
174
176
|
:push_api_key => "abc",
|
|
175
177
|
:request_headers => [],
|
|
@@ -536,6 +538,18 @@ describe Appsignal::Config do
|
|
|
536
538
|
end
|
|
537
539
|
end
|
|
538
540
|
end
|
|
541
|
+
|
|
542
|
+
describe ":logging_endpoint" do
|
|
543
|
+
subject { config[:logging_endpoint] }
|
|
544
|
+
|
|
545
|
+
context "with a non-standard port" do
|
|
546
|
+
let(:config) { project_fixture_config("production", :logging_endpoint => "http://localhost:4567") }
|
|
547
|
+
|
|
548
|
+
it "keeps the port" do
|
|
549
|
+
expect(subject).to eq "http://localhost:4567"
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
end
|
|
539
553
|
end
|
|
540
554
|
|
|
541
555
|
describe "#[]" do
|
|
@@ -577,6 +591,7 @@ describe Appsignal::Config do
|
|
|
577
591
|
describe "#write_to_environment" do
|
|
578
592
|
let(:config) { project_fixture_config(:production) }
|
|
579
593
|
before do
|
|
594
|
+
config[:logging_endpoint] = "http://localhost:123"
|
|
580
595
|
config[:http_proxy] = "http://localhost"
|
|
581
596
|
config[:ignore_actions] = %w[action1 action2]
|
|
582
597
|
config[:ignore_errors] = %w[ExampleStandardError AnotherError]
|
|
@@ -600,6 +615,7 @@ describe Appsignal::Config do
|
|
|
600
615
|
expect(ENV["_APPSIGNAL_DEBUG_LOGGING"]).to eq "false"
|
|
601
616
|
expect(ENV["_APPSIGNAL_LOG"]).to eq "stdout"
|
|
602
617
|
expect(ENV["_APPSIGNAL_LOG_FILE_PATH"]).to end_with("/tmp/appsignal.log")
|
|
618
|
+
expect(ENV["_APPSIGNAL_LOGGING_ENDPOINT"]).to eq "http://localhost:123"
|
|
603
619
|
expect(ENV["_APPSIGNAL_PUSH_API_ENDPOINT"]).to eq "https://push.appsignal.com"
|
|
604
620
|
expect(ENV["_APPSIGNAL_PUSH_API_KEY"]).to eq "abc"
|
|
605
621
|
expect(ENV["_APPSIGNAL_APP_NAME"]).to eq "TestApp"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe Appsignal::Hooks::HttpHook do
|
|
4
|
+
before :context do
|
|
5
|
+
start_agent
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
if DependencyHelper.http_present?
|
|
9
|
+
context "with instrument_http_rb set to true" do
|
|
10
|
+
describe "#dependencies_present?" do
|
|
11
|
+
subject { described_class.new.dependencies_present? }
|
|
12
|
+
|
|
13
|
+
it { is_expected.to be_truthy }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "installs the HTTP plugin" do
|
|
17
|
+
expect(HTTP::Client.included_modules)
|
|
18
|
+
.to include(Appsignal::Integrations::HttpIntegration)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "with instrument_http_rb set to false" do
|
|
23
|
+
before { Appsignal.config.config_hash[:instrument_http_rb] = false }
|
|
24
|
+
after { Appsignal.config.config_hash[:instrument_http_rb] = true }
|
|
25
|
+
|
|
26
|
+
describe "#dependencies_present?" do
|
|
27
|
+
subject { described_class.new.dependencies_present? }
|
|
28
|
+
|
|
29
|
+
it { is_expected.to be_falsy }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
describe "#dependencies_present?" do
|
|
34
|
+
subject { described_class.new.dependencies_present? }
|
|
35
|
+
|
|
36
|
+
it { is_expected.to be_falsy }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if DependencyHelper.http_present?
|
|
4
|
+
require "appsignal/integrations/http"
|
|
5
|
+
|
|
6
|
+
describe Appsignal::Integrations::HttpIntegration do
|
|
7
|
+
let(:transaction) { http_request_transaction }
|
|
8
|
+
|
|
9
|
+
around do |example|
|
|
10
|
+
keep_transactions { example.run }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
before :context do
|
|
14
|
+
start_agent
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
set_current_transaction(transaction)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "instruments a HTTP request" do
|
|
22
|
+
stub_request(:get, "http://www.google.com")
|
|
23
|
+
|
|
24
|
+
HTTP.get("http://www.google.com")
|
|
25
|
+
|
|
26
|
+
transaction_hash = transaction.to_h
|
|
27
|
+
expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
|
|
28
|
+
expect(transaction_hash["events"].first).to include(
|
|
29
|
+
"body" => "",
|
|
30
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
31
|
+
"name" => "request.http_rb",
|
|
32
|
+
"title" => "GET http://www.google.com"
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "instruments a HTTPS request" do
|
|
37
|
+
stub_request(:get, "https://www.google.com")
|
|
38
|
+
|
|
39
|
+
HTTP.get("https://www.google.com")
|
|
40
|
+
|
|
41
|
+
transaction_hash = transaction.to_h
|
|
42
|
+
expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
|
|
43
|
+
expect(transaction_hash["events"].first).to include(
|
|
44
|
+
"body" => "",
|
|
45
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
46
|
+
"name" => "request.http_rb",
|
|
47
|
+
"title" => "GET https://www.google.com"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "with request parameters" do
|
|
52
|
+
it "does not include the query parameters in the title" do
|
|
53
|
+
stub_request(:get, "https://www.google.com?q=Appsignal")
|
|
54
|
+
|
|
55
|
+
HTTP.get("https://www.google.com", :params => { :q => "Appsignal" })
|
|
56
|
+
|
|
57
|
+
expect(transaction.to_h["events"].first).to include(
|
|
58
|
+
"body" => "",
|
|
59
|
+
"title" => "GET https://www.google.com"
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "does not include the request body in the title" do
|
|
64
|
+
stub_request(:post, "https://www.google.com")
|
|
65
|
+
.with(:body => { :q => "Appsignal" }.to_json)
|
|
66
|
+
|
|
67
|
+
HTTP.post("https://www.google.com", :json => { :q => "Appsignal" })
|
|
68
|
+
|
|
69
|
+
expect(transaction.to_h["events"].first).to include(
|
|
70
|
+
"body" => "",
|
|
71
|
+
"title" => "POST https://www.google.com"
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context "with an HTTP exception" do
|
|
77
|
+
let(:error) { ExampleException.new("oh no!") }
|
|
78
|
+
|
|
79
|
+
it "reports the exception and re-raises it" do
|
|
80
|
+
stub_request(:get, "https://www.google.com").and_raise(error)
|
|
81
|
+
|
|
82
|
+
expect do
|
|
83
|
+
HTTP.get("https://www.google.com")
|
|
84
|
+
end.to raise_error(ExampleException)
|
|
85
|
+
|
|
86
|
+
transaction_hash = transaction.to_h
|
|
87
|
+
expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
|
|
88
|
+
expect(transaction_hash["events"].first).to include(
|
|
89
|
+
"body" => "",
|
|
90
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
91
|
+
"name" => "request.http_rb",
|
|
92
|
+
"title" => "GET https://www.google.com"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
expect(transaction_hash["error"]).to include(
|
|
96
|
+
"backtrace" => kind_of(String),
|
|
97
|
+
"name" => error.class.name,
|
|
98
|
+
"message" => error.message
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -1,25 +1,94 @@
|
|
|
1
1
|
describe Appsignal::Logger do
|
|
2
|
-
let(:
|
|
3
|
-
let(:logger)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
let(:level) { ::Logger::DEBUG }
|
|
3
|
+
let(:logger) { Appsignal::Logger.new("group", level) }
|
|
4
|
+
|
|
5
|
+
it "should not create a logger with a nil group" do
|
|
6
|
+
expect do
|
|
7
|
+
Appsignal::Logger.new(nil, level)
|
|
8
|
+
end.to raise_error(TypeError)
|
|
7
9
|
end
|
|
8
10
|
|
|
9
|
-
describe "#
|
|
10
|
-
it "
|
|
11
|
-
expect(
|
|
11
|
+
describe "#add" do
|
|
12
|
+
it "should log with a level and message" do
|
|
13
|
+
expect(Appsignal::Extension).to receive(:log)
|
|
14
|
+
.with("group", 3, "Log message", instance_of(Appsignal::Extension::Data))
|
|
15
|
+
logger.add(::Logger::INFO, "Log message")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should log with a block" do
|
|
19
|
+
expect(Appsignal::Extension).to receive(:log)
|
|
20
|
+
.with("group", 3, "Log message", instance_of(Appsignal::Extension::Data))
|
|
21
|
+
logger.add(::Logger::INFO) do
|
|
22
|
+
"Log message"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should log with a level, message and group" do
|
|
27
|
+
expect(Appsignal::Extension).to receive(:log)
|
|
28
|
+
.with("other_group", 3, "Log message", instance_of(Appsignal::Extension::Data))
|
|
29
|
+
logger.add(::Logger::INFO, "Log message", "other_group")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should return with a nil message" do
|
|
33
|
+
expect(Appsignal::Extension).not_to receive(:log)
|
|
34
|
+
logger.add(::Logger::INFO, nil)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "with debug log level" do
|
|
38
|
+
let(:level) { ::Logger::INFO }
|
|
39
|
+
|
|
40
|
+
it "should skip logging if the level is too low" do
|
|
41
|
+
expect(Appsignal::Extension).not_to receive(:log)
|
|
42
|
+
logger.add(::Logger::DEBUG, "Log message")
|
|
43
|
+
end
|
|
12
44
|
end
|
|
13
45
|
end
|
|
14
46
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
47
|
+
[
|
|
48
|
+
["debug", 2, ::Logger::INFO],
|
|
49
|
+
["info", 3, ::Logger::WARN],
|
|
50
|
+
["warn", 5, ::Logger::ERROR],
|
|
51
|
+
["error", 6, ::Logger::FATAL],
|
|
52
|
+
["fatal", 7, nil]
|
|
53
|
+
].each do |method|
|
|
54
|
+
describe "##{method[0]}" do
|
|
55
|
+
it "should log with a message" do
|
|
56
|
+
expect(Appsignal::Utils::Data).to receive(:generate)
|
|
57
|
+
.with({})
|
|
58
|
+
.and_call_original
|
|
59
|
+
expect(Appsignal::Extension).to receive(:log)
|
|
60
|
+
.with("group", method[1], "Log message", instance_of(Appsignal::Extension::Data))
|
|
61
|
+
|
|
62
|
+
logger.send(method[0], "Log message")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should log with a block" do
|
|
66
|
+
expect(Appsignal::Utils::Data).to receive(:generate)
|
|
67
|
+
.with({})
|
|
68
|
+
.and_call_original
|
|
69
|
+
expect(Appsignal::Extension).to receive(:log)
|
|
70
|
+
.with("group", method[1], "Log message", instance_of(Appsignal::Extension::Data))
|
|
71
|
+
|
|
72
|
+
logger.send(method[0]) do
|
|
73
|
+
"Log message"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should return with a nil message" do
|
|
78
|
+
expect(Appsignal::Extension).not_to receive(:log)
|
|
79
|
+
logger.send(method[0])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if method[2]
|
|
83
|
+
context "with a lower log level" do
|
|
84
|
+
let(:level) { method[2] }
|
|
19
85
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
86
|
+
it "should skip logging if the level is too low" do
|
|
87
|
+
expect(Appsignal::Extension).not_to receive(:log)
|
|
88
|
+
logger.send(method[0], "Log message")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
23
92
|
end
|
|
24
93
|
end
|
|
25
94
|
end
|