tobox 0.1.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/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
|