good_job 1.0.1 → 1.1.2
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/CHANGELOG.md +79 -2
- data/README.md +230 -56
- data/lib/active_job/queue_adapters/good_job_adapter.rb +3 -12
- data/lib/generators/good_job/install_generator.rb +24 -0
- data/lib/generators/good_job/templates/migration.rb.erb +20 -0
- data/lib/good_job.rb +6 -2
- data/lib/good_job/adapter.rb +30 -16
- data/lib/good_job/cli.rb +18 -48
- data/lib/good_job/configuration.rb +55 -0
- data/lib/good_job/job.rb +43 -25
- data/lib/good_job/log_subscriber.rb +110 -0
- data/lib/good_job/multi_scheduler.rb +25 -0
- data/lib/good_job/performer.rb +4 -1
- data/lib/good_job/railtie.rb +1 -0
- data/lib/good_job/scheduler.rb +82 -15
- data/lib/good_job/version.rb +1 -1
- metadata +38 -6
- data/lib/good_job/logging.rb +0 -70
@@ -1,18 +1,9 @@
|
|
1
1
|
module ActiveJob
|
2
2
|
module QueueAdapters
|
3
3
|
class GoodJobAdapter < GoodJob::Adapter
|
4
|
-
def initialize(execution_mode: nil)
|
5
|
-
|
6
|
-
|
7
|
-
elsif ENV['GOOD_JOB_EXECUTION_MODE'].present?
|
8
|
-
ENV['GOOD_JOB_EXECUTION_MODE'].to_sym
|
9
|
-
elsif Rails.env.development? || Rails.env.test?
|
10
|
-
:inline
|
11
|
-
else
|
12
|
-
:external
|
13
|
-
end
|
14
|
-
|
15
|
-
super(execution_mode: execution_mode)
|
4
|
+
def initialize(execution_mode: nil, max_threads: nil, poll_interval: nil, scheduler: nil, inline: false)
|
5
|
+
configuration = GoodJob::Configuration.new({ execution_mode: execution_mode }, env: ENV)
|
6
|
+
super(execution_mode: configuration.rails_execution_mode, max_threads: max_threads, poll_interval: poll_interval, scheduler: scheduler, inline: inline)
|
16
7
|
end
|
17
8
|
end
|
18
9
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module GoodJob
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
class << self
|
9
|
+
delegate :next_migration_number, to: ActiveRecord::Generators::Base
|
10
|
+
end
|
11
|
+
|
12
|
+
source_paths << File.join(File.dirname(__FILE__), "templates")
|
13
|
+
|
14
|
+
def create_migration_file
|
15
|
+
migration_template 'migration.rb.erb', 'db/migrate/create_good_jobs.rb', migration_version: migration_version
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def migration_version
|
21
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
enable_extension 'pgcrypto'
|
4
|
+
|
5
|
+
create_table :good_jobs, id: :uuid do |t|
|
6
|
+
t.text :queue_name
|
7
|
+
t.integer :priority
|
8
|
+
t.jsonb :serialized_params
|
9
|
+
t.timestamp :scheduled_at
|
10
|
+
t.timestamp :performed_at
|
11
|
+
t.timestamp :finished_at
|
12
|
+
t.text :error
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)"
|
18
|
+
add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)"
|
19
|
+
end
|
20
|
+
end
|
data/lib/good_job.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "rails"
|
2
2
|
require 'good_job/railtie'
|
3
3
|
|
4
|
-
require 'good_job/
|
4
|
+
require 'good_job/configuration'
|
5
|
+
require 'good_job/log_subscriber'
|
5
6
|
require 'good_job/lockable'
|
6
7
|
require 'good_job/job'
|
7
8
|
require 'good_job/scheduler'
|
9
|
+
require 'good_job/multi_scheduler'
|
8
10
|
require 'good_job/adapter'
|
9
11
|
require 'good_job/pg_locks'
|
10
12
|
require 'good_job/performer'
|
@@ -12,8 +14,10 @@ require 'good_job/performer'
|
|
12
14
|
require 'active_job/queue_adapters/good_job_adapter'
|
13
15
|
|
14
16
|
module GoodJob
|
17
|
+
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
15
18
|
mattr_accessor :preserve_job_records, default: false
|
16
|
-
|
19
|
+
mattr_accessor :reperform_jobs_on_standard_error, default: true
|
20
|
+
mattr_accessor :on_thread_error, default: nil
|
17
21
|
|
18
22
|
ActiveSupport.run_load_hooks(:good_job, self)
|
19
23
|
end
|
data/lib/good_job/adapter.rb
CHANGED
@@ -1,18 +1,26 @@
|
|
1
1
|
module GoodJob
|
2
2
|
class Adapter
|
3
|
-
EXECUTION_MODES = [:
|
3
|
+
EXECUTION_MODES = [:async, :external, :inline].freeze
|
4
4
|
|
5
|
-
def initialize(execution_mode: nil, inline: false)
|
6
|
-
if inline
|
5
|
+
def initialize(execution_mode: nil, max_threads: nil, poll_interval: nil, scheduler: nil, inline: false)
|
6
|
+
if inline && execution_mode.nil?
|
7
7
|
ActiveSupport::Deprecation.warn('GoodJob::Adapter#new(inline: true) is deprecated; use GoodJob::Adapter.new(execution_mode: :inline) instead')
|
8
|
-
|
9
|
-
elsif execution_mode
|
10
|
-
raise ArgumentError, "execution_mode: must be one of #{EXECUTION_MODES.join(', ')}." unless EXECUTION_MODES.include?(execution_mode)
|
11
|
-
|
12
|
-
@execution_mode = execution_mode
|
13
|
-
else
|
14
|
-
@execution_mode = :external
|
8
|
+
execution_mode = :inline
|
15
9
|
end
|
10
|
+
|
11
|
+
configuration = GoodJob::Configuration.new({
|
12
|
+
execution_mode: execution_mode,
|
13
|
+
max_threads: max_threads,
|
14
|
+
poll_interval: poll_interval,
|
15
|
+
},
|
16
|
+
env: ENV)
|
17
|
+
|
18
|
+
raise ArgumentError, "execution_mode: must be one of #{EXECUTION_MODES.join(', ')}." unless EXECUTION_MODES.include?(configuration.execution_mode)
|
19
|
+
|
20
|
+
@execution_mode = configuration.execution_mode
|
21
|
+
|
22
|
+
@scheduler = scheduler
|
23
|
+
@scheduler = GoodJob::Scheduler.from_configuration(configuration) if @execution_mode == :async && @scheduler.blank?
|
16
24
|
end
|
17
25
|
|
18
26
|
def enqueue(active_job)
|
@@ -34,11 +42,21 @@ module GoodJob
|
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
45
|
+
@scheduler.create_thread if execute_async?
|
46
|
+
|
37
47
|
good_job
|
38
48
|
end
|
39
49
|
|
40
|
-
def shutdown(wait: true)
|
41
|
-
|
50
|
+
def shutdown(wait: true)
|
51
|
+
@scheduler&.shutdown(wait: wait)
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute_async?
|
55
|
+
@execution_mode == :async
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute_externally?
|
59
|
+
@execution_mode == :external
|
42
60
|
end
|
43
61
|
|
44
62
|
def execute_inline?
|
@@ -49,9 +67,5 @@ module GoodJob
|
|
49
67
|
ActiveSupport::Deprecation.warn('GoodJob::Adapter::inline? is deprecated; use GoodJob::Adapter::execute_inline? instead')
|
50
68
|
execute_inline?
|
51
69
|
end
|
52
|
-
|
53
|
-
def execute_externally?
|
54
|
-
@execution_mode == :external
|
55
|
-
end
|
56
70
|
end
|
57
71
|
end
|
data/lib/good_job/cli.rb
CHANGED
@@ -10,53 +10,16 @@ module GoodJob
|
|
10
10
|
desc: "Maximum number of threads to use for working jobs (default: ActiveRecord::Base.connection_pool.size)"
|
11
11
|
method_option :queues,
|
12
12
|
type: :string,
|
13
|
-
banner: "queue1,queue2",
|
14
|
-
desc: "Queues to work from. Separate multiple queues with commas (default: *)"
|
13
|
+
banner: "queue1,queue2(;queue3,queue4:5;-queue1,queue2)",
|
14
|
+
desc: "Queues to work from. Separate multiple queues with commas; exclude queues with a leading minus; separate isolated execution pools with semicolons and threads with colons (default: *)"
|
15
15
|
method_option :poll_interval,
|
16
16
|
type: :numeric,
|
17
17
|
desc: "Interval between polls for available jobs in seconds (default: 1)"
|
18
18
|
def start
|
19
|
-
|
19
|
+
set_up_application!
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
ENV['GOOD_JOB_MAX_THREADS'] ||
|
24
|
-
ENV['RAILS_MAX_THREADS'] ||
|
25
|
-
ActiveRecord::Base.connection_pool.size
|
26
|
-
).to_i
|
27
|
-
|
28
|
-
queue_names = (
|
29
|
-
options[:queues] ||
|
30
|
-
ENV['GOOD_JOB_QUEUES'] ||
|
31
|
-
'*'
|
32
|
-
).split(',').map(&:strip)
|
33
|
-
|
34
|
-
poll_interval = (
|
35
|
-
options[:poll_interval] ||
|
36
|
-
ENV['GOOD_JOB_POLL_INTERVAL']
|
37
|
-
).to_i
|
38
|
-
|
39
|
-
job_query = GoodJob::Job.all.priority_ordered
|
40
|
-
queue_names_without_all = queue_names.reject { |q| q == '*' }
|
41
|
-
job_query = job_query.where(queue_name: queue_names_without_all) unless queue_names_without_all.size.zero?
|
42
|
-
|
43
|
-
performer_method = if GoodJob.preserve_job_records
|
44
|
-
:perform_with_advisory_lock_and_preserve_job_records
|
45
|
-
else
|
46
|
-
:perform_with_advisory_lock_and_destroy_job_records
|
47
|
-
end
|
48
|
-
job_performer = GoodJob::Performer.new(job_query, performer_method)
|
49
|
-
|
50
|
-
$stdout.puts "GoodJob worker starting with max_threads=#{max_threads} on queues=#{queue_names.join(',')}"
|
51
|
-
|
52
|
-
timer_options = {}
|
53
|
-
timer_options[:execution_interval] = poll_interval if poll_interval.positive?
|
54
|
-
|
55
|
-
pool_options = {
|
56
|
-
max_threads: max_threads,
|
57
|
-
}
|
58
|
-
|
59
|
-
scheduler = GoodJob::Scheduler.new(job_performer, timer_options: timer_options, pool_options: pool_options)
|
21
|
+
configuration = Configuration.new(options, env: ENV)
|
22
|
+
scheduler = Scheduler.from_configuration(configuration)
|
60
23
|
|
61
24
|
@stop_good_job_executable = false
|
62
25
|
%w[INT TERM].each do |signal|
|
@@ -68,24 +31,31 @@ module GoodJob
|
|
68
31
|
break if @stop_good_job_executable || scheduler.shutdown?
|
69
32
|
end
|
70
33
|
|
71
|
-
$stdout.puts "\nFinishing GoodJob's current jobs before exiting..."
|
72
34
|
scheduler.shutdown
|
73
|
-
$stdout.puts "GoodJob's jobs finished, exiting..."
|
74
35
|
end
|
75
36
|
|
37
|
+
default_task :start
|
38
|
+
|
76
39
|
desc :cleanup_preserved_jobs, "Delete preserved job records"
|
77
40
|
method_option :before_seconds_ago,
|
78
41
|
type: :numeric,
|
79
42
|
default: 24 * 60 * 60,
|
80
43
|
desc: "Delete records finished more than this many seconds ago"
|
81
44
|
def cleanup_preserved_jobs
|
82
|
-
|
45
|
+
set_up_application!
|
83
46
|
|
84
47
|
timestamp = Time.current - options[:before_seconds_ago]
|
85
|
-
|
86
|
-
|
48
|
+
ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { before_seconds_ago: options[:before_seconds_ago], timestamp: timestamp }) do |payload|
|
49
|
+
deleted_records_count = GoodJob::Job.finished(timestamp).delete_all
|
50
|
+
|
51
|
+
payload[:deleted_records_count] = deleted_records_count
|
52
|
+
end
|
87
53
|
end
|
88
54
|
|
89
|
-
|
55
|
+
no_commands do
|
56
|
+
def set_up_application!
|
57
|
+
require RAILS_ENVIRONMENT_RB
|
58
|
+
end
|
59
|
+
end
|
90
60
|
end
|
91
61
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GoodJob
|
2
|
+
class Configuration
|
3
|
+
attr_reader :options, :env
|
4
|
+
|
5
|
+
def initialize(options, env: ENV)
|
6
|
+
@options = options
|
7
|
+
@env = env
|
8
|
+
end
|
9
|
+
|
10
|
+
def execution_mode(default: :external)
|
11
|
+
if options[:execution_mode]
|
12
|
+
options[:execution_mode]
|
13
|
+
elsif env['GOOD_JOB_EXECUTION_MODE'].present?
|
14
|
+
env['GOOD_JOB_EXECUTION_MODE'].to_sym
|
15
|
+
else
|
16
|
+
default
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def rails_execution_mode
|
21
|
+
if execution_mode(default: nil)
|
22
|
+
execution_mode
|
23
|
+
elsif Rails.env.development?
|
24
|
+
:inline
|
25
|
+
elsif Rails.env.test?
|
26
|
+
:inline
|
27
|
+
else
|
28
|
+
:external
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def max_threads
|
33
|
+
(
|
34
|
+
options[:max_threads] ||
|
35
|
+
env['GOOD_JOB_MAX_THREADS'] ||
|
36
|
+
env['RAILS_MAX_THREADS'] ||
|
37
|
+
ActiveRecord::Base.connection_pool.size
|
38
|
+
).to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
def queue_string
|
42
|
+
options[:queues] ||
|
43
|
+
env['GOOD_JOB_QUEUES'] ||
|
44
|
+
'*'
|
45
|
+
end
|
46
|
+
|
47
|
+
def poll_interval
|
48
|
+
(
|
49
|
+
options[:poll_interval] ||
|
50
|
+
env['GOOD_JOB_POLL_INTERVAL'] ||
|
51
|
+
1
|
52
|
+
).to_i
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/good_job/job.rb
CHANGED
@@ -18,32 +18,41 @@ module GoodJob
|
|
18
18
|
end
|
19
19
|
end)
|
20
20
|
scope :only_scheduled, -> { where(arel_table['scheduled_at'].lteq(Time.current)).or(where(scheduled_at: nil)) }
|
21
|
-
scope :priority_ordered, -> { order(priority
|
21
|
+
scope :priority_ordered, -> { order('priority DESC NULLS LAST') }
|
22
22
|
scope :finished, ->(timestamp = nil) { timestamp ? where(arel_table['finished_at'].lteq(timestamp)) : where.not(finished_at: nil) }
|
23
|
+
scope :queue_string, (lambda do |string|
|
24
|
+
string = string.presence || '*'
|
23
25
|
|
24
|
-
|
26
|
+
if string.first == '-'
|
27
|
+
exclude_queues = true
|
28
|
+
string = string[1..-1]
|
29
|
+
end
|
30
|
+
|
31
|
+
queue_names_without_all = string.split(',').map(&:strip).reject { |q| q == '*' }
|
32
|
+
return if queue_names_without_all.size.zero?
|
33
|
+
|
34
|
+
if exclude_queues
|
35
|
+
where.not(queue_name: queue_names_without_all).or where(queue_name: nil)
|
36
|
+
else
|
37
|
+
where(queue_name: queue_names_without_all)
|
38
|
+
end
|
39
|
+
end)
|
40
|
+
|
41
|
+
def self.perform_with_advisory_lock
|
25
42
|
good_job = nil
|
26
43
|
result = nil
|
27
44
|
error = nil
|
28
45
|
|
29
|
-
unfinished.only_scheduled.limit(1).with_advisory_lock do |good_jobs|
|
46
|
+
unfinished.priority_ordered.only_scheduled.limit(1).with_advisory_lock do |good_jobs|
|
30
47
|
good_job = good_jobs.first
|
31
48
|
break unless good_job
|
32
49
|
|
33
|
-
result, error = good_job.perform
|
50
|
+
result, error = good_job.perform
|
34
51
|
end
|
35
52
|
|
36
53
|
[good_job, result, error] if good_job
|
37
54
|
end
|
38
55
|
|
39
|
-
def self.perform_with_advisory_lock_and_preserve_job_records
|
40
|
-
perform_with_advisory_lock(destroy_after: false)
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.perform_with_advisory_lock_and_destroy_job_records
|
44
|
-
perform_with_advisory_lock(destroy_after: true)
|
45
|
-
end
|
46
|
-
|
47
56
|
def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
|
48
57
|
good_job = nil
|
49
58
|
ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
|
@@ -64,40 +73,49 @@ module GoodJob
|
|
64
73
|
good_job
|
65
74
|
end
|
66
75
|
|
67
|
-
def perform(destroy_after:
|
76
|
+
def perform(destroy_after: !GoodJob.preserve_job_records, reperform_on_standard_error: GoodJob.reperform_jobs_on_standard_error)
|
68
77
|
raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
|
69
78
|
|
70
79
|
result = nil
|
80
|
+
rescued_error = nil
|
71
81
|
error = nil
|
72
82
|
|
73
83
|
ActiveSupport::Notifications.instrument("before_perform_job.good_job", { good_job: self })
|
74
84
|
self.performed_at = Time.current
|
75
85
|
save! unless destroy_after
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
87
|
+
params = serialized_params.merge(
|
88
|
+
"provider_job_id" => id
|
89
|
+
)
|
90
|
+
|
91
|
+
begin
|
92
|
+
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self }) do
|
82
93
|
result = ActiveJob::Base.execute(params)
|
83
|
-
rescue StandardError => e
|
84
|
-
error = e
|
85
94
|
end
|
95
|
+
rescue StandardError => e
|
96
|
+
rescued_error = e
|
86
97
|
end
|
87
98
|
|
88
|
-
if
|
99
|
+
if rescued_error
|
100
|
+
error = rescued_error
|
101
|
+
elsif result.is_a?(Exception)
|
89
102
|
error = result
|
90
103
|
result = nil
|
91
104
|
end
|
92
105
|
|
93
106
|
error_message = "#{error.class}: #{error.message}" if error
|
94
107
|
self.error = error_message
|
95
|
-
self.finished_at = Time.current
|
96
108
|
|
97
|
-
if
|
98
|
-
destroy!
|
99
|
-
else
|
109
|
+
if rescued_error && reperform_on_standard_error
|
100
110
|
save!
|
111
|
+
else
|
112
|
+
self.finished_at = Time.current
|
113
|
+
|
114
|
+
if destroy_after
|
115
|
+
destroy!
|
116
|
+
else
|
117
|
+
save!
|
118
|
+
end
|
101
119
|
end
|
102
120
|
|
103
121
|
[result, error]
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module GoodJob
|
2
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
|
+
def create(event)
|
4
|
+
good_job = event.payload[:good_job]
|
5
|
+
|
6
|
+
debug do
|
7
|
+
"GoodJob created job resource with id #{good_job.id}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def timer_task_finished(event)
|
12
|
+
exception = event.payload[:error]
|
13
|
+
return unless exception
|
14
|
+
|
15
|
+
error do
|
16
|
+
"GoodJob error: #{exception}\n #{exception.backtrace}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def job_finished(event)
|
21
|
+
exception = event.payload[:error]
|
22
|
+
return unless exception
|
23
|
+
|
24
|
+
error do
|
25
|
+
"GoodJob error: #{exception}\n #{exception.backtrace}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def scheduler_create_pools(event)
|
30
|
+
max_threads = event.payload[:max_threads]
|
31
|
+
poll_interval = event.payload[:poll_interval]
|
32
|
+
performer_name = event.payload[:performer_name]
|
33
|
+
process_id = event.payload[:process_id]
|
34
|
+
|
35
|
+
info_and_stdout(tags: [process_id]) do
|
36
|
+
"GoodJob started scheduler with queues=#{performer_name} max_threads=#{max_threads} poll_interval=#{poll_interval}."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def scheduler_shutdown_start(event)
|
41
|
+
process_id = event.payload[:process_id]
|
42
|
+
|
43
|
+
info_and_stdout(tags: [process_id]) do
|
44
|
+
"GoodJob shutting down scheduler..."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def scheduler_shutdown(event)
|
49
|
+
process_id = event.payload[:process_id]
|
50
|
+
|
51
|
+
info_and_stdout(tags: [process_id]) do
|
52
|
+
"GoodJob scheduler is shut down."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def scheduler_restart_pools(event)
|
57
|
+
process_id = event.payload[:process_id]
|
58
|
+
|
59
|
+
info_and_stdout(tags: [process_id]) do
|
60
|
+
"GoodJob scheduler has restarted."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def cleanup_preserved_jobs(event)
|
65
|
+
timestamp = event.payload[:timestamp]
|
66
|
+
deleted_records_count = event.payload[:deleted_records_count]
|
67
|
+
|
68
|
+
info_and_stdout do
|
69
|
+
"GoodJob deleted #{deleted_records_count} preserved #{'job'.pluralize(deleted_records_count)} finished before #{timestamp}."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def logger
|
76
|
+
GoodJob.logger
|
77
|
+
end
|
78
|
+
|
79
|
+
%w(info debug warn error fatal unknown).each do |level|
|
80
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
81
|
+
def #{level}(progname = nil, tags: [], &block)
|
82
|
+
return unless logger
|
83
|
+
|
84
|
+
if logger.respond_to?(:tagged)
|
85
|
+
tags.unshift "GoodJob" unless logger.formatter.current_tags.include?("GoodJob")
|
86
|
+
logger.tagged(*tags.compact) do
|
87
|
+
logger.#{level}(progname, &block)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
logger.#{level}(progname, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
METHOD
|
94
|
+
end
|
95
|
+
|
96
|
+
def info_and_stdout(progname = nil, tags: [], &block)
|
97
|
+
unless ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
|
98
|
+
tags_string = (['GoodJob'] + tags).map { |t| "[#{t}]" }.join(' ')
|
99
|
+
stdout_message = "#{tags_string} #{yield}"
|
100
|
+
$stdout.puts stdout_message
|
101
|
+
end
|
102
|
+
|
103
|
+
info(progname, tags: [], &block)
|
104
|
+
end
|
105
|
+
|
106
|
+
def thread_name
|
107
|
+
Thread.current.name || Thread.current.object_id
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|