loga 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'timecop'
5
+ require 'fakeredis'
6
+
7
+ dummy_redis_config = ConnectionPool.new(size: 5) { Redis.new }
8
+
9
+ Sidekiq.configure_client do |config|
10
+ config.redis = dummy_redis_config
11
+ end
12
+
13
+ Sidekiq.configure_server do |config|
14
+ config.redis = dummy_redis_config
15
+ end
16
+
17
+ class MySidekiqWorker
18
+ include Sidekiq::Job
19
+
20
+ def perform(_name)
21
+ logger.info('Hello from MySidekiqWorker')
22
+ end
23
+ end
24
+
25
+ module Sidekiq
26
+ def self.reset!
27
+ @config = DEFAULTS.dup
28
+ end
29
+ end
30
+
31
+ describe 'Sidekiq client logger' do
32
+ let(:target) { StringIO.new }
33
+ let(:config) do
34
+ c = Sidekiq
35
+
36
+ c[:queues] = %w[default]
37
+ c[:fetch] = Sidekiq::BasicFetch.new(c)
38
+ c[:error_handlers] << Sidekiq.method(:default_error_handler)
39
+
40
+ c
41
+ end
42
+
43
+ def read_json_log(line:)
44
+ target.rewind
45
+ JSON.parse(target.each_line.drop(line - 1).first)
46
+ end
47
+
48
+ before do
49
+ Sidekiq.reset!
50
+ Sidekiq.redis(&:flushdb)
51
+
52
+ Loga.reset
53
+
54
+ Loga.configure(
55
+ service_name: 'hello_world_app',
56
+ service_version: '1.0',
57
+ device: target,
58
+ format: :gelf,
59
+ )
60
+ end
61
+
62
+ it 'has the proper job logger' do
63
+ expect(Sidekiq.options[:job_logger]).to eq Loga::Sidekiq6::JobLogger
64
+ end
65
+
66
+ it 'has the proper logger for Sidekiq.logger' do
67
+ expect(Sidekiq.logger).to eq Loga.logger
68
+ end
69
+
70
+ it 'pushes a new element in the default queue' do
71
+ MySidekiqWorker.perform_async('Bob')
72
+
73
+ last_element = JSON.parse(Redis.current.lpop('queue:default'))
74
+
75
+ aggregate_failures do
76
+ expect(last_element['class']).to eq 'MySidekiqWorker'
77
+ expect(last_element['args']).to eq ['Bob']
78
+ expect(last_element['retry']).to eq true
79
+ expect(last_element['queue']).to eq 'default'
80
+ end
81
+ end
82
+
83
+ def test_log_from_worker(json_line)
84
+ aggregate_failures do
85
+ expect(json_line).to include(
86
+ '_class' => 'MySidekiqWorker',
87
+ '_service.name' => 'hello_world_app',
88
+ '_service.version' => '1.0',
89
+ '_tags' => '',
90
+ 'level' => 6,
91
+ 'version' => '1.1',
92
+ 'short_message' => 'Hello from MySidekiqWorker',
93
+ )
94
+
95
+ %w[_jid timestamp host].each do |key|
96
+ expect(json_line).to have_key(key)
97
+ end
98
+
99
+ expect(json_line).not_to include('_duration')
100
+ end
101
+ end
102
+
103
+ def test_job_end_log(json_line) # rubocop:disable Metrics/MethodLength
104
+ aggregate_failures do
105
+ expect(json_line).to include(
106
+ '_queue' => 'default',
107
+ '_retry' => true,
108
+ '_params' => ['Bob'],
109
+ '_class' => 'MySidekiqWorker',
110
+ '_type' => 'sidekiq',
111
+ '_service.name' => 'hello_world_app',
112
+ '_service.version' => '1.0',
113
+ '_tags' => '',
114
+ 'level' => 6,
115
+ 'version' => '1.1',
116
+ )
117
+
118
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
119
+ expect(json_line).to have_key(key)
120
+ end
121
+
122
+ expect(json_line['_duration']).to be < 500
123
+ expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
124
+ end
125
+ end
126
+
127
+ context 'with processor' do
128
+ let(:mutex) { Mutex.new }
129
+ let(:cond) { ConditionVariable.new }
130
+
131
+ def result(_processor, _exception)
132
+ mutex.synchronize { cond.signal }
133
+ end
134
+
135
+ def await(timeout: 0.5)
136
+ mutex.synchronize do
137
+ yield
138
+ cond.wait(mutex, timeout)
139
+ end
140
+ end
141
+
142
+ it 'logs the job processing event' do
143
+ MySidekiqWorker.perform_async('Bob')
144
+
145
+ require 'sidekiq/processor'
146
+
147
+ p = Sidekiq::Processor.new(config) { |pr, ex| result(pr, ex) }
148
+
149
+ await { p.start }
150
+
151
+ test_log_from_worker(read_json_log(line: 1))
152
+ test_job_end_log(read_json_log(line: 2))
153
+
154
+ # This was a bug - the duration was constantly incresing based on when
155
+ # the logger was created. https://github.com/FundingCircle/loga/pull/117
156
+ #
157
+ # Test that after sleeping for few seconds the duration is still under 500ms
158
+ sleep 1
159
+
160
+ await { MySidekiqWorker.perform_async('Bob') }
161
+
162
+ test_log_from_worker(read_json_log(line: 3))
163
+ test_job_end_log(read_json_log(line: 4))
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Sidekiq client logger' do
6
+ let(:target) { StringIO.new }
7
+ let(:config) { Sidekiq.instance_variable_get :@config }
8
+
9
+ before do
10
+ Sidekiq.configure_server do |config|
11
+ config.redis = { pool_name: :default }
12
+ end
13
+
14
+ Loga.reset
15
+
16
+ Loga.configure(
17
+ service_name: 'hello_world_app',
18
+ service_version: '1.0',
19
+ device: target,
20
+ format: :gelf,
21
+ )
22
+ end
23
+
24
+ it 'has the proper job logger' do
25
+ expect(config[:job_logger]).to eq Loga::Sidekiq7::JobLogger
26
+ end
27
+
28
+ it 'has the proper logger for Sidekiq.logger' do
29
+ expect(Sidekiq.logger).to eq Loga.logger
30
+ end
31
+
32
+ context 'with processor' do
33
+ require 'sidekiq/processor'
34
+
35
+ let(:mutex) { Mutex.new }
36
+ let(:cond) { ConditionVariable.new }
37
+ let(:processor) do
38
+ Sidekiq::Processor.new(config.default_capsule) { |pr, ex| result(pr, ex) }
39
+ end
40
+
41
+ before do
42
+ @exception = nil
43
+ end
44
+
45
+ context 'with a successful job' do
46
+ before do
47
+ MySidekiqWorker.perform_async('Bob')
48
+
49
+ await { processor.start }
50
+
51
+ processor.terminate(true)
52
+ end
53
+
54
+ it 'logs the job processing event' do
55
+ test_log_from_worker(read_json_log(line: -2))
56
+ end
57
+
58
+ it 'logs the "done" event' do
59
+ test_job_end_log(read_json_log(line: -1))
60
+ end
61
+ end
62
+
63
+ context 'with an error' do
64
+ before do
65
+ MySidekiqWorker.perform_async('Boom')
66
+
67
+ await { processor.start }
68
+
69
+ processor.terminate(true)
70
+ end
71
+
72
+ it 'logs the "error" event' do
73
+ test_job_fail_log(read_json_log(line: 0))
74
+ end
75
+
76
+ it 're-throws the error' do
77
+ # rubocop:disable RSpec/InstanceVariable
78
+ expect(@exception.message).to eq('Boom')
79
+ # rubocop:enable RSpec/InstanceVariable
80
+ end
81
+ end
82
+
83
+ def result(_processor, exception)
84
+ @exception = exception
85
+ mutex.synchronize { cond.signal }
86
+ end
87
+
88
+ def await(timeout: 0.1)
89
+ mutex.synchronize do
90
+ yield
91
+ cond.wait(mutex, timeout)
92
+ end
93
+ end
94
+
95
+ def common_log_fields
96
+ {
97
+ '_class' => 'MySidekiqWorker',
98
+ '_service.name' => 'hello_world_app',
99
+ '_service.version' => '1.0',
100
+ '_tags' => '',
101
+ 'version' => '1.1',
102
+ }.freeze
103
+ end
104
+
105
+ def job_logger_common_fields
106
+ common_log_fields.merge(
107
+ '_queue' => 'default',
108
+ '_retry' => true,
109
+ '_type' => 'sidekiq',
110
+ )
111
+ end
112
+
113
+ def test_log_from_worker(json_line)
114
+ aggregate_failures do
115
+ expect(json_line).to include(
116
+ common_log_fields.merge(
117
+ 'level' => 6,
118
+ 'short_message' => 'Hello from MySidekiqWorker',
119
+ ),
120
+ )
121
+
122
+ %w[_jid timestamp host].each do |key|
123
+ expect(json_line).to have_key(key)
124
+ end
125
+
126
+ expect(json_line).not_to include('_duration')
127
+ end
128
+ end
129
+
130
+ def test_job_end_log(json_line)
131
+ aggregate_failures do
132
+ expect(json_line).to include(
133
+ job_logger_common_fields.merge(
134
+ '_params' => ['Bob'],
135
+ 'level' => 6,
136
+ ),
137
+ )
138
+
139
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
140
+ expect(json_line).to have_key(key)
141
+ end
142
+
143
+ expect(json_line['_duration']).to be < 500
144
+ expect(json_line['short_message'])
145
+ .to match(/MySidekiqWorker with jid: '\w+' done/)
146
+ end
147
+ end
148
+
149
+ def test_job_fail_log(json_line)
150
+ aggregate_failures do
151
+ expect(json_line).to include(
152
+ job_logger_common_fields.merge(
153
+ '_params' => ['Boom'],
154
+ 'level' => 4,
155
+ ),
156
+ )
157
+
158
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
159
+ expect(json_line).to have_key(key)
160
+ end
161
+
162
+ expect(json_line['_duration']).to be < 500
163
+ expect(json_line['short_message'])
164
+ .to match(/MySidekiqWorker with jid: '\w+' fail/)
165
+ end
166
+ end
167
+ end
168
+
169
+ def dump_log
170
+ offset = target.pos
171
+
172
+ target.rewind
173
+ target.each_line { puts _1 }
174
+
175
+ target.pos = offset
176
+ end
177
+
178
+ def read_json_log(line:)
179
+ target.rewind
180
+
181
+ JSON.parse(target.readlines[line])
182
+ end
183
+ end
184
+
185
+ class MySidekiqWorker
186
+ include Sidekiq::Job
187
+
188
+ def perform(name)
189
+ raise name if name == 'Boom'
190
+
191
+ logger.info('Hello from MySidekiqWorker')
192
+ end
193
+ end
@@ -64,6 +64,7 @@ RSpec.describe Loga::Sidekiq6::JobLogger do
64
64
 
