appsignal 3.0.6-java → 3.0.10-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/.gitmodules +3 -0
- data/.rubocop.yml +2 -2
- data/.semaphore/semaphore.yml +460 -39
- data/CHANGELOG.md +23 -0
- data/README.md +22 -16
- data/Rakefile +11 -10
- data/build_matrix.yml +106 -32
- data/ext/agent.yml +24 -17
- data/ext/base.rb +7 -5
- data/lib/appsignal/cli/diagnose.rb +10 -5
- data/lib/appsignal/config.rb +1 -0
- data/lib/appsignal/hooks/puma.rb +1 -16
- data/lib/appsignal/probes.rb +0 -1
- data/lib/appsignal/system.rb +30 -3
- data/lib/appsignal/transaction.rb +1 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +146 -17
- data/script/install_lintje +18 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +4 -2
- data/spec/lib/appsignal/hooks/puma_spec.rb +0 -46
- data/spec/lib/appsignal/system_spec.rb +30 -0
- data/spec/lib/appsignal/transaction_spec.rb +7 -0
- data/spec/lib/puma/appsignal_spec.rb +246 -66
- data/spec/support/helpers/wait_for_helper.rb +16 -4
- metadata +4 -5
- data/lib/appsignal/probes/puma.rb +0 -61
- data/spec/lib/appsignal/probes/puma_spec.rb +0 -180
@@ -105,7 +105,6 @@ module Appsignal
|
|
105
105
|
paths_report = Paths.new
|
106
106
|
data[:paths] = paths_report.report
|
107
107
|
print_paths_section(paths_report)
|
108
|
-
print_empty_line
|
109
108
|
|
110
109
|
transmit_report_to_appsignal if send_report_to_appsignal?(options)
|
111
110
|
end
|
@@ -327,6 +326,7 @@ module Appsignal
|
|
327
326
|
puts "AppSignal library"
|
328
327
|
data_section :library do
|
329
328
|
save :language, "ruby"
|
329
|
+
puts_value "Language", "Ruby"
|
330
330
|
puts_and_save :package_version, "Gem version", Appsignal::VERSION
|
331
331
|
puts_and_save :agent_version, "Agent version", Appsignal::Extension.agent_version
|
332
332
|
puts_and_save :extension_loaded, "Extension loaded", Appsignal.extension_loaded
|
@@ -399,6 +399,7 @@ module Appsignal
|
|
399
399
|
puts " Architecture: #{report["architecture"]}"
|
400
400
|
puts " Target: #{report["target"]}"
|
401
401
|
puts " Musl override: #{report["musl_override"]}"
|
402
|
+
puts " Linux ARM override: #{report["linux_arm_override"]}"
|
402
403
|
puts " Library type: #{report["library_type"]}"
|
403
404
|
puts " Source: #{report["source"]}" if report["source"] != "remote"
|
404
405
|
puts " Dependencies: #{report["dependencies"]}"
|
@@ -416,7 +417,7 @@ module Appsignal
|
|
416
417
|
rbconfig = RbConfig::CONFIG
|
417
418
|
puts "Host information"
|
418
419
|
data_section :host do
|
419
|
-
puts_and_save :architecture, "Architecture",
|
420
|
+
puts_and_save :architecture, "Architecture", Appsignal::System.agent_architecture
|
420
421
|
|
421
422
|
os_label = os = rbconfig["host_os"]
|
422
423
|
os_label = "#{os} (Microsoft Windows is not supported.)" if Gem.win_platform?
|
@@ -573,9 +574,13 @@ module Appsignal
|
|
573
574
|
"(file: #{ownership[:user]}:#{ownership[:uid]}, " \
|
574
575
|
"process: #{process_user[:user]}:#{process_user[:uid]})"
|
575
576
|
puts_value "Ownership?", owner, :level => 2
|
576
|
-
|
577
|
-
|
578
|
-
|
577
|
+
|
578
|
+
if path.key?(:content)
|
579
|
+
puts " Contents (last 10 lines):"
|
580
|
+
puts path[:content].last(10)
|
581
|
+
else
|
582
|
+
print_empty_line
|
583
|
+
end
|
579
584
|
end
|
580
585
|
|
581
586
|
def print_empty_line
|
data/lib/appsignal/config.rb
CHANGED
@@ -290,6 +290,7 @@ module Appsignal
|
|
290
290
|
ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = config_hash[:files_world_accessible].to_s
|
291
291
|
ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"] = config_hash[:transaction_debug_mode].to_s
|
292
292
|
ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = config_hash[:send_environment_metadata].to_s
|
293
|
+
ENV["_APPSIGNAL_ENABLE_STATSD"] = "true"
|
293
294
|
ENV["_APP_REVISION"] = config_hash[:revision].to_s
|
294
295
|
end
|
295
296
|
|
data/lib/appsignal/hooks/puma.rb
CHANGED
@@ -11,23 +11,8 @@ module Appsignal
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def install
|
14
|
-
if ::Puma.respond_to?(:stats) && !defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)
|
15
|
-
# Only install the minutely probe if a user isn't using our Puma
|
16
|
-
# plugin, which lives in `lib/puma/appsignal.rb`. This plugin defines
|
17
|
-
# the {APPSIGNAL_PUMA_PLUGIN_LOADED} constant.
|
18
|
-
#
|
19
|
-
# We prefer people use the AppSignal Puma plugin. This fallback is
|
20
|
-
# only there when users relied on our *magic* integration.
|
21
|
-
#
|
22
|
-
# Using the Puma plugin, the minutely probe thread will still run in
|
23
|
-
# Puma workers, for other non-Puma probes, but the Puma probe only
|
24
|
-
# runs in the Puma main process.
|
25
|
-
# For more information:
|
26
|
-
# https://docs.appsignal.com/ruby/integrations/puma.html
|
27
|
-
Appsignal::Minutely.probes.register :puma, ::Appsignal::Probes::PumaProbe
|
28
|
-
end
|
29
|
-
|
30
14
|
return unless defined?(::Puma::Cluster)
|
15
|
+
|
31
16
|
# For clustered mode with multiple workers
|
32
17
|
::Puma::Cluster.send(:prepend, Module.new do
|
33
18
|
def stop_workers
|
data/lib/appsignal/probes.rb
CHANGED
data/lib/appsignal/system.rb
CHANGED
@@ -8,6 +8,7 @@ module Appsignal
|
|
8
8
|
# @api private
|
9
9
|
module System
|
10
10
|
LINUX_TARGET = "linux".freeze
|
11
|
+
LINUX_ARM_ARCHITECTURE = "aarch64".freeze
|
11
12
|
MUSL_TARGET = "linux-musl".freeze
|
12
13
|
FREEBSD_TARGET = "freebsd".freeze
|
13
14
|
GEM_EXT_PATH = File.expand_path("../../../ext", __FILE__).freeze
|
@@ -18,15 +19,18 @@ module Appsignal
|
|
18
19
|
|
19
20
|
# Detect agent and extension platform build
|
20
21
|
#
|
21
|
-
# Used by `ext
|
22
|
+
# Used by `ext/*` to select which build it should download and
|
22
23
|
# install.
|
23
24
|
#
|
24
|
-
# Use `export APPSIGNAL_BUILD_FOR_MUSL=1` if the detection doesn't work
|
25
|
-
#
|
25
|
+
# - Use `export APPSIGNAL_BUILD_FOR_MUSL=1` if the detection doesn't work
|
26
|
+
# and to force selection of the musl build.
|
27
|
+
# - Use `export APPSIGNAL_BUILD_FOR_LINUX_ARM=1` to enable the experimental
|
28
|
+
# Linux ARM build.
|
26
29
|
#
|
27
30
|
# @api private
|
28
31
|
# @return [String]
|
29
32
|
def self.agent_platform
|
33
|
+
return LINUX_TARGET if force_linux_arm_build?
|
30
34
|
return MUSL_TARGET if force_musl_build?
|
31
35
|
|
32
36
|
host_os = RbConfig::CONFIG["host_os"].downcase
|
@@ -53,6 +57,22 @@ module Appsignal
|
|
53
57
|
local_os
|
54
58
|
end
|
55
59
|
|
60
|
+
# Detect agent and extension architecture build
|
61
|
+
#
|
62
|
+
# Used by the `ext/*` tasks to select which architecture build it should download and install.
|
63
|
+
#
|
64
|
+
# - Use `export APPSIGNAL_BUILD_FOR_LINUX_ARM=1` to enable the experimental
|
65
|
+
# Linux ARM build.
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @return [String]
|
69
|
+
def self.agent_architecture
|
70
|
+
return LINUX_ARM_ARCHITECTURE if force_linux_arm_build?
|
71
|
+
|
72
|
+
# Fallback on the Ruby
|
73
|
+
RbConfig::CONFIG["host_cpu"]
|
74
|
+
end
|
75
|
+
|
56
76
|
# Returns whether or not the musl build was forced by the user.
|
57
77
|
#
|
58
78
|
# @api private
|
@@ -60,6 +80,13 @@ module Appsignal
|
|
60
80
|
%w[true 1].include?(ENV["APPSIGNAL_BUILD_FOR_MUSL"])
|
61
81
|
end
|
62
82
|
|
83
|
+
# Returns whether or not the linux ARM build was selected by the user.
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def self.force_linux_arm_build?
|
87
|
+
%w[true 1].include?(ENV["APPSIGNAL_BUILD_FOR_LINUX_ARM"])
|
88
|
+
end
|
89
|
+
|
63
90
|
# @api private
|
64
91
|
def self.versionify(version)
|
65
92
|
Gem::Version.new(version)
|
@@ -526,7 +526,7 @@ module Appsignal
|
|
526
526
|
end
|
527
527
|
|
528
528
|
def cleaned_backtrace(backtrace)
|
529
|
-
if defined?(::Rails) && backtrace
|
529
|
+
if defined?(::Rails) && Rails.respond_to?(:backtrace_cleaner) && backtrace
|
530
530
|
::Rails.backtrace_cleaner.clean(backtrace, nil)
|
531
531
|
else
|
532
532
|
backtrace
|
data/lib/appsignal/version.rb
CHANGED
@@ -1,27 +1,156 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
2
4
|
|
3
5
|
# AppSignal Puma plugin
|
4
6
|
#
|
5
|
-
# This plugin ensures
|
6
|
-
# minutely probe in the Puma master process.
|
7
|
-
#
|
8
|
-
# The constant {APPSIGNAL_PUMA_PLUGIN_LOADED} is here to mark the Plugin as
|
9
|
-
# loaded by the rest of the AppSignal gem. This ensures that the Puma minutely
|
10
|
-
# probe is not also started in every Puma workers, which was the old behavior.
|
11
|
-
# See {Appsignal::Hooks::PumaHook#install} for more information.
|
7
|
+
# This plugin ensures Puma metrics are sent to the AppSignal agent using StatsD.
|
12
8
|
#
|
13
9
|
# For even more information:
|
14
10
|
# https://docs.appsignal.com/ruby/integrations/puma.html
|
15
|
-
Puma::Plugin.create do
|
16
|
-
def start(launcher
|
17
|
-
launcher
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
Puma::Plugin.create do # rubocop:disable Metrics/BlockLength
|
12
|
+
def start(launcher)
|
13
|
+
@launcher = launcher
|
14
|
+
@launcher.events.debug "AppSignal: Puma plugin start."
|
15
|
+
in_background do
|
16
|
+
@launcher.events.debug "AppSignal: Start Puma stats collection loop."
|
17
|
+
plugin = AppsignalPumaPlugin.new
|
18
|
+
|
19
|
+
loop do
|
20
|
+
begin
|
21
|
+
# Implement similar behavior to minutely probes.
|
22
|
+
# Initial sleep to wait until the app is fully initalized.
|
23
|
+
# Then loop every 60 seconds and collect the Puma stats as AppSignal
|
24
|
+
# metrics.
|
25
|
+
sleep sleep_time
|
26
|
+
|
27
|
+
@launcher.events.debug "AppSignal: Collecting Puma stats."
|
28
|
+
stats = fetch_puma_stats
|
29
|
+
if stats
|
30
|
+
plugin.call(stats)
|
31
|
+
else
|
32
|
+
@launcher.events.log "AppSignal: No Puma stats to report."
|
33
|
+
end
|
34
|
+
rescue StandardError => error
|
35
|
+
log_error "Error while processing metrics.", error
|
36
|
+
end
|
22
37
|
end
|
23
|
-
|
24
|
-
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def sleep_time
|
44
|
+
60 # seconds
|
45
|
+
end
|
46
|
+
|
47
|
+
def log_error(message, error)
|
48
|
+
@launcher.events.log "AppSignal: #{message}\n" \
|
49
|
+
"#{error.class}: #{error.message}\n#{error.backtrace.join("\n")}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def fetch_puma_stats
|
53
|
+
if Puma.respond_to? :stats_hash # Puma >= 5.0.0
|
54
|
+
Puma.stats_hash
|
55
|
+
elsif Puma.respond_to? :stats # Puma < 5.0.0
|
56
|
+
# Puma.stats_hash returns symbolized keys as well
|
57
|
+
JSON.parse Puma.stats, :symbolize_names => true
|
58
|
+
end
|
59
|
+
rescue StandardError => error
|
60
|
+
log_error "Error while parsing Puma stats.", error
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# AppsignalPumaPlugin
|
66
|
+
#
|
67
|
+
# Class to handle the logic of translating the Puma stats to AppSignal metrics.
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
class AppsignalPumaPlugin
|
71
|
+
def initialize
|
72
|
+
@hostname = fetch_hostname
|
73
|
+
@statsd = Statsd.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def call(stats)
|
77
|
+
counts = {}
|
78
|
+
count_keys = [:backlog, :running, :pool_capacity, :max_threads]
|
79
|
+
|
80
|
+
if stats[:worker_status] # Clustered mode - Multiple workers
|
81
|
+
stats[:worker_status].each do |worker|
|
82
|
+
stat = worker[:last_status]
|
83
|
+
count_keys.each do |key|
|
84
|
+
count_if_present counts, key, stat
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
gauge(:workers, stats[:workers], :type => :count)
|
89
|
+
gauge(:workers, stats[:booted_workers], :type => :booted)
|
90
|
+
gauge(:workers, stats[:old_workers], :type => :old)
|
91
|
+
else # Single mode - Single worker
|
92
|
+
count_keys.each do |key|
|
93
|
+
count_if_present counts, key, stats
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
gauge(:connection_backlog, counts[:backlog]) if counts[:backlog]
|
98
|
+
gauge(:pool_capacity, counts[:pool_capacity]) if counts[:pool_capacity]
|
99
|
+
gauge(:threads, counts[:running], :type => :running) if counts[:running]
|
100
|
+
gauge(:threads, counts[:max_threads], :type => :max) if counts[:max_threads]
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
attr_reader :hostname
|
106
|
+
|
107
|
+
def fetch_hostname
|
108
|
+
# Configure hostname as reported for the Puma metrics with the
|
109
|
+
# APPSIGNAL_HOSTNAME environment variable.
|
110
|
+
env_hostname = ENV["APPSIGNAL_HOSTNAME"]
|
111
|
+
return env_hostname if env_hostname
|
112
|
+
|
113
|
+
# Auto detect hostname as fallback. May be inaccurate.
|
114
|
+
Socket.gethostname
|
115
|
+
end
|
116
|
+
|
117
|
+
def gauge(field, count, tags = {})
|
118
|
+
@statsd.gauge("puma_#{field}", count, tags.merge(:hostname => hostname))
|
119
|
+
end
|
120
|
+
|
121
|
+
def count_if_present(counts, key, stats)
|
122
|
+
stat_value = stats[key]
|
123
|
+
return unless stat_value
|
124
|
+
|
125
|
+
counts[key] ||= 0
|
126
|
+
counts[key] += stat_value
|
127
|
+
end
|
128
|
+
|
129
|
+
class Statsd
|
130
|
+
def initialize
|
131
|
+
# StatsD server location as configured in AppSignal agent StatsD server.
|
132
|
+
@host = "127.0.0.1"
|
133
|
+
@port = 8125
|
134
|
+
end
|
135
|
+
|
136
|
+
def gauge(metric_name, value, tags)
|
137
|
+
send_metric "g", metric_name, value, tags
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
attr_reader :host, :port
|
143
|
+
|
144
|
+
def send_metric(type, metric_name, metric_value, tags_hash)
|
145
|
+
tags = tags_hash.map { |key, value| "#{key}:#{value}" }.join(",")
|
146
|
+
data = "#{metric_name}:#{metric_value}|#{type}|##{tags}"
|
147
|
+
|
148
|
+
# Open (and close) a new socket every time because we don't know when the
|
149
|
+
# plugin will exit and when to cleanly close the socket connection.
|
150
|
+
socket = UDPSocket.new
|
151
|
+
socket.send(data, 0, host, port)
|
152
|
+
ensure
|
153
|
+
socket && socket.close
|
25
154
|
end
|
26
155
|
end
|
27
156
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -eu
|
4
|
+
|
5
|
+
mkdir -p $HOME/bin
|
6
|
+
cache_key=v1-lintje-$LINTJE_VERSION
|
7
|
+
cache restore $cache_key
|
8
|
+
|
9
|
+
# File exists and is executable
|
10
|
+
if [ -x "$HOME/bin/lintje" ]; then
|
11
|
+
echo "Restored Lintje $LINTJE_VERSION from cache"
|
12
|
+
else
|
13
|
+
echo "Downloading Lintje $LINTJE_VERSION"
|
14
|
+
curl -L \
|
15
|
+
https://github.com/tombruijn/lintje/releases/download/v$LINTJE_VERSION/x86_64-unknown-linux-gnu.tar.gz | \
|
16
|
+
tar -xz --directory $HOME/bin
|
17
|
+
cache store $cache_key $HOME/bin/lintje
|
18
|
+
fi
|
@@ -270,9 +270,10 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
270
270
|
"build" => {
|
271
271
|
"time" => kind_of(String),
|
272
272
|
"package_path" => File.expand_path("../../../../../", __FILE__),
|
273
|
-
"architecture" =>
|
273
|
+
"architecture" => Appsignal::System.agent_architecture,
|
274
274
|
"target" => Appsignal::System.agent_platform,
|
275
275
|
"musl_override" => false,
|
276
|
+
"linux_arm_override" => false,
|
276
277
|
"library_type" => jruby ? "dynamic" : "static",
|
277
278
|
"source" => "remote",
|
278
279
|
"dependencies" => kind_of(Hash),
|
@@ -301,9 +302,10 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
301
302
|
" Checksum: verified",
|
302
303
|
"Build details",
|
303
304
|
" Install time: 20",
|
304
|
-
" Architecture: #{
|
305
|
+
" Architecture: #{Appsignal::System.agent_architecture}",
|
305
306
|
" Target: #{Appsignal::System.agent_platform}",
|
306
307
|
" Musl override: false",
|
308
|
+
" Linux ARM override: false",
|
307
309
|
" Library type: #{jruby ? "dynamic" : "static"}",
|
308
310
|
" Dependencies: {",
|
309
311
|
" Flags: {",
|
@@ -35,29 +35,6 @@ describe Appsignal::Hooks::PumaHook do
|
|
35
35
|
# Does not error on call
|
36
36
|
Appsignal::Hooks::PumaHook.new.install
|
37
37
|
end
|
38
|
-
|
39
|
-
context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
40
|
-
before do
|
41
|
-
# Set in lib/puma/appsignal.rb
|
42
|
-
APPSIGNAL_PUMA_PLUGIN_LOADED = true
|
43
|
-
end
|
44
|
-
after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
|
45
|
-
|
46
|
-
it "does not add the Puma minutely probe" do
|
47
|
-
Appsignal::Hooks::PumaHook.new.install
|
48
|
-
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
53
|
-
it "adds the Puma minutely probe" do
|
54
|
-
expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
|
55
|
-
|
56
|
-
Appsignal::Hooks::PumaHook.new.install
|
57
|
-
probe = Appsignal::Minutely.probes[:puma]
|
58
|
-
expect(probe).to eql(Appsignal::Probes::PumaProbe)
|
59
|
-
end
|
60
|
-
end
|
61
38
|
end
|
62
39
|
|
63
40
|
context "when in clustered mode" do
|
@@ -81,29 +58,6 @@ describe Appsignal::Hooks::PumaHook do
|
|
81
58
|
cluster.stop_workers
|
82
59
|
expect(cluster.instance_variable_get(:@called)).to be(true)
|
83
60
|
end
|
84
|
-
|
85
|
-
context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
86
|
-
before do
|
87
|
-
# Set in lib/puma/appsignal.rb
|
88
|
-
APPSIGNAL_PUMA_PLUGIN_LOADED = true
|
89
|
-
end
|
90
|
-
after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
|
91
|
-
|
92
|
-
it "does not add the Puma minutely probe" do
|
93
|
-
Appsignal::Hooks::PumaHook.new.install
|
94
|
-
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
99
|
-
it "adds the Puma minutely probe" do
|
100
|
-
expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
|
101
|
-
|
102
|
-
Appsignal::Hooks::PumaHook.new.install
|
103
|
-
probe = Appsignal::Minutely.probes[:puma]
|
104
|
-
expect(probe).to eql(Appsignal::Probes::PumaProbe)
|
105
|
-
end
|
106
|
-
end
|
107
61
|
end
|
108
62
|
end
|
109
63
|
end
|