atatus 1.0.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 +7 -0
- data/.gitignore +16 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +57 -0
- data/LICENSE +65 -0
- data/LICENSE-THIRD-PARTY +205 -0
- data/README.md +13 -0
- data/Rakefile +19 -0
- data/atatus.gemspec +36 -0
- data/atatus.yml +2 -0
- data/bench/.gitignore +2 -0
- data/bench/app.rb +53 -0
- data/bench/benchmark.rb +36 -0
- data/bench/report.rb +55 -0
- data/bench/rubyprof.rb +39 -0
- data/bench/stackprof.rb +23 -0
- data/bin/build_docs +5 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/bin/with_framework +7 -0
- data/lib/atatus.rb +325 -0
- data/lib/atatus/agent.rb +260 -0
- data/lib/atatus/central_config.rb +141 -0
- data/lib/atatus/central_config/cache_control.rb +34 -0
- data/lib/atatus/collector/base.rb +329 -0
- data/lib/atatus/collector/builder.rb +317 -0
- data/lib/atatus/collector/transport.rb +72 -0
- data/lib/atatus/config.rb +248 -0
- data/lib/atatus/config/bytes.rb +25 -0
- data/lib/atatus/config/duration.rb +23 -0
- data/lib/atatus/config/options.rb +134 -0
- data/lib/atatus/config/regexp_list.rb +13 -0
- data/lib/atatus/context.rb +33 -0
- data/lib/atatus/context/request.rb +11 -0
- data/lib/atatus/context/request/socket.rb +19 -0
- data/lib/atatus/context/request/url.rb +42 -0
- data/lib/atatus/context/response.rb +22 -0
- data/lib/atatus/context/user.rb +42 -0
- data/lib/atatus/context_builder.rb +97 -0
- data/lib/atatus/deprecations.rb +22 -0
- data/lib/atatus/error.rb +22 -0
- data/lib/atatus/error/exception.rb +46 -0
- data/lib/atatus/error/log.rb +24 -0
- data/lib/atatus/error_builder.rb +76 -0
- data/lib/atatus/instrumenter.rb +224 -0
- data/lib/atatus/internal_error.rb +6 -0
- data/lib/atatus/logging.rb +55 -0
- data/lib/atatus/metadata.rb +19 -0
- data/lib/atatus/metadata/process_info.rb +18 -0
- data/lib/atatus/metadata/service_info.rb +61 -0
- data/lib/atatus/metadata/system_info.rb +35 -0
- data/lib/atatus/metadata/system_info/container_info.rb +121 -0
- data/lib/atatus/metadata/system_info/hw_info.rb +118 -0
- data/lib/atatus/metadata/system_info/os_info.rb +31 -0
- data/lib/atatus/metrics.rb +98 -0
- data/lib/atatus/metrics/cpu_mem.rb +240 -0
- data/lib/atatus/metrics/vm.rb +60 -0
- data/lib/atatus/metricset.rb +19 -0
- data/lib/atatus/middleware.rb +76 -0
- data/lib/atatus/naively_hashable.rb +21 -0
- data/lib/atatus/normalizers.rb +68 -0
- data/lib/atatus/normalizers/action_controller.rb +27 -0
- data/lib/atatus/normalizers/action_mailer.rb +26 -0
- data/lib/atatus/normalizers/action_view.rb +77 -0
- data/lib/atatus/normalizers/active_record.rb +45 -0
- data/lib/atatus/opentracing.rb +346 -0
- data/lib/atatus/rails.rb +61 -0
- data/lib/atatus/railtie.rb +30 -0
- data/lib/atatus/span.rb +125 -0
- data/lib/atatus/span/context.rb +40 -0
- data/lib/atatus/span_helpers.rb +44 -0
- data/lib/atatus/spies.rb +86 -0
- data/lib/atatus/spies/action_dispatch.rb +28 -0
- data/lib/atatus/spies/delayed_job.rb +68 -0
- data/lib/atatus/spies/elasticsearch.rb +36 -0
- data/lib/atatus/spies/faraday.rb +70 -0
- data/lib/atatus/spies/http.rb +44 -0
- data/lib/atatus/spies/json.rb +22 -0
- data/lib/atatus/spies/mongo.rb +87 -0
- data/lib/atatus/spies/net_http.rb +70 -0
- data/lib/atatus/spies/rake.rb +45 -0
- data/lib/atatus/spies/redis.rb +27 -0
- data/lib/atatus/spies/sequel.rb +47 -0
- data/lib/atatus/spies/sidekiq.rb +89 -0
- data/lib/atatus/spies/sinatra.rb +41 -0
- data/lib/atatus/spies/tilt.rb +27 -0
- data/lib/atatus/sql_summarizer.rb +35 -0
- data/lib/atatus/stacktrace.rb +16 -0
- data/lib/atatus/stacktrace/frame.rb +52 -0
- data/lib/atatus/stacktrace_builder.rb +104 -0
- data/lib/atatus/subscriber.rb +77 -0
- data/lib/atatus/trace_context.rb +85 -0
- data/lib/atatus/transaction.rb +100 -0
- data/lib/atatus/transport/base.rb +174 -0
- data/lib/atatus/transport/connection.rb +156 -0
- data/lib/atatus/transport/connection/http.rb +116 -0
- data/lib/atatus/transport/connection/proxy_pipe.rb +75 -0
- data/lib/atatus/transport/filters.rb +43 -0
- data/lib/atatus/transport/filters/secrets_filter.rb +74 -0
- data/lib/atatus/transport/serializers.rb +93 -0
- data/lib/atatus/transport/serializers/context_serializer.rb +85 -0
- data/lib/atatus/transport/serializers/error_serializer.rb +77 -0
- data/lib/atatus/transport/serializers/metadata_serializer.rb +70 -0
- data/lib/atatus/transport/serializers/metricset_serializer.rb +28 -0
- data/lib/atatus/transport/serializers/span_serializer.rb +80 -0
- data/lib/atatus/transport/serializers/transaction_serializer.rb +37 -0
- data/lib/atatus/transport/worker.rb +73 -0
- data/lib/atatus/util.rb +42 -0
- data/lib/atatus/util/inflector.rb +93 -0
- data/lib/atatus/util/lru_cache.rb +48 -0
- data/lib/atatus/util/prefixed_logger.rb +18 -0
- data/lib/atatus/util/throttle.rb +35 -0
- data/lib/atatus/version.rb +5 -0
- data/vendor/.gitkeep +0 -0
- metadata +190 -0
data/lib/atatus/rails.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'atatus/subscriber'
|
|
4
|
+
|
|
5
|
+
module Atatus
|
|
6
|
+
# Module for explicitly starting the Atatus agent and hooking into Rails.
|
|
7
|
+
# It is recommended to use the Railtie instead.
|
|
8
|
+
module Rails
|
|
9
|
+
extend self
|
|
10
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
11
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
12
|
+
# Start the Atatus agent and hook into Rails.
|
|
13
|
+
# Note that the agent won't be started if the Rails console is being used.
|
|
14
|
+
#
|
|
15
|
+
# @param config [Config, Hash] An instance of Config or a Hash config.
|
|
16
|
+
# @return [true, nil] true if the agent was started, nil otherwise.
|
|
17
|
+
def start(config)
|
|
18
|
+
config = Config.new(config) unless config.is_a?(Config)
|
|
19
|
+
if (reason = should_skip?(config))
|
|
20
|
+
unless config.disable_start_message?
|
|
21
|
+
config.logger.info "Skipping because: #{reason}. " \
|
|
22
|
+
"Start manually with `Atatus.start'"
|
|
23
|
+
end
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Atatus.start(config).tap do |agent|
|
|
28
|
+
attach_subscriber(agent)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if Atatus.running? &&
|
|
32
|
+
!Atatus.agent.config.disabled_instrumentations.include?(
|
|
33
|
+
'action_dispatch'
|
|
34
|
+
)
|
|
35
|
+
require 'atatus/spies/action_dispatch'
|
|
36
|
+
end
|
|
37
|
+
Atatus.running?
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
config.logger.error format('Failed to start: %s', e.message)
|
|
40
|
+
config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
|
|
41
|
+
end
|
|
42
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
43
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def should_skip?(_config)
|
|
48
|
+
if ::Rails.const_defined? 'Rails::Console'
|
|
49
|
+
return 'Rails console'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def attach_subscriber(agent)
|
|
56
|
+
return unless agent
|
|
57
|
+
|
|
58
|
+
agent.instrumenter.subscriber = Atatus::Subscriber.new(agent)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'atatus/rails'
|
|
4
|
+
|
|
5
|
+
module Atatus
|
|
6
|
+
# @api private
|
|
7
|
+
class Railtie < ::Rails::Railtie
|
|
8
|
+
config.atatus = ActiveSupport::OrderedOptions.new
|
|
9
|
+
|
|
10
|
+
Config.schema.each do |key, args|
|
|
11
|
+
next unless args.length > 1
|
|
12
|
+
config.atatus[key] = args.last[:default]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer 'atatus.initialize' do |app|
|
|
16
|
+
config = Config.new(app.config.atatus).tap do |c|
|
|
17
|
+
c.app = app
|
|
18
|
+
|
|
19
|
+
# Prepend Rails.root to log_path if present
|
|
20
|
+
if c.log_path && !c.log_path.start_with?('/')
|
|
21
|
+
c.log_path = ::Rails.root.join(c.log_path)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if Rails.start(config)
|
|
26
|
+
app.middleware.insert 0, Middleware
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/atatus/span.rb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
require 'atatus/span/context'
|
|
7
|
+
|
|
8
|
+
module Atatus
|
|
9
|
+
# @api private
|
|
10
|
+
class Span
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
def_delegators :@trace_context, :trace_id, :parent_id, :id
|
|
14
|
+
|
|
15
|
+
DEFAULT_TYPE = 'custom'
|
|
16
|
+
|
|
17
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
|
18
|
+
def initialize(
|
|
19
|
+
name:,
|
|
20
|
+
transaction_id:,
|
|
21
|
+
trace_context:,
|
|
22
|
+
type: nil,
|
|
23
|
+
subtype: nil,
|
|
24
|
+
action: nil,
|
|
25
|
+
context: nil,
|
|
26
|
+
stacktrace_builder: nil
|
|
27
|
+
)
|
|
28
|
+
@name = name
|
|
29
|
+
|
|
30
|
+
if subtype.nil? && type&.include?('.')
|
|
31
|
+
@type, @subtype, @action = type.split('.')
|
|
32
|
+
else
|
|
33
|
+
@type = type || DEFAULT_TYPE
|
|
34
|
+
@subtype = subtype
|
|
35
|
+
@action = action
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@transaction_id = transaction_id
|
|
39
|
+
@trace_context = trace_context
|
|
40
|
+
|
|
41
|
+
@context = context || Span::Context.new
|
|
42
|
+
@stacktrace_builder = stacktrace_builder
|
|
43
|
+
end
|
|
44
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
|
|
45
|
+
|
|
46
|
+
attr_accessor(
|
|
47
|
+
:action,
|
|
48
|
+
:name,
|
|
49
|
+
:original_backtrace,
|
|
50
|
+
:subtype,
|
|
51
|
+
:trace_context,
|
|
52
|
+
:type
|
|
53
|
+
)
|
|
54
|
+
attr_reader(
|
|
55
|
+
:context,
|
|
56
|
+
:duration,
|
|
57
|
+
:stacktrace,
|
|
58
|
+
:timestamp,
|
|
59
|
+
:transaction_id
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# life cycle
|
|
63
|
+
|
|
64
|
+
def start(clock_start = Util.monotonic_micros)
|
|
65
|
+
@timestamp = Util.micros
|
|
66
|
+
@clock_start = clock_start
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def stop(clock_end = Util.monotonic_micros)
|
|
71
|
+
@duration ||= (clock_end - @clock_start)
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def done(clock_end: Util.monotonic_micros)
|
|
76
|
+
stop clock_end
|
|
77
|
+
|
|
78
|
+
build_stacktrace! if should_build_stacktrace?
|
|
79
|
+
self.original_backtrace = nil # release original
|
|
80
|
+
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def stopped?
|
|
85
|
+
!!duration
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def started?
|
|
89
|
+
!!timestamp
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def running?
|
|
93
|
+
started? && !stopped?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# relations
|
|
97
|
+
|
|
98
|
+
def inspect
|
|
99
|
+
"<Atatus::Span id:#{id}" \
|
|
100
|
+
" name:#{name.inspect}" \
|
|
101
|
+
" type:#{type.inspect}" \
|
|
102
|
+
'>'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def build_stacktrace!
|
|
108
|
+
@stacktrace = @stacktrace_builder.build(original_backtrace, type: :span)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def should_build_stacktrace?
|
|
112
|
+
@stacktrace_builder && original_backtrace && long_enough_for_stacktrace?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def long_enough_for_stacktrace?
|
|
116
|
+
min_duration =
|
|
117
|
+
@stacktrace_builder.config.span_frames_min_duration_us
|
|
118
|
+
|
|
119
|
+
return true if min_duration < 0
|
|
120
|
+
return false if min_duration == 0
|
|
121
|
+
|
|
122
|
+
duration >= min_duration
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Atatus
|
|
4
|
+
class Span
|
|
5
|
+
# @api private
|
|
6
|
+
class Context
|
|
7
|
+
def initialize(db: nil, http: nil, labels: {})
|
|
8
|
+
@sync = true
|
|
9
|
+
@db = db && Db.new(db)
|
|
10
|
+
@http = http && Http.new(http)
|
|
11
|
+
@labels = labels
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_accessor :sync, :db, :http, :labels
|
|
15
|
+
|
|
16
|
+
# @api private
|
|
17
|
+
class Db
|
|
18
|
+
def initialize(instance: nil, statement: nil, type: nil, user: nil)
|
|
19
|
+
@instance = instance
|
|
20
|
+
@statement = statement
|
|
21
|
+
@type = type
|
|
22
|
+
@user = user
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_accessor :instance, :statement, :type, :user
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
class Http
|
|
30
|
+
def initialize(url: nil, status_code: nil, method: nil)
|
|
31
|
+
@url = url
|
|
32
|
+
@status_code = status_code
|
|
33
|
+
@method = method
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_accessor :url, :status_code, :method
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Atatus
|
|
4
|
+
# @api private
|
|
5
|
+
module SpanHelpers
|
|
6
|
+
# @api private
|
|
7
|
+
module ClassMethods
|
|
8
|
+
def span_class_method(method, name = nil, type = nil)
|
|
9
|
+
__span_method_on(singleton_class, method, name, type)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def span_method(method, name = nil, type = nil)
|
|
13
|
+
__span_method_on(self, method, name, type)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def __span_method_on(klass, method, name = nil, type = nil)
|
|
19
|
+
name ||= method.to_s
|
|
20
|
+
type ||= Span::DEFAULT_TYPE
|
|
21
|
+
|
|
22
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
23
|
+
alias :"__without_apm_#{method}" :"#{method}"
|
|
24
|
+
|
|
25
|
+
def #{method}(*args, &block)
|
|
26
|
+
unless Atatus.current_transaction
|
|
27
|
+
return __without_apm_#{method}(*args, &block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Atatus.with_span "#{name}", "#{type}" do
|
|
31
|
+
__without_apm_#{method}(*args, &block)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
RUBY
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.included(kls)
|
|
39
|
+
kls.class_eval do
|
|
40
|
+
extend ClassMethods
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/atatus/spies.rb
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'atatus/util/inflector'
|
|
5
|
+
|
|
6
|
+
module Atatus
|
|
7
|
+
# @api private
|
|
8
|
+
module Spies
|
|
9
|
+
# @api private
|
|
10
|
+
class Registration
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
def initialize(const_name, require_paths, spy)
|
|
14
|
+
@const_name = const_name
|
|
15
|
+
@require_paths = Array(require_paths)
|
|
16
|
+
@spy = spy
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :const_name, :require_paths
|
|
20
|
+
|
|
21
|
+
def_delegator :@spy, :install
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.require_hooks
|
|
25
|
+
@require_hooks ||= {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.installed
|
|
29
|
+
@installed ||= {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.register(*args)
|
|
33
|
+
registration = Registration.new(*args)
|
|
34
|
+
|
|
35
|
+
if safe_defined?(registration.const_name)
|
|
36
|
+
registration.install
|
|
37
|
+
installed[registration.const_name] = registration
|
|
38
|
+
else
|
|
39
|
+
register_require_hook registration
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.register_require_hook(registration)
|
|
44
|
+
registration.require_paths.each do |path|
|
|
45
|
+
require_hooks[path] = registration
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.hook_into(name)
|
|
50
|
+
return unless (registration = require_hooks[name])
|
|
51
|
+
return unless safe_defined?(registration.const_name)
|
|
52
|
+
|
|
53
|
+
installed[registration.const_name] = registration
|
|
54
|
+
registration.install
|
|
55
|
+
|
|
56
|
+
registration.require_paths.each do |path|
|
|
57
|
+
require_hooks.delete path
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.safe_defined?(const_name)
|
|
62
|
+
Util::Inflector.safe_constantize(const_name)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
module Kernel
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
alias require_without_apm require
|
|
72
|
+
|
|
73
|
+
def require(path)
|
|
74
|
+
res = require_without_apm(path)
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
Atatus::Spies.hook_into(path)
|
|
78
|
+
rescue ::Exception => e
|
|
79
|
+
puts "Failed hooking into '#{path}'. Please report this at " \
|
|
80
|
+
'success@atatus.com'
|
|
81
|
+
puts e.backtrace.join("\n")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
res
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Atatus
|
|
4
|
+
# @api private
|
|
5
|
+
module Spies
|
|
6
|
+
# @api private
|
|
7
|
+
class ActionDispatchSpy
|
|
8
|
+
def install
|
|
9
|
+
::ActionDispatch::ShowExceptions.class_eval do
|
|
10
|
+
alias render_exception_without_apm render_exception
|
|
11
|
+
|
|
12
|
+
def render_exception(env, exception)
|
|
13
|
+
context = Atatus.build_context(rack_env: env, for_type: :error)
|
|
14
|
+
Atatus.report(exception, context: context, handled: false)
|
|
15
|
+
|
|
16
|
+
render_exception_without_apm env, exception
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
register(
|
|
23
|
+
'ActionDispatch::ShowExceptions',
|
|
24
|
+
'action_dispatch/show_exception',
|
|
25
|
+
ActionDispatchSpy.new
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Atatus
|
|
4
|
+
# @api private
|
|
5
|
+
module Spies
|
|
6
|
+
# @api private
|
|
7
|
+
class DelayedJobSpy
|
|
8
|
+
CLASS_SEPARATOR = '.'
|
|
9
|
+
METHOD_SEPARATOR = '#'
|
|
10
|
+
TYPE = 'Delayed::Job'
|
|
11
|
+
|
|
12
|
+
def install
|
|
13
|
+
::Delayed::Backend::Base.class_eval do
|
|
14
|
+
alias invoke_job_without_apm invoke_job
|
|
15
|
+
|
|
16
|
+
def invoke_job(*args, &block)
|
|
17
|
+
::Atatus::Spies::DelayedJobSpy
|
|
18
|
+
.invoke_job(self, *args, &block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.invoke_job(job, *args, &block)
|
|
24
|
+
job_name = name_from_payload(job.payload_object)
|
|
25
|
+
transaction = Atatus.start_transaction(job_name, TYPE)
|
|
26
|
+
job.invoke_job_without_apm(*args, &block)
|
|
27
|
+
transaction.done 'success'
|
|
28
|
+
rescue ::Exception => e
|
|
29
|
+
Atatus.report(e, handled: false)
|
|
30
|
+
transaction.done 'error'
|
|
31
|
+
raise
|
|
32
|
+
ensure
|
|
33
|
+
Atatus.end_transaction
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.name_from_payload(payload_object)
|
|
37
|
+
if payload_object.is_a?(::Delayed::PerformableMethod)
|
|
38
|
+
performable_method_name(payload_object)
|
|
39
|
+
else
|
|
40
|
+
payload_object.class.name
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.performable_method_name(payload_object)
|
|
45
|
+
class_name = object_name(payload_object)
|
|
46
|
+
separator = name_separator(payload_object)
|
|
47
|
+
method_name = payload_object.method_name
|
|
48
|
+
"#{class_name}#{separator}#{method_name}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.object_name(payload_object)
|
|
52
|
+
object = payload_object.object
|
|
53
|
+
klass = object.is_a?(Class) ? object : object.class
|
|
54
|
+
klass.name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.name_separator(payload_object)
|
|
58
|
+
payload_object.object.is_a?(Class) ? CLASS_SEPARATOR : METHOD_SEPARATOR
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
register(
|
|
63
|
+
'Delayed::Backend::Base',
|
|
64
|
+
'delayed/backend/base',
|
|
65
|
+
DelayedJobSpy.new
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|