65
65
  aggregate_failures do
66
66
  expect(json_line).to include(expected_body)
67
+ expect(json_line['_duration']).to be_a(Float)
67
68
  expect(json_line['timestamp']).to be_a(Float)
68
69
  expect(json_line['host']).to be_a(String)
69
70
  expect(json_line['short_message']).to match(/HardWorker with jid:*/)
@@ -110,6 +111,7 @@ RSpec.describe Loga::Sidekiq6::JobLogger do
110
111
 
111
112
  aggregate_failures do
112
113
  expect(&failed_job).to raise_error(StandardError)
114
+ expect(json_line['_duration']).to be_a(Float)
113
115
  expect(json_line).to include(expected_body)
114
116
  expect(json_line['timestamp']).to be_a(Float)
115
117
  expect(json_line['host']).to be_a(String)
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'loga/sidekiq7/job_logger'
3
+
4
+ RSpec.describe Loga::Sidekiq7::JobLogger do
5
+ subject(:job_logger) { described_class.new(logger) }
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
+ let(:logger) { Loga.logger }
15
+
16
+ before do
17
+ Loga.reset
18
+
19
+ Loga.configure(
20
+ service_name: 'hello_world_app',
21
+ service_version: '1.0',
22
+ device: target,
23
+ format: :gelf,
24
+ )
25
+ end
26
+
27
+ # https://github.com/mperham/sidekiq/blob/v6.1.2/lib/sidekiq/job_logger.rb
28
+ it 'inherits from ::Sidekiq::JobLogger' do
29
+ expect(subject).to be_a(::Sidekiq::JobLogger)
30
+ end
31
+
32
+ describe '#call' do
33
+ context 'when the job passess successfully' do
34
+ let(:item_data) do
35
+ {
36
+ 'class' => 'HardWorker',
37
+ 'args' => ['asd'],
38
+ 'retry' => true,
39
+ 'queue' => 'default',
40
+ 'jid' => '591f6f66ee0d218fb451dfb6',
41
+ 'created_at' => 1_528_799_582.904939,
42
+ 'enqueued_at' => 1_528_799_582.9049861,
43
+ }
44
+ end
45
+
46
+ it 'has the the required attributes on call' do
47
+ job_logger.call(item_data, 'queue') do
48
+ # something
49
+ end
50
+
51
+ expected_body = {
52
+ 'version' => '1.1',
53
+ 'level' => 6,
54
+ '_type' => 'sidekiq',
55
+ '_created_at' => 1_528_799_582.904939,
56
+ '_enqueued_at' => 1_528_799_582.9049861,
57
+ '_jid' => '591f6f66ee0d218fb451dfb6',
58
+ '_retry' => true,
59
+ '_queue' => 'default',
60
+ '_service.name' => 'hello_world_app',
61
+ '_class' => 'HardWorker',
62
+ '_service.version' => '1.0',
63
+ '_tags' => '',
64
+ '_params' => ['asd'],
65
+ }
66
+
67
+ aggregate_failures do
68
+ expect(json_line).to include(expected_body)
69
+ expect(json_line['_duration']).to be_a(Float)
70
+ expect(json_line['timestamp']).to be_a(Float)
71
+ expect(json_line['host']).to be_a(String)
72
+ expect(json_line['short_message']).to match(/HardWorker with jid:*/)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'when the job fails' do
78
+ let(:item_data) do
79
+ {
80
+ 'class' => 'HardWorker',
81
+ 'args' => ['asd'],
82
+ 'retry' => true,
83
+ 'queue' => 'default',
84
+ 'jid' => '591f6f66ee0d218fb451dfb6',
85
+ 'created_at' => 1_528_799_582.904939,
86
+ 'enqueued_at' => 1_528_799_582.9049861,
87
+ }
88
+ end
89
+
90
+ it 'has the the required attributes on call' do
91
+ failed_job = lambda do
92
+ job_logger.call(item_data, 'queue') do
93
+ raise StandardError
94
+ end
95
+ end
96
+
97
+ expected_body = {
98
+ 'version' => '1.1',
99
+ 'level' => 4,
100
+ '_type' => 'sidekiq',
101
+ '_created_at' => 1_528_799_582.904939,
102
+ '_enqueued_at' => 1_528_799_582.9049861,
103
+ '_jid' => '591f6f66ee0d218fb451dfb6',
104
+ '_retry' => true,
105
+ '_queue' => 'default',
106
+ '_service.name' => 'hello_world_app',
107
+ '_class' => 'HardWorker',
108
+ '_service.version' => '1.0',
109
+ '_tags' => '',
110
+ '_params' => ['asd'],
111
+ '_exception' => 'StandardError',
112
+ }
113
+
114
+ aggregate_failures do
115
+ expect(&failed_job).to raise_error(StandardError)
116
+ expect(json_line['_duration']).to be_a(Float)
117
+ expect(json_line).to include(expected_body)
118
+ expect(json_line['timestamp']).to be_a(Float)
119
+ expect(json_line['host']).to be_a(String)
120
+ expect(json_line['short_message']).to match(/HardWorker with jid:*/)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
data/spec/spec_helper.rb CHANGED
@@ -30,21 +30,40 @@ when /unit/
30
30
  rspec_pattern = 'unit/**/*_spec.rb'
