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
@@ -82,6 +82,8 @@ module Appsignal
|
|
82
82
|
print_empty_line
|
83
83
|
|
84
84
|
library_information
|
85
|
+
data[:installation] = fetch_installation_report
|
86
|
+
print_installation_report
|
85
87
|
print_empty_line
|
86
88
|
|
87
89
|
host_information
|
@@ -180,7 +182,8 @@ module Appsignal
|
|
180
182
|
if rails_app?
|
181
183
|
data[:app][:rails] = true
|
182
184
|
current_path = Rails.root
|
183
|
-
initial_config[:name] =
|
185
|
+
initial_config[:name] =
|
186
|
+
Appsignal::Utils::RailsHelper.detected_rails_app_name
|
184
187
|
initial_config[:log_path] = current_path.join("log")
|
185
188
|
end
|
186
189
|
|
@@ -325,12 +328,89 @@ module Appsignal
|
|
325
328
|
save :language, "ruby"
|
326
329
|
puts_and_save :package_version, "Gem version", Appsignal::VERSION
|
327
330
|
puts_and_save :agent_version, "Agent version", Appsignal::Extension.agent_version
|
328
|
-
puts_and_save :agent_architecture, "Agent architecture",
|
329
|
-
Appsignal::System.installed_agent_architecture
|
330
331
|
puts_and_save :extension_loaded, "Extension loaded", Appsignal.extension_loaded
|
331
332
|
end
|
332
333
|
end
|
333
334
|
|
335
|
+
def fetch_installation_report
|
336
|
+
path = File.expand_path("../../../../ext/install.report", __FILE__)
|
337
|
+
raw_report = File.read(path)
|
338
|
+
Utils.parse_yaml(raw_report)
|
339
|
+
rescue => e
|
340
|
+
{
|
341
|
+
"parsing_error" => {
|
342
|
+
"error" => "#{e.class}: #{e}",
|
343
|
+
"backtrace" => e.backtrace
|
344
|
+
}.tap do |r|
|
345
|
+
r["raw"] = raw_report if raw_report
|
346
|
+
end
|
347
|
+
}
|
348
|
+
end
|
349
|
+
|
350
|
+
def print_installation_report
|
351
|
+
puts "\nExtension installation report"
|
352
|
+
install_report = data[:installation]
|
353
|
+
if install_report.key? "parsing_error"
|
354
|
+
print_installation_report_parsing_error(install_report)
|
355
|
+
return
|
356
|
+
end
|
357
|
+
|
358
|
+
print_installation_result_report(install_report)
|
359
|
+
print_installation_language_report(install_report)
|
360
|
+
print_installation_download_report(install_report)
|
361
|
+
print_installation_build_report(install_report)
|
362
|
+
print_installation_host_report(install_report)
|
363
|
+
end
|
364
|
+
|
365
|
+
def print_installation_report_parsing_error(report)
|
366
|
+
report = report["parsing_error"]
|
367
|
+
puts " Error found while parsing the report."
|
368
|
+
puts " Error: #{report["error"]}"
|
369
|
+
puts " Raw report:\n#{report["raw"]}" if report["raw"]
|
370
|
+
end
|
371
|
+
|
372
|
+
def print_installation_result_report(report)
|
373
|
+
report = report.fetch("download", {})
|
374
|
+
puts " Installation result"
|
375
|
+
puts " Status: #{report["status"]}"
|
376
|
+
puts " Message: #{report["message"]}" if report["message"]
|
377
|
+
puts " Error: #{report["error"]}" if report["error"]
|
378
|
+
end
|
379
|
+
|
380
|
+
def print_installation_language_report(report)
|
381
|
+
report = report.fetch("language", {})
|
382
|
+
puts " Language details"
|
383
|
+
puts " Implementation: #{report["implementation"]}"
|
384
|
+
puts " Ruby version: #{report["version"]}"
|
385
|
+
end
|
386
|
+
|
387
|
+
def print_installation_download_report(report)
|
388
|
+
report = report.fetch("download", {})
|
389
|
+
puts " Download details"
|
390
|
+
puts " Download URL: #{report["download_url"]}"
|
391
|
+
puts " Checksum: #{report["checksum"]}"
|
392
|
+
end
|
393
|
+
|
394
|
+
def print_installation_build_report(report)
|
395
|
+
report = report.fetch("build", {})
|
396
|
+
puts " Build details"
|
397
|
+
puts " Install time: #{report["time"]}"
|
398
|
+
puts " Architecture: #{report["architecture"]}"
|
399
|
+
puts " Target: #{report["target"]}"
|
400
|
+
puts " Musl override: #{report["musl_override"]}"
|
401
|
+
puts " Library type: #{report["library_type"]}"
|
402
|
+
puts " Source: #{report["source"]}" if report["source"] != "remote"
|
403
|
+
puts " Dependencies: #{report["dependencies"]}"
|
404
|
+
puts " Flags: #{report["flags"]}"
|
405
|
+
end
|
406
|
+
|
407
|
+
def print_installation_host_report(report)
|
408
|
+
report = report.fetch("host", {})
|
409
|
+
puts " Host details"
|
410
|
+
puts " Root user: #{report["root_user"]}"
|
411
|
+
puts " Dependencies: #{report["dependencies"]}"
|
412
|
+
end
|
413
|
+
|
334
414
|
def host_information
|
335
415
|
rbconfig = RbConfig::CONFIG
|
336
416
|
puts "Host information"
|
@@ -349,7 +429,7 @@ module Appsignal
|
|
349
429
|
save :heroku, Appsignal::System.heroku?
|
350
430
|
|
351
431
|
save :root, Process.uid.zero?
|
352
|
-
puts_value "
|
432
|
+
puts_value "Root user",
|
353
433
|
Process.uid.zero? ? "true (not recommended)" : "false"
|
354
434
|
puts_and_save :running_in_container, "Running in container",
|
355
435
|
Appsignal::Extension.running_in_container?
|
@@ -17,7 +17,6 @@ module Appsignal
|
|
17
17
|
begin
|
18
18
|
config = Appsignal.config
|
19
19
|
log_file_path = config.log_file_path
|
20
|
-
install_log_path = File.join("ext", "install.log")
|
21
20
|
makefile_log_path = File.join("ext", "mkmf.log")
|
22
21
|
{
|
23
22
|
:package_install_path => {
|
@@ -36,10 +35,6 @@ module Appsignal
|
|
36
35
|
:label => "Log directory",
|
37
36
|
:path => log_file_path ? File.dirname(log_file_path) : ""
|
38
37
|
},
|
39
|
-
install_log_path => {
|
40
|
-
:label => "Extension install log",
|
41
|
-
:path => File.join(gem_path, install_log_path)
|
42
|
-
},
|
43
38
|
makefile_log_path => {
|
44
39
|
:label => "Makefile install log",
|
45
40
|
:path => File.join(gem_path, makefile_log_path)
|
@@ -30,6 +30,23 @@ module Appsignal
|
|
30
30
|
|
31
31
|
IO.binread(path, length, offset)
|
32
32
|
end
|
33
|
+
|
34
|
+
def self.parse_yaml(contents)
|
35
|
+
arguments = [contents]
|
36
|
+
if YAML.respond_to? :safe_load
|
37
|
+
method = :safe_load
|
38
|
+
arguments << \
|
39
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
|
40
|
+
# Use keyword params for Ruby 2.6 and up
|
41
|
+
{ :permitted_classes => [Time] }
|
42
|
+
else
|
43
|
+
[Time]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
method = :load
|
47
|
+
end
|
48
|
+
YAML.send(method, *arguments)
|
49
|
+
end
|
33
50
|
end
|
34
51
|
end
|
35
52
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "appsignal/utils/rails_helper"
|
4
|
+
|
3
5
|
module Appsignal
|
4
6
|
class CLI
|
5
7
|
module Helpers
|
6
8
|
private
|
7
9
|
|
10
|
+
def ruby_2_6_or_up?
|
11
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
|
12
|
+
end
|
13
|
+
|
8
14
|
def colorize(text, color)
|
9
15
|
return text if Gem.win_platform?
|
10
16
|
|
@@ -77,8 +77,7 @@ module Appsignal
|
|
77
77
|
|
78
78
|
require File.expand_path(File.join(Dir.pwd, "config/application.rb"))
|
79
79
|
|
80
|
-
config[:name] =
|
81
|
-
|
80
|
+
config[:name] = Appsignal::Utils::RailsHelper.detected_rails_app_name
|
82
81
|
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): ")
|
83
82
|
puts
|
84
83
|
if name_overwritten
|
@@ -249,12 +248,19 @@ module Appsignal
|
|
249
248
|
end
|
250
249
|
|
251
250
|
def write_config_file(data)
|
252
|
-
|
253
|
-
File.
|
254
|
-
|
255
|
-
"-"
|
251
|
+
filename = File.join(
|
252
|
+
File.dirname(__FILE__),
|
253
|
+
"../../../resources/appsignal.yml.erb"
|
256
254
|
)
|
257
|
-
|
255
|
+
file_contents = File.read(filename)
|
256
|
+
arguments = [file_contents]
|
257
|
+
if ruby_2_6_or_up?
|
258
|
+
arguments << { :trim_mode => "-" }
|
259
|
+
else
|
260
|
+
arguments << nil
|
261
|
+
arguments << "-"
|
262
|
+
end
|
263
|
+
template = ERB.new(*arguments)
|
258
264
|
config = template.result(OpenStruct.new(data).instance_eval { binding })
|
259
265
|
|
260
266
|
FileUtils.mkdir_p(File.join(Dir.pwd, "config"))
|
data/lib/appsignal/config.rb
CHANGED
@@ -36,7 +36,7 @@ module Appsignal
|
|
36
36
|
:enable_allocation_tracking => true,
|
37
37
|
:enable_gc_instrumentation => false,
|
38
38
|
:enable_host_metrics => true,
|
39
|
-
:enable_minutely_probes =>
|
39
|
+
:enable_minutely_probes => true,
|
40
40
|
:ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
|
41
41
|
:dns_servers => [],
|
42
42
|
:files_world_accessible => true
|
@@ -214,7 +214,6 @@ module Appsignal
|
|
214
214
|
ENV["_APPSIGNAL_WORKING_DIR_PATH"] = config_hash[:working_dir_path] if config_hash[:working_dir_path]
|
215
215
|
ENV["_APPSIGNAL_WORKING_DIRECTORY_PATH"] = config_hash[:working_directory_path] if config_hash[:working_directory_path]
|
216
216
|
ENV["_APPSIGNAL_ENABLE_HOST_METRICS"] = config_hash[:enable_host_metrics].to_s
|
217
|
-
ENV["_APPSIGNAL_ENABLE_MINUTELY_PROBES"] = config_hash[:enable_minutely_probes].to_s
|
218
217
|
ENV["_APPSIGNAL_HOSTNAME"] = config_hash[:hostname].to_s
|
219
218
|
ENV["_APPSIGNAL_PROCESS_NAME"] = $PROGRAM_NAME
|
220
219
|
ENV["_APPSIGNAL_CA_FILE_PATH"] = config_hash[:ca_file_path].to_s
|
@@ -75,16 +75,15 @@ module Appsignal
|
|
75
75
|
|
76
76
|
def initialize_formatter(name, formatter)
|
77
77
|
format_method = formatter.instance_method(:format)
|
78
|
-
if format_method
|
79
|
-
formatter_classes[name] = formatter
|
80
|
-
formatters[name] = formatter.new
|
81
|
-
else
|
78
|
+
if !format_method || format_method.arity != 1
|
82
79
|
raise "#{formatter} does not have a format(payload) method"
|
83
80
|
end
|
81
|
+
formatter_classes[name] = formatter
|
82
|
+
formatters[name] = formatter.new
|
84
83
|
rescue => ex
|
85
84
|
formatter_classes.delete(name)
|
86
85
|
formatters.delete(name)
|
87
|
-
logger.
|
86
|
+
logger.error("'#{ex.message}' when initializing #{name} event formatter")
|
88
87
|
end
|
89
88
|
|
90
89
|
def register_deprecated_formatter(name)
|
@@ -6,65 +6,66 @@ module Appsignal
|
|
6
6
|
module Moped
|
7
7
|
class QueryFormatter
|
8
8
|
def format(payload)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
9
|
+
return unless payload[:ops]
|
10
|
+
return if payload[:ops].empty?
|
11
|
+
|
12
|
+
op = payload[:ops].first
|
13
|
+
case op.class.to_s
|
14
|
+
when "Moped::Protocol::Command"
|
15
|
+
[
|
16
|
+
"Command", {
|
17
|
+
:database => op.full_collection_name,
|
18
|
+
:selector => sanitize(op.selector, true, :mongodb)
|
19
|
+
}.inspect
|
20
|
+
]
|
21
|
+
when "Moped::Protocol::Query"
|
22
|
+
[
|
23
|
+
"Query", {
|
24
|
+
:database => op.full_collection_name,
|
25
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
26
|
+
:flags => op.flags,
|
27
|
+
:limit => op.limit,
|
28
|
+
:skip => op.skip,
|
29
|
+
:fields => op.fields
|
30
|
+
}.inspect
|
31
|
+
]
|
32
|
+
when "Moped::Protocol::Delete"
|
33
|
+
[
|
34
|
+
"Delete", {
|
35
|
+
:database => op.full_collection_name,
|
36
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
37
|
+
:flags => op.flags
|
38
|
+
}.inspect
|
39
|
+
]
|
40
|
+
when "Moped::Protocol::Insert"
|
41
|
+
[
|
42
|
+
"Insert", {
|
43
|
+
:database => op.full_collection_name,
|
44
|
+
:documents => sanitize(op.documents, true, :mongodb),
|
45
|
+
:count => op.documents.count,
|
46
|
+
:flags => op.flags
|
47
|
+
}.inspect
|
48
|
+
]
|
49
|
+
when "Moped::Protocol::Update"
|
50
|
+
[
|
51
|
+
"Update",
|
52
|
+
{
|
53
|
+
:database => op.full_collection_name,
|
54
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
55
|
+
:update => sanitize(op.update, true, :mongodb),
|
56
|
+
:flags => op.flags
|
57
|
+
}.inspect
|
58
|
+
]
|
59
|
+
when "Moped::Protocol::KillCursors"
|
60
|
+
[
|
61
|
+
"KillCursors",
|
62
|
+
{ :number_of_cursor_ids => op.number_of_cursor_ids }.inspect
|
63
|
+
]
|
64
|
+
else
|
65
|
+
[
|
66
|
+
op.class.to_s.sub("Moped::Protocol::", ""),
|
67
|
+
{ :database => op.full_collection_name }.inspect
|
68
|
+
]
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
data/lib/appsignal/extension.rb
CHANGED
@@ -12,8 +12,8 @@ begin
|
|
12
12
|
end
|
13
13
|
rescue LoadError => err
|
14
14
|
Appsignal.logger.error(
|
15
|
-
"Failed to load extension (#{err}), please
|
16
|
-
|
15
|
+
"Failed to load extension (#{err}), please run `appsignal diagnose` " \
|
16
|
+
"and email us at support@appsignal.com"
|
17
17
|
)
|
18
18
|
Appsignal.extension_loaded = false
|
19
19
|
end
|
@@ -0,0 +1,485 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Helpers
|
5
|
+
# @api private
|
6
|
+
module Instrumentation # rubocop:disable Metrics/ModuleLength
|
7
|
+
# Creates an AppSignal transaction for the given block.
|
8
|
+
#
|
9
|
+
# If AppSignal is not {.active?} it will still execute the block, but not
|
10
|
+
# create a transaction for it.
|
11
|
+
#
|
12
|
+
# A event is created for this transaction with the name given in the
|
13
|
+
# `name` argument. The event name must start with either `perform_job` or
|
14
|
+
# `process_action` to differentiate between the "web" and "background"
|
15
|
+
# namespace. Custom namespaces are not supported by this helper method.
|
16
|
+
#
|
17
|
+
# This helper method also captures any exception that occurs in the given
|
18
|
+
# block.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# Appsignal.monitor_transaction("perform_job.nightly_update") do
|
22
|
+
# # your code
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @example with an environment
|
26
|
+
# Appsignal.monitor_transaction(
|
27
|
+
# "perform_job.nightly_update",
|
28
|
+
# :metadata => { "user_id" => 1 }
|
29
|
+
# ) do
|
30
|
+
# # your code
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @param name [String] main event name.
|
34
|
+
# @param env [Hash<Symbol, Object>]
|
35
|
+
# @option env [Hash<Symbol/String, Object>] :params Params for the
|
36
|
+
# monitored request/job, see {Appsignal::Transaction#params=} for more
|
37
|
+
# information.
|
38
|
+
# @option env [String] :controller name of the controller in which the
|
39
|
+
# transaction was recorded.
|
40
|
+
# @option env [String] :class name of the Ruby class in which the
|
41
|
+
# transaction was recorded. If `:controller` is also given,
|
42
|
+
# `:controller` is used instead.
|
43
|
+
# @option env [String] :action name of the controller action in which the
|
44
|
+
# transaction was recorded.
|
45
|
+
# @option env [String] :method name of the Ruby method in which the
|
46
|
+
# transaction was recorded. If `:action` is also given, `:action`
|
47
|
+
# is used instead.
|
48
|
+
# @option env [Integer] :queue_start the moment the request/job was
|
49
|
+
# queued. Used to track how long requests/jobs were queued before being
|
50
|
+
# executed.
|
51
|
+
# @option env [Hash<Symbol/String, String/Fixnum>] :metadata Additional
|
52
|
+
# metadata for the transaction, see
|
53
|
+
# {Appsignal::Transaction#set_metadata} for more information.
|
54
|
+
# @yield the block to monitor.
|
55
|
+
# @raise [Exception] any exception that occurs within the given block is
|
56
|
+
# re-raised by this method.
|
57
|
+
# @return [Object] the value of the given block is returned.
|
58
|
+
# @since 0.10.0
|
59
|
+
def monitor_transaction(name, env = {})
|
60
|
+
return yield unless active?
|
61
|
+
|
62
|
+
if name.start_with?("perform_job".freeze)
|
63
|
+
namespace = Appsignal::Transaction::BACKGROUND_JOB
|
64
|
+
request = Appsignal::Transaction::GenericRequest.new(env)
|
65
|
+
elsif name.start_with?("process_action".freeze)
|
66
|
+
namespace = Appsignal::Transaction::HTTP_REQUEST
|
67
|
+
request = ::Rack::Request.new(env)
|
68
|
+
else
|
69
|
+
logger.error("Unrecognized name '#{name}'")
|
70
|
+
return
|
71
|
+
end
|
72
|
+
transaction = Appsignal::Transaction.create(
|
73
|
+
SecureRandom.uuid,
|
74
|
+
namespace,
|
75
|
+
request
|
76
|
+
)
|
77
|
+
begin
|
78
|
+
Appsignal.instrument(name) do
|
79
|
+
yield
|
80
|
+
end
|
81
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
82
|
+
transaction.set_error(error)
|
83
|
+
raise error
|
84
|
+
ensure
|
85
|
+
transaction.set_http_or_background_action(request.env)
|
86
|
+
transaction.set_http_or_background_queue_start
|
87
|
+
Appsignal::Transaction.complete_current!
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Monitor a transaction, stop AppSignal and wait for this single
|
92
|
+
# transaction to be flushed.
|
93
|
+
#
|
94
|
+
# Useful for cases such as Rake tasks and Resque-like systems where a
|
95
|
+
# process is forked and immediately exits after the transaction finishes.
|
96
|
+
#
|
97
|
+
# @see monitor_transaction
|
98
|
+
def monitor_single_transaction(name, env = {}, &block)
|
99
|
+
monitor_transaction(name, env, &block)
|
100
|
+
ensure
|
101
|
+
stop("monitor_single_transaction")
|
102
|
+
end
|
103
|
+
|
104
|
+
# Listen for an error to occur and send it to AppSignal.
|
105
|
+
#
|
106
|
+
# Uses {.send_error} to directly send the error in a separate
|
107
|
+
# transaction. Does not add the error to the current transaction.
|
108
|
+
#
|
109
|
+
# Make sure that AppSignal is integrated in your application beforehand.
|
110
|
+
# AppSignal won't record errors unless {Config#active?} is `true`.
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
# # my_app.rb
|
114
|
+
# # setup AppSignal beforehand
|
115
|
+
#
|
116
|
+
# Appsignal.listen_for_error do
|
117
|
+
# # my code
|
118
|
+
# raise "foo"
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# @see Transaction.set_tags
|
122
|
+
# @see Transaction.set_namespace
|
123
|
+
# @see .send_error
|
124
|
+
# @see https://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html
|
125
|
+
# AppSignal integration guide
|
126
|
+
#
|
127
|
+
# @param tags [Hash, nil]
|
128
|
+
# @param namespace [String] the namespace for this error.
|
129
|
+
# @yield yields the given block.
|
130
|
+
# @return [Object] returns the return value of the given block.
|
131
|
+
def listen_for_error(
|
132
|
+
tags = nil,
|
133
|
+
namespace = Appsignal::Transaction::HTTP_REQUEST
|
134
|
+
)
|
135
|
+
yield
|
136
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
137
|
+
send_error(error, tags, namespace)
|
138
|
+
raise error
|
139
|
+
end
|
140
|
+
alias :listen_for_exception :listen_for_error
|
141
|
+
|
142
|
+
# Send an error to AppSignal regardless of the context.
|
143
|
+
#
|
144
|
+
# Records and send the exception to AppSignal.
|
145
|
+
#
|
146
|
+
# This instrumentation helper does not require a transaction to be
|
147
|
+
# active, it starts a new transaction by itself.
|
148
|
+
#
|
149
|
+
# Use {.set_error} if your want to add an exception to the current
|
150
|
+
# transaction.
|
151
|
+
#
|
152
|
+
# **Note**: Does not do anything if AppSignal is not active or when the
|
153
|
+
# "error" is not a class extended from Ruby's Exception class.
|
154
|
+
#
|
155
|
+
# @example Send an exception
|
156
|
+
# begin
|
157
|
+
# raise "oh no!"
|
158
|
+
# rescue => e
|
159
|
+
# Appsignal.send_error(e)
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# @example Send an exception with tags
|
163
|
+
# begin
|
164
|
+
# raise "oh no!"
|
165
|
+
# rescue => e
|
166
|
+
# Appsignal.send_error(e, :key => "value")
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# @example Add more metadata to transaction
|
170
|
+
# Appsignal.send_error(e, :key => "value") do |transaction|
|
171
|
+
# transaction.params(:search_query => params[:search_query])
|
172
|
+
# transaction.set_action("my_action_name")
|
173
|
+
# transaction.set_namespace("my_namespace")
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# @param error [Exception] The error to send to AppSignal.
|
177
|
+
# @param tags [Hash{String, Symbol => String, Symbol, Integer}]
|
178
|
+
# Additional tags to add to the error. See also {.tag_request}.
|
179
|
+
# @param namespace [String] The namespace in which the error occurred.
|
180
|
+
# See also {.set_namespace}.
|
181
|
+
# @yield [transaction] yields block to allow modification of the
|
182
|
+
# transaction before it's send.
|
183
|
+
# @yieldparam transaction [Transaction] yields the AppSignal transaction
|
184
|
+
# used to send the error.
|
185
|
+
# @return [void]
|
186
|
+
#
|
187
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
|
188
|
+
# Exception handling guide
|
189
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
|
190
|
+
# Tagging guide
|
191
|
+
# @since 0.6.0
|
192
|
+
def send_error(
|
193
|
+
error,
|
194
|
+
tags = nil,
|
195
|
+
namespace = Appsignal::Transaction::HTTP_REQUEST
|
196
|
+
)
|
197
|
+
return unless active?
|
198
|
+
unless error.is_a?(Exception)
|
199
|
+
logger.error("Can't send error, given value is not an exception")
|
200
|
+
return
|
201
|
+
end
|
202
|
+
transaction = Appsignal::Transaction.new(
|
203
|
+
SecureRandom.uuid,
|
204
|
+
namespace,
|
205
|
+
Appsignal::Transaction::GenericRequest.new({})
|
206
|
+
)
|
207
|
+
transaction.set_tags(tags) if tags
|
208
|
+
transaction.set_error(error)
|
209
|
+
yield transaction if block_given?
|
210
|
+
transaction.complete
|
211
|
+
end
|
212
|
+
alias :send_exception :send_error
|
213
|
+
|
214
|
+
# Set an error on the current transaction.
|
215
|
+
#
|
216
|
+
# **Note**: Does not do anything if AppSignal is not active, no
|
217
|
+
# transaction is currently active or when the "error" is not a class
|
218
|
+
# extended from Ruby's Exception class.
|
219
|
+
#
|
220
|
+
# @example Manual instrumentation of set_error.
|
221
|
+
# # Manually starting AppSignal here
|
222
|
+
# # Manually starting a transaction here.
|
223
|
+
# begin
|
224
|
+
# raise "oh no!"
|
225
|
+
# rescue => e
|
226
|
+
# Appsignal.set_error(error)
|
227
|
+
# end
|
228
|
+
# # Manually completing the transaction here.
|
229
|
+
# # Manually stopping AppSignal here
|
230
|
+
#
|
231
|
+
# @example In a Rails application
|
232
|
+
# class SomeController < ApplicationController
|
233
|
+
# # The AppSignal transaction is created by our integration for you.
|
234
|
+
# def create
|
235
|
+
# # Do something that breaks
|
236
|
+
# rescue => e
|
237
|
+
# Appsignal.set_error(e)
|
238
|
+
# end
|
239
|
+
# end
|
240
|
+
#
|
241
|
+
# @param exception [Exception] The error to add to the current
|
242
|
+
# transaction.
|
243
|
+
# @param tags [Hash{String, Symbol => String, Symbol, Integer}]
|
244
|
+
# Additional tags to add to the error. See also {.tag_request}.
|
245
|
+
# @param namespace [String] The namespace in which the error occurred.
|
246
|
+
# See also {.set_namespace}.
|
247
|
+
# @return [void]
|
248
|
+
#
|
249
|
+
# @see Transaction#set_error
|
250
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
|
251
|
+
# Exception handling guide
|
252
|
+
# @since 0.6.6
|
253
|
+
def set_error(exception, tags = nil, namespace = nil)
|
254
|
+
return if !active? ||
|
255
|
+
Appsignal::Transaction.current.nil? ||
|
256
|
+
exception.nil?
|
257
|
+
transaction = Appsignal::Transaction.current
|
258
|
+
transaction.set_error(exception)
|
259
|
+
transaction.set_tags(tags) if tags
|
260
|
+
transaction.set_namespace(namespace) if namespace
|
261
|
+
end
|
262
|
+
alias :set_exception :set_error
|
263
|
+
alias :add_exception :set_error
|
264
|
+
|
265
|
+
# Set a custom action name for the current transaction.
|
266
|
+
#
|
267
|
+
# When using an integration such as the Rails or Sinatra AppSignal will
|
268
|
+
# try to find the action name from the controller or endpoint for you.
|
269
|
+
#
|
270
|
+
# If you want to customize the action name as it appears on AppSignal.com
|
271
|
+
# you can use this method. This overrides the action name AppSignal
|
272
|
+
# generates in an integration.
|
273
|
+
#
|
274
|
+
# @example in a Rails controller
|
275
|
+
# class SomeController < ApplicationController
|
276
|
+
# before_action :set_appsignal_action
|
277
|
+
#
|
278
|
+
# def set_appsignal_action
|
279
|
+
# Appsignal.set_action("DynamicController#dynamic_method")
|
280
|
+
# end
|
281
|
+
# end
|
282
|
+
#
|
283
|
+
# @param action [String]
|
284
|
+
# @return [void]
|
285
|
+
# @see Transaction#set_action
|
286
|
+
# @since 2.2.0
|
287
|
+
def set_action(action)
|
288
|
+
return if !active? ||
|
289
|
+
Appsignal::Transaction.current.nil? ||
|
290
|
+
action.nil?
|
291
|
+
Appsignal::Transaction.current.set_action(action)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Set a custom namespace for the current transaction.
|
295
|
+
#
|
296
|
+
# When using an integration such as Rails or Sidekiq AppSignal will try
|
297
|
+
# to find a appropriate namespace for the transaction.
|
298
|
+
#
|
299
|
+
# A Rails controller will be automatically put in the "http_request"
|
300
|
+
# namespace, while a Sidekiq background job is put in the
|
301
|
+
# "background_job" namespace.
|
302
|
+
#
|
303
|
+
# Note: The "http_request" namespace gets transformed on AppSignal.com to
|
304
|
+
# "Web" and "background_job" gets transformed to "Background".
|
305
|
+
#
|
306
|
+
# If you want to customize the namespace in which transactions appear you
|
307
|
+
# can use this method. This overrides the namespace AppSignal uses by
|
308
|
+
# default.
|
309
|
+
#
|
310
|
+
# A common request we've seen is to split the administration panel from
|
311
|
+
# the main application.
|
312
|
+
#
|
313
|
+
# @example create a custom admin namespace
|
314
|
+
# class AdminController < ApplicationController
|
315
|
+
# before_action :set_appsignal_namespace
|
316
|
+
#
|
317
|
+
# def set_appsignal_namespace
|
318
|
+
# Appsignal.set_namespace("admin")
|
319
|
+
# end
|
320
|
+
# end
|
321
|
+
#
|
322
|
+
# @param namespace [String]
|
323
|
+
# @return [void]
|
324
|
+
# @see Transaction#set_namespace
|
325
|
+
# @since 2.2.0
|
326
|
+
def set_namespace(namespace)
|
327
|
+
return if !active? ||
|
328
|
+
Appsignal::Transaction.current.nil? ||
|
329
|
+
namespace.nil?
|
330
|
+
Appsignal::Transaction.current.set_namespace(namespace)
|
331
|
+
end
|
332
|
+
|
333
|
+
# Set tags on the current transaction.
|
334
|
+
#
|
335
|
+
# Tags are extra bits of information that are added to transaction and
|
336
|
+
# appear on sample details pages on AppSignal.com.
|
337
|
+
#
|
338
|
+
# @example
|
339
|
+
# Appsignal.tag_request(:locale => "en")
|
340
|
+
# Appsignal.tag_request("locale" => "en")
|
341
|
+
# Appsignal.tag_request("user_id" => 1)
|
342
|
+
#
|
343
|
+
# @example Nested hashes are not supported
|
344
|
+
# # Bad
|
345
|
+
# Appsignal.tag_request(:user => { :locale => "en" })
|
346
|
+
#
|
347
|
+
# @example in a Rails controller
|
348
|
+
# class SomeController < ApplicationController
|
349
|
+
# before_action :set_appsignal_tags
|
350
|
+
#
|
351
|
+
# def set_appsignal_tags
|
352
|
+
# Appsignal.tag_request(:locale => I18n.locale)
|
353
|
+
# end
|
354
|
+
# end
|
355
|
+
#
|
356
|
+
# @param tags [Hash] Collection of tags.
|
357
|
+
# @option tags [String, Symbol, Integer] :any
|
358
|
+
# The name of the tag as a Symbol.
|
359
|
+
# @option tags [String, Symbol, Integer] "any"
|
360
|
+
# The name of the tag as a String.
|
361
|
+
# @return [void]
|
362
|
+
#
|
363
|
+
# @see Transaction.set_tags
|
364
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
|
365
|
+
# Tagging guide
|
366
|
+
def tag_request(tags = {})
|
367
|
+
return unless active?
|
368
|
+
transaction = Appsignal::Transaction.current
|
369
|
+
return false unless transaction
|
370
|
+
transaction.set_tags(tags)
|
371
|
+
end
|
372
|
+
alias :tag_job :tag_request
|
373
|
+
|
374
|
+
# Instrument helper for AppSignal.
|
375
|
+
#
|
376
|
+
# For more help, read our custom instrumentation guide, listed under "See
|
377
|
+
# also".
|
378
|
+
#
|
379
|
+
# @example Simple instrumentation
|
380
|
+
# Appsignal.instrument("fetch.issue_fetcher") do
|
381
|
+
# # To be instrumented code
|
382
|
+
# end
|
383
|
+
#
|
384
|
+
# @example Instrumentation with title and body
|
385
|
+
# Appsignal.instrument(
|
386
|
+
# "fetch.issue_fetcher",
|
387
|
+
# "Fetching issue",
|
388
|
+
# "GitHub API"
|
389
|
+
# ) do
|
390
|
+
# # To be instrumented code
|
391
|
+
# end
|
392
|
+
#
|
393
|
+
# @param name [String] Name of the instrumented event. Read our event
|
394
|
+
# naming guide listed under "See also".
|
395
|
+
# @param title [String, nil] Human readable name of the event.
|
396
|
+
# @param body [String, nil] Value of importance for the event, such as
|
397
|
+
# the server against an API call is made.
|
398
|
+
# @param body_format [Integer] Enum for the type of event that is
|
399
|
+
# instrumented. Accepted values are {EventFormatter::DEFAULT} and
|
400
|
+
# {EventFormatter::SQL_BODY_FORMAT}, but we recommend you use
|
401
|
+
# {.instrument_sql} instead of {EventFormatter::SQL_BODY_FORMAT}.
|
402
|
+
# @yield yields the given block of code instrumented in an AppSignal
|
403
|
+
# event.
|
404
|
+
# @return [Object] Returns the block's return value.
|
405
|
+
#
|
406
|
+
# @see Appsignal::Transaction#instrument
|
407
|
+
# @see .instrument_sql
|
408
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
|
409
|
+
# AppSignal custom instrumentation guide
|
410
|
+
# @see http://docs.appsignal.com/api/event-names.html
|
411
|
+
# AppSignal event naming guide
|
412
|
+
# @since 1.3.0
|
413
|
+
def instrument(
|
414
|
+
name,
|
415
|
+
title = nil,
|
416
|
+
body = nil,
|
417
|
+
body_format = Appsignal::EventFormatter::DEFAULT
|
418
|
+
)
|
419
|
+
Appsignal::Transaction.current.start_event
|
420
|
+
yield if block_given?
|
421
|
+
ensure
|
422
|
+
Appsignal::Transaction
|
423
|
+
.current
|
424
|
+
.finish_event(name, title, body, body_format)
|
425
|
+
end
|
426
|
+
|
427
|
+
# Instrumentation helper for SQL queries.
|
428
|
+
#
|
429
|
+
# This helper filters out values from SQL queries so you don't have to.
|
430
|
+
#
|
431
|
+
# @example SQL query instrumentation
|
432
|
+
# body = "SELECT * FROM ..."
|
433
|
+
# Appsignal.instrument_sql("perform.query", nil, body) do
|
434
|
+
# # To be instrumented code
|
435
|
+
# end
|
436
|
+
#
|
437
|
+
# @example SQL query instrumentation
|
438
|
+
# body = "WHERE email = 'foo@..'"
|
439
|
+
# Appsignal.instrument_sql("perform.query", nil, body) do
|
440
|
+
# # query value will replace 'foo..' with a question mark `?`.
|
441
|
+
# end
|
442
|
+
#
|
443
|
+
# @param name [String] Name of the instrumented event. Read our event
|
444
|
+
# naming guide listed under "See also".
|
445
|
+
# @param title [String, nil] Human readable name of the event.
|
446
|
+
# @param body [String, nil] SQL query that's being executed.
|
447
|
+
# @yield yields the given block of code instrumented in an AppSignal
|
448
|
+
# event.
|
449
|
+
# @return [Object] Returns the block's return value.
|
450
|
+
#
|
451
|
+
# @see .instrument
|
452
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
|
453
|
+
# AppSignal custom instrumentation guide
|
454
|
+
# @see http://docs.appsignal.com/api/event-names.html
|
455
|
+
# AppSignal event naming guide
|
456
|
+
# @since 2.0.0
|
457
|
+
def instrument_sql(name, title = nil, body = nil, &block)
|
458
|
+
instrument(
|
459
|
+
name,
|
460
|
+
title,
|
461
|
+
body,
|
462
|
+
Appsignal::EventFormatter::SQL_BODY_FORMAT,
|
463
|
+
&block
|
464
|
+
)
|
465
|
+
end
|
466
|
+
|
467
|
+
# Convenience method for skipping instrumentations around a block of code.
|
468
|
+
#
|
469
|
+
# @example
|
470
|
+
# Appsignal.without_instrumentation do
|
471
|
+
# # Complex code here
|
472
|
+
# end
|
473
|
+
#
|
474
|
+
# @yield block of code that shouldn't be instrumented.
|
475
|
+
# @return [Object] Returns the return value of the block.
|
476
|
+
# @since 0.8.7
|
477
|
+
def without_instrumentation
|
478
|
+
Appsignal::Transaction.current.pause! if Appsignal::Transaction.current
|
479
|
+
yield
|
480
|
+
ensure
|
481
|
+
Appsignal::Transaction.current.resume! if Appsignal::Transaction.current
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|