tobox 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +191 -0
- data/README.md +384 -0
- data/exe/tobox +14 -0
- data/lib/tobox/application.rb +32 -0
- data/lib/tobox/cli.rb +145 -0
- data/lib/tobox/configuration.rb +122 -0
- data/lib/tobox/fetcher.rb +139 -0
- data/lib/tobox/plugins/datadog/configuration.rb +34 -0
- data/lib/tobox/plugins/datadog/integration.rb +39 -0
- data/lib/tobox/plugins/datadog/patcher.rb +26 -0
- data/lib/tobox/plugins/datadog.rb +102 -0
- data/lib/tobox/plugins/sentry.rb +143 -0
- data/lib/tobox/plugins/zeitwerk.rb +52 -0
- data/lib/tobox/pool/fiber_pool.rb +49 -0
- data/lib/tobox/pool/threaded_pool.rb +55 -0
- data/lib/tobox/pool.rb +19 -0
- data/lib/tobox/version.rb +5 -0
- data/lib/tobox/worker.rb +49 -0
- data/lib/tobox.rb +41 -0
- metadata +84 -0
data/lib/tobox/cli.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "optparse"
|
5
|
+
require "uri"
|
6
|
+
require_relative "../tobox"
|
7
|
+
|
8
|
+
module Tobox
|
9
|
+
class CLI
|
10
|
+
def self.run(args = ARGV)
|
11
|
+
new(args).run
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(args)
|
15
|
+
@options = parse(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
options = @options
|
20
|
+
logger = Logger.new($stderr)
|
21
|
+
|
22
|
+
config = Configuration.new do |c|
|
23
|
+
c.logger(logger)
|
24
|
+
c.instance_eval(File.read(options.fetch(:config_file)), options.fetch(:config_file), 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
# boot
|
28
|
+
options.fetch(:require).each(&method(:require))
|
29
|
+
|
30
|
+
# signals
|
31
|
+
pipe_read, pipe_write = IO.pipe
|
32
|
+
%w[INT TERM].each do |sig|
|
33
|
+
old_handler = Signal.trap(sig) do
|
34
|
+
if old_handler.respond_to?(:call)
|
35
|
+
begin
|
36
|
+
old_handler.call
|
37
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
38
|
+
puts ["Error in #{sig} handler", e].inspect
|
39
|
+
end
|
40
|
+
end
|
41
|
+
pipe_write.puts(sig)
|
42
|
+
end
|
43
|
+
rescue ArgumentError
|
44
|
+
puts "Signal #{sig} not supported"
|
45
|
+
end
|
46
|
+
|
47
|
+
app = Tobox::Application.new(config)
|
48
|
+
|
49
|
+
begin
|
50
|
+
app.start
|
51
|
+
|
52
|
+
logger.info "Running tobox-#{Tobox::VERSION} (#{RUBY_DESCRIPTION})"
|
53
|
+
logger.info "workers=#{config[:concurrency]}"
|
54
|
+
logger.info "Press Ctrl-C to stop"
|
55
|
+
|
56
|
+
while pipe_read.wait_readable
|
57
|
+
signal = pipe_read.gets.strip
|
58
|
+
handle_signal(signal)
|
59
|
+
end
|
60
|
+
rescue Interrupt
|
61
|
+
logger.info "Shutting down..."
|
62
|
+
app.stop
|
63
|
+
logger.info "Down!"
|
64
|
+
exit(0)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def parse(args)
|
71
|
+
opts = {
|
72
|
+
require: []
|
73
|
+
}
|
74
|
+
parser = OptionParser.new do |o|
|
75
|
+
o.on "-C", "--config PATH", "path to tobox .rb config file" do |arg|
|
76
|
+
arg = File.join(arg, "tobox.rb") if File.directory?(arg)
|
77
|
+
raise ArgumentError, "no such file #{arg}" unless File.exist?(arg)
|
78
|
+
|
79
|
+
opts[:config_file] = arg
|
80
|
+
end
|
81
|
+
|
82
|
+
o.on "-r", "--require [PATH|DIR]", "Location of application with files to require" do |arg|
|
83
|
+
requires = (opts[:require] ||= [])
|
84
|
+
if File.directory?(arg)
|
85
|
+
requires.concat(Dir.glob(File.join("**", "*.rb")))
|
86
|
+
else
|
87
|
+
raise ArgumentError, "no such file #{arg}" unless File.exist?(arg)
|
88
|
+
|
89
|
+
requires << arg
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
o.on "-d", "--database-uri DATABASE_URI", String, "location of the database with the outbox table" do |arg|
|
94
|
+
opts[:database_uri] = URI(arg)
|
95
|
+
end
|
96
|
+
|
97
|
+
o.on "-t", "--table TABLENAME", "(optional) name of the outbox database table" do |arg|
|
98
|
+
opts[:table] = arg
|
99
|
+
end
|
100
|
+
|
101
|
+
o.on "-c", "--concurrency INT", Integer, "processor threads to use" do |arg|
|
102
|
+
raise ArgumentError, "must be positive" unless arg.positive?
|
103
|
+
|
104
|
+
opts[:concurrency] = arg
|
105
|
+
end
|
106
|
+
|
107
|
+
o.on "-g", "--tag TAG", "Process tag for procline" do |arg|
|
108
|
+
opts[:tag] = arg
|
109
|
+
end
|
110
|
+
|
111
|
+
o.on "-t", "--shutdown-timeout NUM", Integer, "Shutdown timeout (in seconds)" do |arg|
|
112
|
+
raise ArgumentError, "must be positive" unless arg.positive?
|
113
|
+
|
114
|
+
opts[:shutdown_timeout] = arg
|
115
|
+
end
|
116
|
+
|
117
|
+
o.on "--verbose", "Print more verbose output" do |arg|
|
118
|
+
opts[:verbose] = arg
|
119
|
+
end
|
120
|
+
|
121
|
+
o.on "-v", "--version", "Print version and exit" do |_arg|
|
122
|
+
puts "Tobox #{Tobox::VERSION}"
|
123
|
+
exit(0)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
parser.banner = "tobox [options]"
|
128
|
+
parser.on_tail "-h", "--help", "Show help" do
|
129
|
+
$stdout.puts parser
|
130
|
+
exit(0)
|
131
|
+
end
|
132
|
+
parser.parse(args)
|
133
|
+
opts
|
134
|
+
end
|
135
|
+
|
136
|
+
def handle_signal(sig)
|
137
|
+
case sig
|
138
|
+
when "INT", "TERM"
|
139
|
+
raise Interrupt
|
140
|
+
else
|
141
|
+
warn "#{sig} is unsupported"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module Tobox
|
7
|
+
class Configuration
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_reader :handlers, :lifecycle_events, :arguments_handler, :default_logger
|
11
|
+
|
12
|
+
def_delegator :@config, :[]
|
13
|
+
|
14
|
+
DEFAULT_CONFIGURATION = {
|
15
|
+
environment: ENV.fetch("APP_ENV", "development"),
|
16
|
+
logger: nil,
|
17
|
+
log_level: nil,
|
18
|
+
database_uri: nil,
|
19
|
+
table: :outbox,
|
20
|
+
max_attempts: 10,
|
21
|
+
exponential_retry_factor: 4,
|
22
|
+
wait_for_events_delay: 5,
|
23
|
+
shutdown_timeout: 10,
|
24
|
+
concurrency: 4, # TODO: CPU count
|
25
|
+
worker: :thread
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
def initialize(name = nil, &block)
|
29
|
+
@name = name
|
30
|
+
@config = DEFAULT_CONFIGURATION.dup
|
31
|
+
|
32
|
+
@lifecycle_events = {}
|
33
|
+
@handlers = {}
|
34
|
+
@message_to_arguments = nil
|
35
|
+
@plugins = []
|
36
|
+
|
37
|
+
if block
|
38
|
+
case block.arity
|
39
|
+
when 0
|
40
|
+
instance_exec(&block)
|
41
|
+
when 1
|
42
|
+
yield(self)
|
43
|
+
else
|
44
|
+
raise Error, "configuration does not support blocks with more than one variable"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
env = @config[:environment]
|
49
|
+
@default_logger = @config[:logger] || Logger.new(STDERR) # rubocop:disable Style/GlobalStdStream
|
50
|
+
@default_logger.level = @config[:log_level] || (env == "production" ? Logger::INFO : Logger::DEBUG)
|
51
|
+
|
52
|
+
freeze
|
53
|
+
end
|
54
|
+
|
55
|
+
def on(event, &callback)
|
56
|
+
(@handlers[event.to_sym] ||= []) << callback
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_before_event(&callback)
|
61
|
+
(@lifecycle_events[:before_event] ||= []) << callback
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_after_event(&callback)
|
66
|
+
(@lifecycle_events[:after_event] ||= []) << callback
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_error_event(&callback)
|
71
|
+
(@lifecycle_events[:error_event] ||= []) << callback
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def message_to_arguments(&callback)
|
76
|
+
@arguments_handler = callback
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def plugin(plugin, _options = nil, &block)
|
81
|
+
raise Error, "Cannot add a plugin to a frozen config" if frozen?
|
82
|
+
|
83
|
+
plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
|
84
|
+
|
85
|
+
return if @plugins.include?(plugin)
|
86
|
+
|
87
|
+
@plugins << plugin
|
88
|
+
plugin.load_dependencies(self, &block) if plugin.respond_to?(:load_dependencies)
|
89
|
+
|
90
|
+
extend(plugin::ConfigurationMethods) if defined?(plugin::ConfigurationMethods)
|
91
|
+
|
92
|
+
plugin.configure(self, &block) if plugin.respond_to?(:configure)
|
93
|
+
end
|
94
|
+
|
95
|
+
def freeze
|
96
|
+
@name.freeze
|
97
|
+
@config.each_value(&:freeze).freeze
|
98
|
+
@handlers.each_value(&:freeze).freeze
|
99
|
+
@lifecycle_events.each_value(&:freeze).freeze
|
100
|
+
@plugins.freeze
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def method_missing(meth, *args, &block)
|
107
|
+
if DEFAULT_CONFIGURATION.key?(meth) && args.size == 1
|
108
|
+
@config[meth] = args.first
|
109
|
+
elsif /\Aon_(.*)\z/.match(meth) && args.size.zero?
|
110
|
+
on(Regexp.last_match(1).to_sym, &block)
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def respond_to_missing?(meth, *args)
|
117
|
+
super(meth, *args) ||
|
118
|
+
DEFAULT_CONFIGURATION.key?(meth) ||
|
119
|
+
/\Aon_(.*)\z/.match(meth)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Tobox
|
6
|
+
class Fetcher
|
7
|
+
def initialize(configuration)
|
8
|
+
@configuration = configuration
|
9
|
+
|
10
|
+
@logger = @configuration.default_logger
|
11
|
+
|
12
|
+
database_uri = @configuration[:database_uri]
|
13
|
+
@db = database_uri ? Sequel.connect(database_uri.to_s) : Sequel::DATABASES.first
|
14
|
+
raise Error, "no database found" unless @db
|
15
|
+
|
16
|
+
@db.extension :date_arithmetic
|
17
|
+
|
18
|
+
@db.loggers << @logger unless @configuration[:environment] == "production"
|
19
|
+
|
20
|
+
@table = configuration[:table]
|
21
|
+
@exponential_retry_factor = configuration[:exponential_retry_factor]
|
22
|
+
|
23
|
+
max_attempts = configuration[:max_attempts]
|
24
|
+
|
25
|
+
@ds = @db[@table]
|
26
|
+
|
27
|
+
run_at_conds = [
|
28
|
+
{ Sequel[@table][:run_at] => nil },
|
29
|
+
(Sequel.expr(Sequel[@table][:run_at]) < Sequel::CURRENT_TIMESTAMP)
|
30
|
+
].reduce { |agg, cond| Sequel.expr(agg) | Sequel.expr(cond) }
|
31
|
+
|
32
|
+
@pick_next_sql = @ds.where(Sequel[@table][:attempts] < max_attempts) # filter out exhausted attempts
|
33
|
+
.where(run_at_conds)
|
34
|
+
.order(Sequel.desc(:run_at, nulls: :first), :id)
|
35
|
+
.for_update
|
36
|
+
.skip_locked
|
37
|
+
.limit(1)
|
38
|
+
|
39
|
+
@before_event_handlers = Array(@configuration.lifecycle_events[:before_event])
|
40
|
+
@after_event_handlers = Array(@configuration.lifecycle_events[:after_event])
|
41
|
+
@error_event_handlers = Array(@configuration.lifecycle_events[:error_event])
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_events(&blk)
|
45
|
+
num_events = 0
|
46
|
+
@db.transaction do
|
47
|
+
event_ids = @pick_next_sql.select_map(:id) # lock starts here
|
48
|
+
|
49
|
+
events = nil
|
50
|
+
error = nil
|
51
|
+
unless event_ids.empty?
|
52
|
+
@db.transaction(savepoint: true) do
|
53
|
+
events = @ds.where(id: event_ids).returning.delete
|
54
|
+
|
55
|
+
if blk
|
56
|
+
num_events = events.size
|
57
|
+
|
58
|
+
events.each do |ev|
|
59
|
+
ev[:metadata] = JSON.parse(ev[:metadata].to_s) if ev[:metadata]
|
60
|
+
handle_before_event(ev)
|
61
|
+
yield(to_message(ev))
|
62
|
+
rescue StandardError => e
|
63
|
+
error = e
|
64
|
+
raise Sequel::Rollback
|
65
|
+
end
|
66
|
+
else
|
67
|
+
events.map!(&method(:to_message))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
return blk ? 0 : [] if events.nil?
|
73
|
+
|
74
|
+
return events unless blk
|
75
|
+
|
76
|
+
if events
|
77
|
+
events.each do |event|
|
78
|
+
if error
|
79
|
+
event.merge!(mark_as_error(event, error))
|
80
|
+
handle_error_event(event, error)
|
81
|
+
else
|
82
|
+
handle_after_event(event)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
num_events
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def mark_as_error(event, error)
|
94
|
+
@ds.where(id: event[:id]).returning.update(
|
95
|
+
attempts: Sequel[@table][:attempts] + 1,
|
96
|
+
run_at: Sequel.date_add(Sequel::CURRENT_TIMESTAMP,
|
97
|
+
seconds: event[:attempts] + (1**@exponential_retry_factor)),
|
98
|
+
# run_at: Sequel.date_add(Sequel::CURRENT_TIMESTAMP,
|
99
|
+
# seconds: Sequel.function(:POWER, Sequel[@table][:attempts] + 1, 4)),
|
100
|
+
last_error: "#{error.message}\n#{error.backtrace.join("\n")}"
|
101
|
+
).first
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_message(event)
|
105
|
+
{
|
106
|
+
id: event[:id],
|
107
|
+
type: event[:type],
|
108
|
+
before: (JSON.parse(event[:data_before].to_s) if event[:data_before]),
|
109
|
+
after: (JSON.parse(event[:data_after].to_s) if event[:data_after]),
|
110
|
+
at: event[:created_at]
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def handle_before_event(event)
|
115
|
+
@logger.debug { "outbox event (type: \"#{event[:type]}\", attempts: #{event[:attempts]}) starting..." }
|
116
|
+
@before_event_handlers.each do |hd|
|
117
|
+
hd.call(event)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_after_event(event)
|
122
|
+
@logger.debug { "outbox event (type: \"#{event[:type]}\", attempts: #{event[:attempts]}) completed" }
|
123
|
+
@after_event_handlers.each do |hd|
|
124
|
+
hd.call(event)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle_error_event(event, error)
|
129
|
+
@logger.error do
|
130
|
+
"outbox event (type: \"#{event[:type]}\", attempts: #{event[:attempts]}) failed with error\n" \
|
131
|
+
"#{error.class}: #{error.message}\n" \
|
132
|
+
"#{error.backtrace.join("\n")}"
|
133
|
+
end
|
134
|
+
@error_event_handlers.each do |hd|
|
135
|
+
hd.call(event, error)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "datadog/tracing/contrib/configuration/settings"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Tracing
|
7
|
+
module Contrib
|
8
|
+
module Tobox
|
9
|
+
module Configuration
|
10
|
+
class Settings < Contrib::Configuration::Settings
|
11
|
+
option :enabled do |o|
|
12
|
+
o.default { env_to_bool("DD_TRACE_SIDEKIQ_ENABLED", true) }
|
13
|
+
o.lazy
|
14
|
+
end
|
15
|
+
|
16
|
+
option :analytics_enabled do |o|
|
17
|
+
o.default { env_to_bool("DD_TRACE_SIDEKIQ_ANALYTICS_ENABLED", false) }
|
18
|
+
o.lazy
|
19
|
+
end
|
20
|
+
|
21
|
+
option :analytics_sample_rate do |o|
|
22
|
+
o.default { env_to_float("DD_TRACE_SIDEKIQ_ANALYTICS_SAMPLE_RATE", 1.0) }
|
23
|
+
o.lazy
|
24
|
+
end
|
25
|
+
|
26
|
+
option :service_name
|
27
|
+
option :error_handler, default: SpanOperation::Events::DEFAULT_ON_ERROR
|
28
|
+
option :distributed_tracing, default: false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "datadog/tracing/contrib/integration"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Tracing
|
7
|
+
module Contrib
|
8
|
+
module Tobox
|
9
|
+
class Integration
|
10
|
+
include Contrib::Integration
|
11
|
+
|
12
|
+
MINIMUM_VERSION = Gem::Version.new("0.1.0")
|
13
|
+
|
14
|
+
register_as :tobox
|
15
|
+
|
16
|
+
def self.version
|
17
|
+
Gem.loaded_specs["tobox"] && Gem.loaded_specs["tobox"].version
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.loaded?
|
21
|
+
!defined?(::Tobox).nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.compatible?
|
25
|
+
super && version >= MINIMUM_VERSION
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_configuration
|
29
|
+
Configuration::Settings.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def patcher
|
33
|
+
Patcher
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "datadog/tracing/contrib/patcher"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Tracing
|
7
|
+
module Contrib
|
8
|
+
module Tobox
|
9
|
+
module Patcher
|
10
|
+
include Contrib::Patcher
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def target_version
|
15
|
+
Integration.version
|
16
|
+
end
|
17
|
+
|
18
|
+
def patch
|
19
|
+
# server-patches provided by plugin(:sidekiq)
|
20
|
+
# TODO: use this once we have a producer side
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "datadog/configuration"
|
4
|
+
require_relative "datadog/integration"
|
5
|
+
require_relative "datadog/patcher"
|
6
|
+
|
7
|
+
module Tobox
|
8
|
+
module Plugins
|
9
|
+
module Datadog
|
10
|
+
class EventHandler
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
@db_table = @config[:table]
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_start(event)
|
17
|
+
datadog_config = ::Datadog.configuration.tracing[:tobox]
|
18
|
+
service = datadog_config[:service_name]
|
19
|
+
error_handler = datadog_config[:error_handler]
|
20
|
+
|
21
|
+
analytics_enabled = datadog_config[:analytics_enabled]
|
22
|
+
analytics_sample_rate = datadog_config[:analytics_sample_rate]
|
23
|
+
distributed_tracing = datadog_config[:distributed_tracing]
|
24
|
+
|
25
|
+
resource = event[:type]
|
26
|
+
|
27
|
+
if (metadata = event[:metadata])
|
28
|
+
previous_span = metadata["datadog-parent-id"]
|
29
|
+
|
30
|
+
if distributed_tracing && previous_span
|
31
|
+
trace_digest = ::Datadog::Tracing::TraceDigest.new(
|
32
|
+
span_id: previous_span,
|
33
|
+
trace_id: event[:metadata]["datadog-trace-id"],
|
34
|
+
trace_sampling_priority: event[:metadata]["datadog-sampling-priority"],
|
35
|
+
trace_origin: event[:metadata]["datadog-origin"]
|
36
|
+
)
|
37
|
+
::Datadog::Tracing.continue_trace!(trace_digest)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
span = ::Datadog::Tracing.trace(
|
42
|
+
"tobox.event",
|
43
|
+
service: service,
|
44
|
+
span_type: ::Datadog::Tracing::Metadata::Ext::AppTypes::TYPE_WORKER,
|
45
|
+
on_error: error_handler
|
46
|
+
)
|
47
|
+
span.resource = resource
|
48
|
+
|
49
|
+
span.set_tag(::Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, "tobox")
|
50
|
+
span.set_tag(::Datadog::Tracing::Metadata::Ext::TAG_OPERATION, "event")
|
51
|
+
|
52
|
+
if ::Datadog::Tracing::Contrib::Analytics.enabled?(analytics_enabled)
|
53
|
+
::Datadog::Tracing::Contrib::Analytics.set_sample_rate(span, analytics_sample_rate)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Measure service stats
|
57
|
+
::Datadog::Tracing::Contrib::Analytics.set_measured(span)
|
58
|
+
|
59
|
+
span.set_tag("tobox.event.id", event[:id])
|
60
|
+
span.set_tag("tobox.event.type", event[:type])
|
61
|
+
span.set_tag("tobox.event.retry", event[:attempts])
|
62
|
+
span.set_tag("tobox.event.table", @db_table)
|
63
|
+
span.set_tag("tobox.event.delay", (Time.now.utc - event[:created_at]).to_f)
|
64
|
+
|
65
|
+
event[:__tobox_event_span] = span
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_finish(event)
|
69
|
+
span = event[:__tobox_event_span]
|
70
|
+
|
71
|
+
return unless span
|
72
|
+
|
73
|
+
span.finish
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_error(event, error)
|
77
|
+
span = event[:__tobox_event_span]
|
78
|
+
|
79
|
+
return unless span
|
80
|
+
|
81
|
+
span.set_error(error)
|
82
|
+
span.finish
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class << self
|
87
|
+
def load_dependencies(*)
|
88
|
+
require "uri"
|
89
|
+
end
|
90
|
+
|
91
|
+
def configure(config)
|
92
|
+
event_handler = EventHandler.new(config)
|
93
|
+
config.on_before_event(&event_handler.method(:on_start))
|
94
|
+
config.on_after_event(&event_handler.method(:on_finish))
|
95
|
+
config.on_error_event(&event_handler.method(:on_error))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
register_plugin :datadog, Datadog
|
101
|
+
end
|
102
|
+
end
|