31
31
  require 'loga'
32
32
  when /sidekiq(?<version>\d+)/
33
- case $LAST_MATCH_INFO['version']
33
+ sidekiq_version = $LAST_MATCH_INFO['version']
34
+ case sidekiq_version
34
35
  when '51'
35
36
  rspec_pattern = [
36
37
  'spec/integration/sidekiq5_spec.rb',
37
38
  'spec/loga/sidekiq5/**/*_spec.rb',
38
39
  'spec/loga/sidekiq_spec.rb',
39
40
  ].join(',')
40
- when '6'
41
+ when '60'
41
42
  rspec_pattern = [
42
- 'spec/integration/sidekiq6_spec.rb',
43
+ 'spec/integration/sidekiq60_spec.rb',
44
+ 'spec/loga/sidekiq5/**/*_spec.rb',
45
+ 'spec/loga/sidekiq_spec.rb',
46
+ ].join(',')
47
+ when '61', '62', '63', '64'
48
+ rspec_pattern = [
49
+ 'spec/integration/sidekiq61_spec.rb',
43
50
  'spec/loga/sidekiq6/**/*_spec.rb',
44
51
  'spec/loga/sidekiq_spec.rb',
45
52
  ].join(',')
53
+ when '65'
54
+ rspec_pattern = [
55
+ 'spec/integration/sidekiq65_spec.rb',
56
+ 'spec/loga/sidekiq6/**/*_spec.rb',
57
+ 'spec/loga/sidekiq_spec.rb',
58
+ ].join(',')
59
+ when '7', '70', '71'
60
+ rspec_pattern = [
61
+ 'spec/integration/sidekiq7_spec.rb',
62
+ 'spec/loga/sidekiq7/**/*_spec.rb',
63
+ 'spec/loga/sidekiq_spec.rb',
64
+ ].join(',')
46
65
  else
