appsignal 2.8.0.alpha.1 → 2.8.0
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/.travis.yml +13 -14
- data/CHANGELOG.md +27 -5
- data/ext/agent.yml +38 -35
- data/ext/extconf.rb +12 -0
- data/lib/appsignal/cli.rb +3 -0
- data/lib/appsignal/cli/diagnose.rb +191 -177
- data/lib/appsignal/cli/diagnose/paths.rb +91 -0
- data/lib/appsignal/cli/diagnose/utils.rb +36 -0
- data/lib/appsignal/cli/install.rb +2 -2
- data/lib/appsignal/config.rb +71 -23
- data/lib/appsignal/event_formatter.rb +11 -5
- data/lib/appsignal/garbage_collection_profiler.rb +11 -0
- data/lib/appsignal/system.rb +6 -4
- data/lib/appsignal/transaction.rb +2 -1
- data/lib/appsignal/utils.rb +1 -0
- data/lib/appsignal/utils/deprecation_message.rb +10 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +37 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +498 -278
- data/spec/lib/appsignal/config_spec.rb +93 -36
- data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +1 -1
- data/spec/lib/appsignal/event_formatter_spec.rb +12 -7
- data/spec/lib/appsignal/extension_spec.rb +5 -2
- data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +10 -0
- data/spec/lib/appsignal/system_spec.rb +1 -1
- data/spec/lib/appsignal/transaction_spec.rb +24 -7
- data/spec/support/helpers/std_streams_helper.rb +31 -8
- metadata +9 -4
@@ -0,0 +1,91 @@
|
|
1
|
+
module Appsignal
|
2
|
+
class CLI
|
3
|
+
class Diagnose
|
4
|
+
class Paths
|
5
|
+
BYTES_TO_READ_FOR_FILES = 2 * 1024 * 1024 # 2 Mebibytes
|
6
|
+
|
7
|
+
def report
|
8
|
+
{}.tap do |hash|
|
9
|
+
paths.each do |filename, config|
|
10
|
+
hash[filename] = path_stat(config[:path])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def paths
|
16
|
+
@paths ||=
|
17
|
+
begin
|
18
|
+
config = Appsignal.config
|
19
|
+
log_file_path = config.log_file_path
|
20
|
+
install_log_path = File.join("ext", "install.log")
|
21
|
+
makefile_log_path = File.join("ext", "mkmf.log")
|
22
|
+
{
|
23
|
+
:package_install_path => {
|
24
|
+
:label => "AppSignal gem path",
|
25
|
+
:path => gem_path
|
26
|
+
},
|
27
|
+
:working_dir => {
|
28
|
+
:label => "Current working directory",
|
29
|
+
:path => Dir.pwd
|
30
|
+
},
|
31
|
+
:root_path => {
|
32
|
+
:label => "Root path",
|
33
|
+
:path => config.root_path
|
34
|
+
},
|
35
|
+
:log_dir_path => {
|
36
|
+
:label => "Log directory",
|
37
|
+
:path => log_file_path ? File.dirname(log_file_path) : ""
|
38
|
+
},
|
39
|
+
install_log_path => {
|
40
|
+
:label => "Extension install log",
|
41
|
+
:path => File.join(gem_path, install_log_path)
|
42
|
+
},
|
43
|
+
makefile_log_path => {
|
44
|
+
:label => "Makefile install log",
|
45
|
+
:path => File.join(gem_path, makefile_log_path)
|
46
|
+
},
|
47
|
+
"appsignal.log" => {
|
48
|
+
:label => "AppSignal log",
|
49
|
+
:path => log_file_path
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def path_stat(path)
|
58
|
+
{
|
59
|
+
:path => path,
|
60
|
+
:exists => File.exist?(path)
|
61
|
+
}.tap do |info|
|
62
|
+
next unless info[:exists]
|
63
|
+
stat = File.stat(path)
|
64
|
+
info[:type] = stat.directory? ? "directory" : "file"
|
65
|
+
info[:mode] = format("%o", stat.mode)
|
66
|
+
info[:writable] = stat.writable?
|
67
|
+
path_uid = stat.uid
|
68
|
+
path_gid = stat.gid
|
69
|
+
info[:ownership] = {
|
70
|
+
:uid => path_uid,
|
71
|
+
:user => Utils.username_for_uid(path_uid),
|
72
|
+
:gid => path_gid,
|
73
|
+
:group => Utils.group_for_gid(path_gid)
|
74
|
+
}
|
75
|
+
if info[:type] == "file"
|
76
|
+
info[:content] = Utils.read_file_content(
|
77
|
+
path,
|
78
|
+
BYTES_TO_READ_FOR_FILES
|
79
|
+
).split("\n")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def gem_path
|
85
|
+
@gem_path ||= \
|
86
|
+
Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Appsignal
|
2
|
+
class CLI
|
3
|
+
class Diagnose
|
4
|
+
class Utils
|
5
|
+
def self.username_for_uid(uid)
|
6
|
+
passwd_struct = Etc.getpwuid(uid)
|
7
|
+
return unless passwd_struct
|
8
|
+
passwd_struct.name
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.group_for_gid(gid)
|
12
|
+
passwd_struct = Etc.getgrgid(gid)
|
13
|
+
return unless passwd_struct
|
14
|
+
passwd_struct.name
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.read_file_content(path, bytes_to_read)
|
18
|
+
file_size = File.size(path)
|
19
|
+
if bytes_to_read > file_size
|
20
|
+
# When the file is smaller than the bytes_to_read
|
21
|
+
# Read the whole file
|
22
|
+
offset = 0
|
23
|
+
length = file_size
|
24
|
+
else
|
25
|
+
# When the file is smaller than the bytes_to_read
|
26
|
+
# Read the last X bytes_to_read
|
27
|
+
length = bytes_to_read
|
28
|
+
offset = file_size - bytes_to_read
|
29
|
+
end
|
30
|
+
|
31
|
+
IO.binread(path, length, offset)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -73,10 +73,10 @@ module Appsignal
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def install_for_rails(config)
|
76
|
-
require File.expand_path(File.join(Dir.pwd, "config/application.rb"))
|
77
|
-
|
78
76
|
puts "Installing for Ruby on Rails"
|
79
77
|
|
78
|
+
require File.expand_path(File.join(Dir.pwd, "config/application.rb"))
|
79
|
+
|
80
80
|
config[:name] = Rails.application.class.parent_name
|
81
81
|
|
82
82
|
name_overwritten = yes_or_no(" Your app's name is: '#{config[:name]}' \n Do you want to change how this is displayed in AppSignal? (y/n): ")
|
data/lib/appsignal/config.rb
CHANGED
@@ -8,6 +8,8 @@ require "tmpdir"
|
|
8
8
|
|
9
9
|
module Appsignal
|
10
10
|
class Config
|
11
|
+
include Appsignal::Utils::DeprecationMessage
|
12
|
+
|
11
13
|
DEFAULT_CONFIG = {
|
12
14
|
:debug => false,
|
13
15
|
:log => "file",
|
@@ -82,27 +84,69 @@ module Appsignal
|
|
82
84
|
:ignore_exceptions => :ignore_errors
|
83
85
|
}.freeze
|
84
86
|
|
85
|
-
|
87
|
+
# @attribute [r] system_config
|
88
|
+
# Config detected on the system level.
|
89
|
+
# Used in diagnose report.
|
90
|
+
# @api private
|
91
|
+
# @return [Hash]
|
92
|
+
# @!attribute [r] initial_config
|
93
|
+
# Config detected on the system level.
|
94
|
+
# Used in diagnose report.
|
95
|
+
# @api private
|
96
|
+
# @return [Hash]
|
97
|
+
# @!attribute [r] file_config
|
98
|
+
# Config loaded from `config/appsignal.yml` config file.
|
99
|
+
# Used in diagnose report.
|
100
|
+
# @api private
|
101
|
+
# @return [Hash]
|
102
|
+
# @!attribute [r] env_config
|
103
|
+
# Config loaded from the system environment.
|
104
|
+
# Used in diagnose report.
|
105
|
+
# @api private
|
106
|
+
# @return [Hash]
|
107
|
+
# @!attribute [r] config_hash
|
108
|
+
# Config used by the AppSignal gem.
|
109
|
+
# Combined Hash of the {system_config}, {initial_config}, {file_config},
|
110
|
+
# {env_config} attributes.
|
111
|
+
# @see #[]
|
112
|
+
# @see #[]=
|
113
|
+
# @api private
|
114
|
+
# @return [Hash]
|
115
|
+
|
116
|
+
attr_reader :root_path, :env, :config_hash, :system_config,
|
117
|
+
:initial_config, :file_config, :env_config
|
86
118
|
attr_accessor :logger
|
87
119
|
|
88
120
|
def initialize(root_path, env, initial_config = {}, logger = Appsignal.logger)
|
89
121
|
@root_path = root_path
|
90
|
-
@env = ENV.fetch("APPSIGNAL_APP_ENV".freeze, env.to_s)
|
91
|
-
@initial_config = initial_config
|
92
122
|
@logger = logger
|
93
123
|
@valid = false
|
94
124
|
@config_hash = Hash[DEFAULT_CONFIG]
|
125
|
+
env_loaded_from_initial = env.to_s
|
126
|
+
@env =
|
127
|
+
if ENV.key?("APPSIGNAL_APP_ENV".freeze)
|
128
|
+
env_loaded_from_env = ENV["APPSIGNAL_APP_ENV".freeze]
|
129
|
+
else
|
130
|
+
env_loaded_from_initial
|
131
|
+
end
|
95
132
|
|
96
133
|
# Set config based on the system
|
97
|
-
detect_from_system
|
134
|
+
@system_config = detect_from_system
|
135
|
+
merge(system_config)
|
98
136
|
# Initial config
|
99
|
-
|
137
|
+
@initial_config = initial_config
|
138
|
+
merge(initial_config)
|
100
139
|
# Load the config file if it exists
|
101
|
-
load_from_disk
|
140
|
+
@file_config = load_from_disk || {}
|
141
|
+
merge(file_config)
|
102
142
|
# Load config from environment variables
|
103
|
-
load_from_environment
|
143
|
+
@env_config = load_from_environment
|
144
|
+
merge(env_config)
|
104
145
|
# Validate that we have a correct config
|
105
146
|
validate
|
147
|
+
# Track origin of env
|
148
|
+
@initial_config[:env] = env_loaded_from_initial if env_loaded_from_initial
|
149
|
+
@env_config[:env] = env_loaded_from_env if env_loaded_from_env
|
106
150
|
end
|
107
151
|
|
108
152
|
# @api private
|
@@ -166,7 +210,6 @@ module Appsignal
|
|
166
210
|
ENV["_APPSIGNAL_IGNORE_ACTIONS"] = config_hash[:ignore_actions].join(",")
|
167
211
|
ENV["_APPSIGNAL_IGNORE_ERRORS"] = config_hash[:ignore_errors].join(",")
|
168
212
|
ENV["_APPSIGNAL_IGNORE_NAMESPACES"] = config_hash[:ignore_namespaces].join(",")
|
169
|
-
ENV["_APPSIGNAL_SEND_PARAMS"] = config_hash[:send_params].to_s
|
170
213
|
ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"] = config_hash[:running_in_container].to_s
|
171
214
|
ENV["_APPSIGNAL_WORKING_DIR_PATH"] = config_hash[:working_dir_path] if config_hash[:working_dir_path]
|
172
215
|
ENV["_APPSIGNAL_WORKING_DIRECTORY_PATH"] = config_hash[:working_directory_path] if config_hash[:working_directory_path]
|
@@ -208,10 +251,12 @@ module Appsignal
|
|
208
251
|
end
|
209
252
|
|
210
253
|
def detect_from_system
|
211
|
-
|
254
|
+
{}.tap do |hash|
|
255
|
+
hash[:log] = "stdout" if Appsignal::System.heroku?
|
212
256
|
|
213
|
-
|
214
|
-
|
257
|
+
# Make active by default if APPSIGNAL_PUSH_API_KEY is present
|
258
|
+
hash[:active] = true if ENV["APPSIGNAL_PUSH_API_KEY"]
|
259
|
+
end
|
215
260
|
end
|
216
261
|
|
217
262
|
def load_from_disk
|
@@ -225,11 +270,10 @@ module Appsignal
|
|
225
270
|
hash[key.to_sym] = value # convert keys to symbols
|
226
271
|
end
|
227
272
|
|
228
|
-
|
229
|
-
|
230
|
-
merge(@config_hash, config_for_this_env)
|
273
|
+
maintain_backwards_compatibility(config_for_this_env)
|
231
274
|
else
|
232
275
|
@logger.error "Not loading from config file: config for '#{env}' not found"
|
276
|
+
nil
|
233
277
|
end
|
234
278
|
end
|
235
279
|
|
@@ -242,17 +286,21 @@ module Appsignal
|
|
242
286
|
DEPRECATED_CONFIG_KEY_MAPPING.each do |old_key, new_key|
|
243
287
|
old_config_value = config.delete(old_key)
|
244
288
|
next unless old_config_value
|
245
|
-
|
246
|
-
"
|
289
|
+
deprecation_message \
|
290
|
+
"Old configuration key found. Please update the "\
|
291
|
+
"'#{old_key}' to '#{new_key}'.",
|
292
|
+
logger
|
247
293
|
|
248
294
|
next if config[new_key] # Skip if new key is already in use
|
249
295
|
config[new_key] = old_config_value
|
250
296
|
end
|
251
297
|
|
252
298
|
if config.include?(:working_dir_path)
|
253
|
-
|
254
|
-
|
255
|
-
|
299
|
+
deprecation_message \
|
300
|
+
"'working_dir_path' is deprecated, please use " \
|
301
|
+
"'working_directory_path' instead and specify the " \
|
302
|
+
"full path to the working directory",
|
303
|
+
logger
|
256
304
|
end
|
257
305
|
end
|
258
306
|
end
|
@@ -292,15 +340,15 @@ module Appsignal
|
|
292
340
|
config[ENV_TO_KEY_MAPPING[var]] = env_var.split(",")
|
293
341
|
end
|
294
342
|
|
295
|
-
|
343
|
+
config
|
296
344
|
end
|
297
345
|
|
298
|
-
def merge(
|
346
|
+
def merge(new_config)
|
299
347
|
new_config.each do |key, value|
|
300
|
-
unless
|
348
|
+
unless config_hash[key].nil?
|
301
349
|
@logger.debug("Config key '#{key}' is being overwritten")
|
302
350
|
end
|
303
|
-
|
351
|
+
config_hash[key] = value
|
304
352
|
end
|
305
353
|
end
|
306
354
|
end
|
@@ -14,6 +14,8 @@ module Appsignal
|
|
14
14
|
# @api private
|
15
15
|
class EventFormatter
|
16
16
|
class << self
|
17
|
+
include Appsignal::Utils::DeprecationMessage
|
18
|
+
|
17
19
|
def formatters
|
18
20
|
@@formatters ||= {}
|
19
21
|
end
|
@@ -33,7 +35,7 @@ module Appsignal
|
|
33
35
|
end
|
34
36
|
|
35
37
|
if registered?(name, formatter)
|
36
|
-
|
38
|
+
logger.warn(
|
37
39
|
"Formatter for '#{name}' already registered, not registering "\
|
38
40
|
"'#{formatter.name}'"
|
39
41
|
)
|
@@ -82,19 +84,23 @@ module Appsignal
|
|
82
84
|
rescue => ex
|
83
85
|
formatter_classes.delete(name)
|
84
86
|
formatters.delete(name)
|
85
|
-
|
87
|
+
logger.warn("'#{ex.message}' when initializing #{name} event formatter")
|
86
88
|
end
|
87
89
|
|
88
90
|
def register_deprecated_formatter(name)
|
89
|
-
|
91
|
+
deprecation_message \
|
90
92
|
"Formatter for '#{name}' is using a deprecated registration " \
|
91
93
|
"method. This event formatter will not be loaded. " \
|
92
94
|
"Please update the formatter according to the documentation at: " \
|
93
|
-
"https://docs.appsignal.com/ruby/instrumentation/event-formatters.html"
|
94
|
-
|
95
|
+
"https://docs.appsignal.com/ruby/instrumentation/event-formatters.html",
|
96
|
+
logger
|
95
97
|
|
96
98
|
deprecated_formatter_classes[name] = self
|
97
99
|
end
|
100
|
+
|
101
|
+
def logger
|
102
|
+
Appsignal.logger
|
103
|
+
end
|
98
104
|
end
|
99
105
|
|
100
106
|
# @api public
|
@@ -47,4 +47,15 @@ module Appsignal
|
|
47
47
|
self.class.lock
|
48
48
|
end
|
49
49
|
end
|
50
|
+
|
51
|
+
# {Appsignal::NilGarbageCollectionProfiler} is a dummy profiler
|
52
|
+
# that always returns 0 as the total time.
|
53
|
+
# Used when we don't want any profile information
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
class NilGarbageCollectionProfiler
|
57
|
+
def total_time
|
58
|
+
0
|
59
|
+
end
|
60
|
+
end
|
50
61
|
end
|
data/lib/appsignal/system.rb
CHANGED
@@ -7,7 +7,9 @@ module Appsignal
|
|
7
7
|
#
|
8
8
|
# @api private
|
9
9
|
module System
|
10
|
+
LINUX_TARGET = "linux".freeze
|
10
11
|
MUSL_TARGET = "linux-musl".freeze
|
12
|
+
FREEBSD_TARGET = "freebsd".freeze
|
11
13
|
GEM_EXT_PATH = File.expand_path("../../../ext", __FILE__).freeze
|
12
14
|
|
13
15
|
def self.heroku?
|
@@ -45,12 +47,12 @@ module Appsignal
|
|
45
47
|
host_os = RbConfig::CONFIG["host_os"].downcase
|
46
48
|
local_os =
|
47
49
|
case host_os
|
48
|
-
when /
|
49
|
-
|
50
|
+
when /#{LINUX_TARGET}/
|
51
|
+
LINUX_TARGET
|
50
52
|
when /darwin/
|
51
53
|
"darwin"
|
52
|
-
when /
|
53
|
-
|
54
|
+
when /#{FREEBSD_TARGET}/
|
55
|
+
FREEBSD_TARGET
|
54
56
|
else
|
55
57
|
host_os
|
56
58
|
end
|
@@ -53,7 +53,8 @@ module Appsignal
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def garbage_collection_profiler
|
56
|
-
@garbage_collection_profiler ||=
|
56
|
+
@garbage_collection_profiler ||=
|
57
|
+
Appsignal.config[:enable_gc_instrumentation] ? Appsignal::GarbageCollectionProfiler.new : NilGarbageCollectionProfiler.new
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
data/lib/appsignal/utils.rb
CHANGED
data/lib/appsignal/version.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "appsignal/cli/diagnose/utils"
|
2
|
+
|
3
|
+
describe Appsignal::CLI::Diagnose::Utils do
|
4
|
+
describe ".read_file_content" do
|
5
|
+
let(:path) { File.join(spec_system_tmp_dir, "test_file.txt") }
|
6
|
+
let(:bytes_to_read) { 100 }
|
7
|
+
subject { described_class.read_file_content(path, bytes_to_read) }
|
8
|
+
before do
|
9
|
+
File.open path, "w+" do |f|
|
10
|
+
f.write file_contents
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when file is bigger than read size" do
|
15
|
+
let(:file_contents) do
|
16
|
+
"".tap do |s|
|
17
|
+
100.times do |i|
|
18
|
+
s << "line #{i}\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the last X bytes" do
|
24
|
+
is_expected
|
25
|
+
.to eq(file_contents[(file_contents.length - bytes_to_read)..file_contents.length])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when file is smaller than read size" do
|
30
|
+
let(:file_contents) { "line 1\n" }
|
31
|
+
|
32
|
+
it "returns the whole file content" do
|
33
|
+
is_expected.to eq(file_contents)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|