loga 2.4.0 → 2.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +12 -0
- data/Appraisals +19 -7
- data/CHANGELOG.md +20 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/gemfiles/rails60.gemfile +11 -0
- data/gemfiles/sidekiq51.gemfile +1 -1
- data/gemfiles/sidekiq6.gemfile +11 -0
- data/gemfiles/sinatra14.gemfile +1 -1
- data/gemfiles/unit.gemfile +1 -1
- data/lib/loga/configuration.rb +1 -0
- data/lib/loga/formatters/gelf_formatter.rb +16 -1
- data/lib/loga/rack/logger.rb +31 -41
- data/lib/loga/railtie.rb +1 -1
- data/lib/loga/sidekiq.rb +13 -5
- data/lib/loga/{sidekiq → sidekiq5}/job_logger.rb +1 -3
- data/lib/loga/sidekiq6/job_logger.rb +50 -0
- data/lib/loga/version.rb +1 -1
- data/loga.gemspec +1 -1
- data/spec/fixtures/rails60.rb +80 -0
- data/spec/integration/sidekiq5_spec.rb +143 -0
- data/spec/integration/sidekiq6_spec.rb +164 -0
- data/spec/integration/sinatra_spec.rb +1 -1
- data/spec/loga/{sidekiq → sidekiq5}/job_logger_spec.rb +2 -1
- data/spec/loga/sidekiq6/job_logger_spec.rb +121 -0
- data/spec/loga/sidekiq_spec.rb +10 -3
- data/spec/spec_helper.rb +17 -8
- data/spec/unit/loga/formatters/gelf_formatter_spec.rb +43 -0
- data/spec/unit/loga/rack/logger_spec.rb +58 -37
- metadata +18 -9
- data/spec/integration/sidekiq_spec.rb +0 -147
data/lib/loga/version.rb
CHANGED
data/loga.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency 'rack'
|
23
23
|
|
24
24
|
spec.add_development_dependency 'appraisal', '~> 2.2.0'
|
25
|
-
spec.add_development_dependency 'bundler', '
|
25
|
+
spec.add_development_dependency 'bundler', '>= 1.6'
|
26
26
|
spec.add_development_dependency 'byebug'
|
27
27
|
spec.add_development_dependency 'guard', '~> 2.13'
|
28
28
|
spec.add_development_dependency 'guard-rspec', '~> 4.7.3'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'action_controller/railtie'
|
2
|
+
require 'action_mailer/railtie'
|
3
|
+
|
4
|
+
Bundler.require(*Rails.groups)
|
5
|
+
|
6
|
+
STREAM = StringIO.new unless defined?(STREAM)
|
7
|
+
|
8
|
+
class Dummy < Rails::Application
|
9
|
+
config.eager_load = true
|
10
|
+
config.filter_parameters += [:password]
|
11
|
+
config.secret_key_base = '2624599ca9ab3cf3823626240138a128118a87683bf03ab8f155844c33b3cd8cbbfa3ef5e29db6f5bd182f8bd4776209d9577cfb46ac51bfd232b00ab0136b24'
|
12
|
+
config.session_store :cookie_store, key: '_rails60_session'
|
13
|
+
|
14
|
+
config.log_tags = [:uuid, 'TEST_TAG']
|
15
|
+
config.loga = {
|
16
|
+
device: STREAM,
|
17
|
+
host: 'bird.example.com',
|
18
|
+
service_name: 'hello_world_app',
|
19
|
+
service_version: '1.0',
|
20
|
+
}
|
21
|
+
config.action_mailer.delivery_method = :test
|
22
|
+
end
|
23
|
+
|
24
|
+
class ApplicationController < ActionController::Base
|
25
|
+
include Rails.application.routes.url_helpers
|
26
|
+
protect_from_forgery with: :null_session
|
27
|
+
|
28
|
+
def ok
|
29
|
+
render plain: 'Hello Rails'
|
30
|
+
end
|
31
|
+
|
32
|
+
def error
|
33
|
+
nil.name
|
34
|
+
end
|
35
|
+
|
36
|
+
def show
|
37
|
+
render json: params
|
38
|
+
end
|
39
|
+
|
40
|
+
def create
|
41
|
+
render json: params
|
42
|
+
end
|
43
|
+
|
44
|
+
def new
|
45
|
+
redirect_to :ok
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
@id = params[:id]
|
50
|
+
render '/user'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class FakeMailer < ActionMailer::Base
|
55
|
+
default from: 'notifications@example.com'
|
56
|
+
|
57
|
+
def self.send_email
|
58
|
+
basic_mail.deliver_now
|
59
|
+
end
|
60
|
+
|
61
|
+
def basic_mail
|
62
|
+
mail(
|
63
|
+
to: 'user@example.com',
|
64
|
+
subject: 'Welcome to My Awesome Site',
|
65
|
+
body: 'Banana muffin',
|
66
|
+
content_type: 'text/html',
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Dummy.routes.append do
|
72
|
+
get 'ok' => 'application#ok'
|
73
|
+
get 'error' => 'application#error'
|
74
|
+
get 'show' => 'application#show'
|
75
|
+
post 'users' => 'application#create'
|
76
|
+
get 'new' => 'application#new'
|
77
|
+
put 'users/:id' => 'application#update'
|
78
|
+
end
|
79
|
+
|
80
|
+
Dummy.initialize!
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
require 'fakeredis'
|
4
|
+
|
5
|
+
dummy_redis_config = ConnectionPool.new(size: 5) { Redis.new }
|
6
|
+
|
7
|
+
Sidekiq.configure_client do |config|
|
8
|
+
config.redis = dummy_redis_config
|
9
|
+
end
|
10
|
+
|
11
|
+
Sidekiq.configure_server do |config|
|
12
|
+
config.redis = dummy_redis_config
|
13
|
+
end
|
14
|
+
|
15
|
+
class MySidekiqWorker
|
16
|
+
include Sidekiq::Worker
|
17
|
+
|
18
|
+
def perform(_name); end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'Sidekiq client logger' do
|
22
|
+
let(:mgr) do
|
23
|
+
Class.new do
|
24
|
+
attr_reader :latest_error, :mutex, :cond
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@mutex = ::Mutex.new
|
28
|
+
@cond = ::ConditionVariable.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def processor_died(_inst, err)
|
32
|
+
@latest_error = err
|
33
|
+
|
34
|
+
@mutex.synchronize { @cond.signal }
|
35
|
+
end
|
36
|
+
|
37
|
+
def processor_stopped(_inst)
|
38
|
+
@mutex.synchronize { @cond.signal }
|
39
|
+
end
|
40
|
+
|
41
|
+
def options
|
42
|
+
{
|
43
|
+
concurrency: 3,
|
44
|
+
queues: ['default'],
|
45
|
+
job_logger: Loga::Sidekiq5::JobLogger,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:target) { StringIO.new }
|
52
|
+
|
53
|
+
def read_json_log(line:)
|
54
|
+
target.rewind
|
55
|
+
JSON.parse(target.each_line.drop(line - 1).first)
|
56
|
+
end
|
57
|
+
|
58
|
+
before do
|
59
|
+
Redis.current.flushall
|
60
|
+
|
61
|
+
Loga.reset
|
62
|
+
|
63
|
+
Loga.configure(
|
64
|
+
service_name: 'hello_world_app',
|
65
|
+
service_version: '1.0',
|
66
|
+
device: target,
|
67
|
+
format: :gelf,
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'has the proper job logger' do
|
72
|
+
expect(Sidekiq.options[:job_logger]).to eq Loga::Sidekiq5::JobLogger
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'has the proper logger for Sidekiq.logger' do
|
76
|
+
expect(Sidekiq.logger).to eq Loga.logger
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'pushes a new element in the default queue' do
|
80
|
+
MySidekiqWorker.perform_async('Bob')
|
81
|
+
|
82
|
+
last_element = JSON.parse(Redis.current.lpop('queue:default'))
|
83
|
+
|
84
|
+
aggregate_failures do
|
85
|
+
expect(last_element['class']).to eq 'MySidekiqWorker'
|
86
|
+
expect(last_element['args']).to eq ['Bob']
|
87
|
+
expect(last_element['retry']).to eq true
|
88
|
+
expect(last_element['queue']).to eq 'default'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'has the proper logger Sidekiq::Logging.logger' do
|
93
|
+
expect(Sidekiq::Logging.logger).to eq Loga.logger
|
94
|
+
end
|
95
|
+
|
96
|
+
# https://github.com/mperham/sidekiq/blob/97363210b47a4f8a1d8c1233aaa059d6643f5040/test/test_actors.rb#L57-L79
|
97
|
+
|
98
|
+
it 'logs the job processing event' do
|
99
|
+
MySidekiqWorker.perform_async('Bob')
|
100
|
+
|
101
|
+
require 'sidekiq/processor'
|
102
|
+
|
103
|
+
Sidekiq::Processor.new(mgr.new).start
|
104
|
+
sleep 0.5
|
105
|
+
|
106
|
+
json_line = read_json_log(line: 1)
|
107
|
+
|
108
|
+
aggregate_failures do
|
109
|
+
expect(json_line).to include(
|
110
|
+
'_queue'=> 'default',
|
111
|
+
'_retry'=> true,
|
112
|
+
'_params'=> ['Bob'],
|
113
|
+
'_class'=> 'MySidekiqWorker',
|
114
|
+
'_type'=> 'sidekiq',
|
115
|
+
'_service.name'=> 'hello_world_app',
|
116
|
+
'_service.version'=> '1.0',
|
117
|
+
'_tags'=> '',
|
118
|
+
'level'=> 6,
|
119
|
+
'version'=> '1.1',
|
120
|
+
)
|
121
|
+
|
122
|
+
%w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
|
123
|
+
expect(json_line).to have_key(key)
|
124
|
+
end
|
125
|
+
|
126
|
+
expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
|
127
|
+
end
|
128
|
+
|
129
|
+
# This was a bug - the duration was constantly incresing based on when
|
130
|
+
# the logger was created. https://github.com/FundingCircle/loga/pull/117
|
131
|
+
#
|
132
|
+
# Test that after sleeping for few seconds the duration is still under 500ms
|
133
|
+
sleep 1
|
134
|
+
|
135
|
+
MySidekiqWorker.perform_async('Bob')
|
136
|
+
|
137
|
+
sleep 1
|
138
|
+
|
139
|
+
json_line = read_json_log(line: 2)
|
140
|
+
|
141
|
+
expect(json_line['_duration']).to be < 500
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
require 'fakeredis'
|
4
|
+
|
5
|
+
dummy_redis_config = ConnectionPool.new(size: 5) { Redis.new }
|
6
|
+
|
7
|
+
Sidekiq.configure_client do |config|
|
8
|
+
config.redis = dummy_redis_config
|
9
|
+
end
|
10
|
+
|
11
|
+
Sidekiq.configure_server do |config|
|
12
|
+
config.redis = dummy_redis_config
|
13
|
+
end
|
14
|
+
|
15
|
+
class MySidekiqWorker
|
16
|
+
include Sidekiq::Worker
|
17
|
+
|
18
|
+
def perform(_name)
|
19
|
+
logger.info('Hello from MySidekiqWorker')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'Sidekiq client logger' do
|
24
|
+
let(:mgr) do
|
25
|
+
# https://github.com/mperham/sidekiq/blob/v6.1.2/test/test_actors.rb#L58-L82
|
26
|
+
Class.new do
|
27
|
+
attr_reader :latest_error, :mutex, :cond
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@mutex = ::Mutex.new
|
31
|
+
@cond = ::ConditionVariable.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def processor_died(_inst, err)
|
35
|
+
@latest_error = err
|
36
|
+
|
37
|
+
@mutex.synchronize { @cond.signal }
|
38
|
+
end
|
39
|
+
|
40
|
+
def processor_stopped(_inst)
|
41
|
+
@mutex.synchronize { @cond.signal }
|
42
|
+
end
|
43
|
+
|
44
|
+
def options
|
45
|
+
{
|
46
|
+
concurrency: 3,
|
47
|
+
queues: ['default'],
|
48
|
+
job_logger: Loga::Sidekiq6::JobLogger,
|
49
|
+
}.tap { |opts| opts[:fetch] = ::Sidekiq::BasicFetch.new(opts) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:target) { StringIO.new }
|
55
|
+
|
56
|
+
def read_json_log(line:)
|
57
|
+
target.rewind
|
58
|
+
JSON.parse(target.each_line.drop(line - 1).first)
|
59
|
+
end
|
60
|
+
|
61
|
+
before do
|
62
|
+
Redis.current.flushall
|
63
|
+
|
64
|
+
Loga.reset
|
65
|
+
|
66
|
+
Loga.configure(
|
67
|
+
service_name: 'hello_world_app',
|
68
|
+
service_version: '1.0',
|
69
|
+
device: target,
|
70
|
+
format: :gelf,
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'has the proper job logger' do
|
75
|
+
expect(Sidekiq.options[:job_logger]).to eq Loga::Sidekiq6::JobLogger
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'has the proper logger for Sidekiq.logger' do
|
79
|
+
expect(Sidekiq.logger).to eq Loga.logger
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'pushes a new element in the default queue' do
|
83
|
+
MySidekiqWorker.perform_async('Bob')
|
84
|
+
|
85
|
+
last_element = JSON.parse(Redis.current.lpop('queue:default'))
|
86
|
+
|
87
|
+
aggregate_failures do
|
88
|
+
expect(last_element['class']).to eq 'MySidekiqWorker'
|
89
|
+
expect(last_element['args']).to eq ['Bob']
|
90
|
+
expect(last_element['retry']).to eq true
|
91
|
+
expect(last_element['queue']).to eq 'default'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_log_from_worker(json_line)
|
96
|
+
aggregate_failures do
|
97
|
+
expect(json_line).to include(
|
98
|
+
'_class' => 'MySidekiqWorker',
|
99
|
+
'_service.name' => 'hello_world_app',
|
100
|
+
'_service.version' => '1.0',
|
101
|
+
'_tags' => '',
|
102
|
+
'level' => 6,
|
103
|
+
'version' => '1.1',
|
104
|
+
'short_message' => 'Hello from MySidekiqWorker',
|
105
|
+
)
|
106
|
+
|
107
|
+
%w[_jid timestamp host].each do |key|
|
108
|
+
expect(json_line).to have_key(key)
|
109
|
+
end
|
110
|
+
|
111
|
+
expect(json_line).not_to include('_duration')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_job_end_log(json_line) # rubocop:disable Metrics/MethodLength
|
116
|
+
aggregate_failures do
|
117
|
+
expect(json_line).to include(
|
118
|
+
'_queue' => 'default',
|
119
|
+
'_retry' => true,
|
120
|
+
'_params' => ['Bob'],
|
121
|
+
'_class' => 'MySidekiqWorker',
|
122
|
+
'_type' => 'sidekiq',
|
123
|
+
'_service.name' => 'hello_world_app',
|
124
|
+
'_service.version' => '1.0',
|
125
|
+
'_tags' => '',
|
126
|
+
'level' => 6,
|
127
|
+
'version' => '1.1',
|
128
|
+
)
|
129
|
+
|
130
|
+
%w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
|
131
|
+
expect(json_line).to have_key(key)
|
132
|
+
end
|
133
|
+
|
134
|
+
expect(json_line['_duration']).to be < 500
|
135
|
+
expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'logs the job processing event' do
|
140
|
+
MySidekiqWorker.perform_async('Bob')
|
141
|
+
|
142
|
+
require 'sidekiq/processor'
|
143
|
+
|
144
|
+
sidekiq_manager = mgr.new
|
145
|
+
Sidekiq::Processor.new(sidekiq_manager, sidekiq_manager.options).start
|
146
|
+
sleep 0.5
|
147
|
+
|
148
|
+
test_log_from_worker(read_json_log(line: 1))
|
149
|
+
test_job_end_log(read_json_log(line: 2))
|
150
|
+
|
151
|
+
# This was a bug - the duration was constantly incresing based on when
|
152
|
+
# the logger was created. https://github.com/FundingCircle/loga/pull/117
|
153
|
+
#
|
154
|
+
# Test that after sleeping for few seconds the duration is still under 500ms
|
155
|
+
sleep 1
|
156
|
+
|
157
|
+
MySidekiqWorker.perform_async('Bob')
|
158
|
+
|
159
|
+
sleep 1
|
160
|
+
|
161
|
+
test_log_from_worker(read_json_log(line: 3))
|
162
|
+
test_job_end_log(read_json_log(line: 4))
|
163
|
+
end
|
164
|
+
end
|
@@ -68,7 +68,6 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
68
68
|
end
|
69
69
|
let(:data) do
|
70
70
|
{
|
71
|
-
'status' => 200,
|
72
71
|
'method' => 'GET',
|
73
72
|
'path' => '/ok',
|
74
73
|
'params' => { 'username'=>'yoshi' },
|
@@ -76,6 +75,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
76
75
|
'request_ip' => '127.0.0.1',
|
77
76
|
'user_agent' => nil,
|
78
77
|
'duration' => 0,
|
78
|
+
'status' => 200,
|
79
79
|
}
|
80
80
|
end
|
81
81
|
let(:data_as_text) { "data=#{{ request: data }.inspect}" }
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'loga/sidekiq6/job_logger'
|
3
|
+
|
4
|
+
RSpec.describe Loga::Sidekiq6::JobLogger do
|
5
|
+
subject(:job_logger) { described_class.new }
|
6
|
+
|
7
|
+
let(:target) { StringIO.new }
|
8
|
+
|
9
|
+
let(:json_line) do
|
10
|
+
target.rewind
|
11
|
+
JSON.parse(target.read.split("\n").last)
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
Loga.reset
|
16
|
+
|
17
|
+
Loga.configure(
|
18
|
+
service_name: 'hello_world_app',
|
19
|
+
service_version: '1.0',
|
20
|
+
device: target,
|
21
|
+
format: :gelf,
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# https://github.com/mperham/sidekiq/blob/v6.1.2/lib/sidekiq/job_logger.rb
|
26
|
+
it 'inherits from ::Sidekiq::JobLogger' do
|
27
|
+
expect(subject).to be_a(::Sidekiq::JobLogger)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#call' do
|
31
|
+
context 'when the job passess successfully' do
|
32
|
+
let(:item_data) do
|
33
|
+
{
|
34
|
+
'class' => 'HardWorker',
|
35
|
+
'args' => ['asd'],
|
36
|
+
'retry' => true,
|
37
|
+
'queue' => 'default',
|
38
|
+
'jid' => '591f6f66ee0d218fb451dfb6',
|
39
|
+
'created_at' => 1_528_799_582.904939,
|
40
|
+
'enqueued_at' => 1_528_799_582.9049861,
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'has the the required attributes on call' do
|
45
|
+
job_logger.call(item_data, 'queue') do
|
46
|
+
# something
|
47
|
+
end
|
48
|
+
|
49
|
+
expected_body = {
|
50
|
+
'version' => '1.1',
|
51
|
+
'level' => 6,
|
52
|
+
'_type' => 'sidekiq',
|
53
|
+
'_created_at' => 1_528_799_582.904939,
|
54
|
+
'_enqueued_at' => 1_528_799_582.9049861,
|
55
|
+
'_jid' => '591f6f66ee0d218fb451dfb6',
|
56
|
+
'_retry' => true,
|
57
|
+
'_queue' => 'default',
|
58
|
+
'_service.name' => 'hello_world_app',
|
59
|
+
'_class' => 'HardWorker',
|
60
|
+
'_service.version' => '1.0',
|
61
|
+
'_tags' => '',
|
62
|
+
'_params' => ['asd'],
|
63
|
+
}
|
64
|
+
|
65
|
+
aggregate_failures do
|
66
|
+
expect(json_line).to include(expected_body)
|
67
|
+
expect(json_line['timestamp']).to be_a(Float)
|
68
|
+
expect(json_line['host']).to be_a(String)
|
69
|
+
expect(json_line['short_message']).to match(/HardWorker with jid:*/)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the job fails' do
|
75
|
+
let(:item_data) do
|
76
|
+
{
|
77
|
+
'class' => 'HardWorker',
|
78
|
+
'args' => ['asd'],
|
79
|
+
'retry' => true,
|
80
|
+
'queue' => 'default',
|
81
|
+
'jid' => '591f6f66ee0d218fb451dfb6',
|
82
|
+
'created_at' => 1_528_799_582.904939,
|
83
|
+
'enqueued_at' => 1_528_799_582.9049861,
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'has the the required attributes on call' do
|
88
|
+
failed_job = lambda do
|
89
|
+
job_logger.call(item_data, 'queue') do
|
90
|
+
raise StandardError
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
expected_body = {
|
95
|
+
'version' => '1.1',
|
96
|
+
'level' => 4,
|
97
|
+
'_type' => 'sidekiq',
|
98
|
+
'_created_at' => 1_528_799_582.904939,
|
99
|
+
'_enqueued_at' => 1_528_799_582.9049861,
|
100
|
+
'_jid' => '591f6f66ee0d218fb451dfb6',
|
101
|
+
'_retry' => true,
|
102
|
+
'_queue' => 'default',
|
103
|
+
'_service.name' => 'hello_world_app',
|
104
|
+
'_class' => 'HardWorker',
|
105
|
+
'_service.version' => '1.0',
|
106
|
+
'_tags' => '',
|
107
|
+
'_params' => ['asd'],
|
108
|
+
'_exception' => 'StandardError',
|
109
|
+
}
|
110
|
+
|
111
|
+
aggregate_failures do
|
112
|
+
expect(&failed_job).to raise_error(StandardError)
|
113
|
+
expect(json_line).to include(expected_body)
|
114
|
+
expect(json_line['timestamp']).to be_a(Float)
|
115
|
+
expect(json_line['host']).to be_a(String)
|
116
|
+
expect(json_line['short_message']).to match(/HardWorker with jid:*/)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|