47
- raise 'FIXME: Unknown sidekiq - update this file.'
66
+ raise "FIXME: Unknown sidekiq #{sidekiq_version} - update this file."
48
67
  end
49
68
 
50
69
  require 'sidekiq'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loga
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Funding Circle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-29 00:00:00.000000000 Z
11
+ date: 2023-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -254,8 +254,6 @@ files:
254
254
  - LICENSE.txt
255
255
  - README.md
256
256
  - Rakefile
257
- - gemfiles/rails32.gemfile
258
- - gemfiles/rails40.gemfile
259
257
  - gemfiles/rails42.gemfile
260
258
  - gemfiles/rails50.gemfile
261
259
  - gemfiles/rails52.gemfile
@@ -263,7 +261,15 @@ files:
263
261
  - gemfiles/rails61.gemfile
264
262
  - gemfiles/rails70.gemfile
265
263
  - gemfiles/sidekiq51.gemfile
266
- - gemfiles/sidekiq6.gemfile
264
+ - gemfiles/sidekiq60.gemfile
265
+ - gemfiles/sidekiq61.gemfile
266
+ - gemfiles/sidekiq62.gemfile
267
+ - gemfiles/sidekiq63.gemfile
268
+ - gemfiles/sidekiq64.gemfile
269
+ - gemfiles/sidekiq65.gemfile
270
+ - gemfiles/sidekiq7.gemfile
271
+ - gemfiles/sidekiq70.gemfile
272
+ - gemfiles/sidekiq71.gemfile
267
273
  - gemfiles/sinatra14.gemfile
268
274
  - gemfiles/unit.gemfile
269
275
  - lib/loga.rb
@@ -285,13 +291,12 @@ files:
285
291
  - lib/loga/sidekiq.rb
286
292
  - lib/loga/sidekiq5/job_logger.rb
287
293
  - lib/loga/sidekiq6/job_logger.rb
294
+ - lib/loga/sidekiq7/job_logger.rb
288
295
  - lib/loga/tagged_logging.rb
289
296
  - lib/loga/utilities.rb
290
297
  - lib/loga/version.rb
291
298
  - loga.gemspec
292
299
  - spec/fixtures/README.md
293
- - spec/fixtures/rails32.rb
294
- - spec/fixtures/rails40.rb
295
300
  - spec/fixtures/rails42.rb
296
301
  - spec/fixtures/rails50.rb
297
302
  - spec/fixtures/rails52.rb
@@ -303,10 +308,14 @@ files:
303
308
  - spec/integration/rails/railtie_spec.rb
304
309
  - spec/integration/rails/request_spec.rb
305
310
  - spec/integration/sidekiq5_spec.rb
306
- - spec/integration/sidekiq6_spec.rb
311
+ - spec/integration/sidekiq60_spec.rb
312
+ - spec/integration/sidekiq61_spec.rb
313
+ - spec/integration/sidekiq65_spec.rb
314
+ - spec/integration/sidekiq7_spec.rb
307
315
  - spec/integration/sinatra_spec.rb
308
316
  - spec/loga/sidekiq5/job_logger_spec.rb
309
317
  - spec/loga/sidekiq6/job_logger_spec.rb
318
+ - spec/loga/sidekiq7/job_logger_spec.rb
310
319
  - spec/loga/sidekiq_spec.rb
311
320
  - spec/spec_helper.rb
312
321
  - spec/support/gethostname_shared.rb
@@ -349,8 +358,6 @@ specification_version: 4
349
358
  summary: Facilitate log aggregation via unified logging
350
359
  test_files:
351
360
  - spec/fixtures/README.md
352
- - spec/fixtures/rails32.rb
353
- - spec/fixtures/rails40.rb
354
361
  - spec/fixtures/rails42.rb
355
362
  - spec/fixtures/rails50.rb
356
363
  - spec/fixtures/rails52.rb
@@ -362,10 +369,14 @@ test_files:
362
369
  - spec/integration/rails/railtie_spec.rb
363
370
  - spec/integration/rails/request_spec.rb
364
371
  - spec/integration/sidekiq5_spec.rb
365
- - spec/integration/sidekiq6_spec.rb
372
+ - spec/integration/sidekiq60_spec.rb
373
+ - spec/integration/sidekiq61_spec.rb
374
+ - spec/integration/sidekiq65_spec.rb
375
+ - spec/integration/sidekiq7_spec.rb
366
376
  - spec/integration/sinatra_spec.rb
367
377
  - spec/loga/sidekiq5/job_logger_spec.rb
368
378
  - spec/loga/sidekiq6/job_logger_spec.rb
379
+ - spec/loga/sidekiq7/job_logger_spec.rb
369
380
  - spec/loga/sidekiq_spec.rb
370
381
  - spec/spec_helper.rb
371
382
  - spec/support/gethostname_shared.rb