loga 2.5.2 → 2.5.3
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 +12 -10
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/loga/configuration.rb +1 -0
- data/lib/loga/formatters/gelf_formatter.rb +16 -1
- data/lib/loga/sidekiq.rb +12 -4
- 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/integration/sidekiq5_spec.rb +143 -0
- data/spec/integration/sidekiq6_spec.rb +164 -0
- 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
- metadata +14 -9
- data/spec/integration/sidekiq_spec.rb +0 -147
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 252a23996e615f29819c4465c083adb286278026baaf2c2d693883bf76560e24
|
4
|
+
data.tar.gz: c410a1c3442a4f66fd299c456cf62ae7cc21d8beb89ce16ba98460a0b1142b3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbeb468b6ddd72798c497a8f1f2f0d34acf189e9ada83d4dc6d878461e9d65b19365582deb9a9ce48886379f10e97185c551c228b4a0adc827a3d1d2d686c987
|
7
|
+
data.tar.gz: f063246243858c1645bfd9b02b50396268c0c3c72ae966d2e070f22cf2dd332635601c66ab4c550680c49eb9faaaafaae0c60e98c71fa249f14de5fd45881eb4
|
data/.circleci/config.yml
CHANGED
@@ -74,6 +74,10 @@ jobs:
|
|
74
74
|
docker:
|
75
75
|
- image: circleci/ruby:2.6
|
76
76
|
<<: *test_build
|
77
|
+
ruby-2.7:
|
78
|
+
docker:
|
79
|
+
- image: circleci/ruby:2.7
|
80
|
+
<<: *test_build
|
77
81
|
rubocop:
|
78
82
|
<<: *basic_build
|
79
83
|
steps:
|
@@ -135,6 +139,12 @@ workflows:
|
|
135
139
|
only: /.*/
|
136
140
|
requires:
|
137
141
|
- build
|
142
|
+
- ruby-2.7:
|
143
|
+
filters:
|
144
|
+
tags:
|
145
|
+
only: /.*/
|
146
|
+
requires:
|
147
|
+
- build
|
138
148
|
- upload-coverage:
|
139
149
|
filters:
|
140
150
|
tags:
|
@@ -144,6 +154,7 @@ workflows:
|
|
144
154
|
- ruby-2.4
|
145
155
|
- ruby-2.5
|
146
156
|
- ruby-2.6
|
157
|
+
- ruby-2.7
|
147
158
|
- push-to-rubygems:
|
148
159
|
filters:
|
149
160
|
tags:
|
@@ -156,3 +167,4 @@ workflows:
|
|
156
167
|
- ruby-2.4
|
157
168
|
- ruby-2.5
|
158
169
|
- ruby-2.6
|
170
|
+
- ruby-2.7
|
data/Appraisals
CHANGED
@@ -8,20 +8,22 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
|
12
|
+
appraise 'rails42' do
|
13
|
+
gem 'rails', '~> 4.2.0'
|
14
|
+
end
|
14
15
|
|
15
|
-
appraise '
|
16
|
-
|
17
|
-
end
|
16
|
+
appraise 'rails50' do
|
17
|
+
gem 'rails', '~> 5.0.0'
|
18
|
+
end
|
18
19
|
|
19
|
-
appraise '
|
20
|
-
|
20
|
+
appraise 'rails52' do
|
21
|
+
gem 'rails', '~> 5.2.0'
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
|
-
appraise '
|
24
|
-
gem '
|
25
|
+
appraise 'sinatra14' do
|
26
|
+
gem 'sinatra', '~> 1.4.0'
|
25
27
|
end
|
26
28
|
|
27
29
|
if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.5.0')
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
|
+
## [2.5.3] - 2020-10-27
|
8
|
+
### Fixed
|
9
|
+
- Support for sidekiq 6 - previous versions were causing sidekiq to crash with `Internal exception!`
|
10
|
+
|
7
11
|
## [2.5.2] - 2020-10-21
|
8
12
|
### Fixed
|
9
13
|
- Support for sidekiq 6
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
[![Build Status](https://circleci.com/gh/FundingCircle/loga/tree/master.svg?style=shield&circle-token=9b81c3cf8468a8c3dc760f4c0398cf8914cb27d4)](https://circleci.com/gh/FundingCircle/loga/tree/master)
|
5
5
|
[![Code Quality](https://codeclimate.com/repos/5563694f6956805723005d2f/badges/8eecb9144730614fb39e/gpa.svg)](https://codeclimate.com/repos/5563694f6956805723005d2f/feed)
|
6
6
|
[![Test Coverage](https://codeclimate.com/repos/5563694f6956805723005d2f/badges/8eecb9144730614fb39e/coverage.svg)](https://codeclimate.com/repos/5563694f6956805723005d2f/coverage)
|
7
|
-
[![Dependency Status](https://
|
7
|
+
[![Dependency Status](https://img.shields.io/librariesio/release/rubygems/loga)](https://libraries.io/rubygems/loga)
|
8
8
|
|
9
9
|
## Description
|
10
10
|
|
data/lib/loga/configuration.rb
CHANGED
@@ -79,6 +79,7 @@ module Loga
|
|
79
79
|
{ format: ENV['LOGA_FORMAT'].presence }.reject { |_, v| v.nil? }
|
80
80
|
end
|
81
81
|
|
82
|
+
# Note: sidekiq 6 will extend the logger -> https://github.com/mperham/sidekiq/blob/v6.1.2/lib/sidekiq.rb#L210
|
82
83
|
def initialize_logger
|
83
84
|
device.sync = sync
|
84
85
|
logger = Logger.new(device)
|
@@ -47,7 +47,8 @@ module Loga
|
|
47
47
|
end
|
48
48
|
|
49
49
|
event.timestamp ||= time
|
50
|
-
|
50
|
+
# Overwrite sidekiq_context data anything manually specified
|
51
|
+
event.data = sidekiq_context.merge!(event.data || {})
|
51
52
|
event.data.tap do |hash|
|
52
53
|
hash.merge! compute_exception(event.exception)
|
53
54
|
hash.merge! compute_type(event.type)
|
@@ -101,6 +102,20 @@ module Loga
|
|
101
102
|
tags: current_tags.join(' '),
|
102
103
|
}
|
103
104
|
end
|
105
|
+
|
106
|
+
def sidekiq_context
|
107
|
+
return {} unless defined?(::Sidekiq::Context)
|
108
|
+
|
109
|
+
c = ::Sidekiq::Context.current
|
110
|
+
|
111
|
+
# The context usually holds :class and :jid. :elapsed is added when the job ends
|
112
|
+
data = c.dup
|
113
|
+
if data.key?(:elapsed)
|
114
|
+
data[:duration] = data[:elapsed].to_f
|
115
|
+
data.delete(:elapsed)
|
116
|
+
end
|
117
|
+
data
|
118
|
+
end
|
104
119
|
end
|
105
120
|
end
|
106
121
|
end
|
data/lib/loga/sidekiq.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
|
-
require 'loga/sidekiq/job_logger'
|
2
|
-
|
3
1
|
module Loga
|
4
2
|
module Sidekiq
|
5
3
|
def self.configure_logging
|
6
4
|
return unless defined?(::Sidekiq)
|
7
5
|
return if Gem::Version.new(::Sidekiq::VERSION) < Gem::Version.new('5.0')
|
8
6
|
|
9
|
-
::Sidekiq
|
10
|
-
|
7
|
+
if Gem::Version.new(::Sidekiq::VERSION) < Gem::Version.new('6.0')
|
8
|
+
require 'loga/sidekiq5/job_logger'
|
9
|
+
|
10
|
+
::Sidekiq.configure_server do |config|
|
11
|
+
config.options[:job_logger] = Loga::Sidekiq5::JobLogger
|
12
|
+
end
|
13
|
+
elsif Gem::Version.new(::Sidekiq::VERSION) < Gem::Version.new('7.0')
|
14
|
+
require 'loga/sidekiq6/job_logger'
|
15
|
+
|
16
|
+
::Sidekiq.configure_server do |config|
|
17
|
+
config.options[:job_logger] = Loga::Sidekiq6::JobLogger
|
18
|
+
end
|
11
19
|
end
|
12
20
|
|
13
21
|
::Sidekiq.logger = Loga.configuration.logger
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module Loga
|
2
|
-
module
|
2
|
+
module Sidekiq5
|
3
3
|
# The approach of using a custom job logger in sidekiq was introduced
|
4
4
|
# in v5.0: https://github.com/mperham/sidekiq/pull/3235
|
5
|
-
# This job logger does not support logging for Sidekiq versions
|
6
|
-
# that are before 5.0
|
7
5
|
class JobLogger
|
8
6
|
include Loga::Utilities
|
9
7
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sidekiq/job_logger'
|
2
|
+
|
3
|
+
module Loga
|
4
|
+
module Sidekiq6
|
5
|
+
class JobLogger < ::Sidekiq::JobLogger
|
6
|
+
EVENT_TYPE = 'sidekiq'.freeze
|
7
|
+
|
8
|
+
def call(item, _queue)
|
9
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
10
|
+
|
11
|
+
yield
|
12
|
+
|
13
|
+
with_elapsed_time_context(start) do
|
14
|
+
loga_log(
|
15
|
+
message: "#{item['class']} with jid: '#{item['jid']}' done", item: item,
|
16
|
+
)
|
17
|
+
end
|
18
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
19
|
+
with_elapsed_time_context(start) do
|
20
|
+
loga_log(
|
21
|
+
message: "#{item['class']} with jid: '#{item['jid']}' fail", item: item,
|
22
|
+
exception: e
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def loga_log(message:, item:, exception: nil)
|
32
|
+
data = {
|
33
|
+
'created_at' => item['created_at'],
|
34
|
+
'enqueued_at' => item['enqueued_at'],
|
35
|
+
'jid' => item['jid'],
|
36
|
+
'queue' => item['queue'],
|
37
|
+
'retry' => item['retry'],
|
38
|
+
'params' => item['args'],
|
39
|
+
'class' => item['class'],
|
40
|
+
}
|
41
|
+
if exception
|
42
|
+
data['exception'] = exception
|
43
|
+
@logger.warn(Event.new(type: EVENT_TYPE, message: message, data: data))
|
44
|
+
else
|
45
|
+
@logger.info(Event.new(type: EVENT_TYPE, message: message, data: data))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
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,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
|
@@ -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
|
data/spec/loga/sidekiq_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Loga::Sidekiq do
|
4
4
|
describe '.configure_logging' do
|
5
|
-
context 'when sidekiq
|
5
|
+
context 'when sidekiq is defined' do
|
6
6
|
it 'gets invoked on Loga.configure' do
|
7
7
|
allow(described_class).to receive(:configure_logging)
|
8
8
|
|
@@ -18,7 +18,7 @@ RSpec.describe Loga::Sidekiq do
|
|
18
18
|
expect(described_class).to have_received(:configure_logging)
|
19
19
|
end
|
20
20
|
|
21
|
-
it 'assigns our custom sidekiq job logger' do
|
21
|
+
it 'assigns our custom sidekiq job logger depending on the sidekiq version' do
|
22
22
|
Loga.reset
|
23
23
|
|
24
24
|
Loga.configure(
|
@@ -28,7 +28,14 @@ RSpec.describe Loga::Sidekiq do
|
|
28
28
|
format: :gelf,
|
29
29
|
)
|
30
30
|
|
31
|
-
|
31
|
+
m = ENV['BUNDLE_GEMFILE'].match(/sidekiq(?<version>\d+)/)
|
32
|
+
|
33
|
+
case m['version']
|
34
|
+
when '51'
|
35
|
+
expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq5::JobLogger)
|
36
|
+
when '6'
|
37
|
+
expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq6::JobLogger)
|
38
|
+
end
|
32
39
|
end
|
33
40
|
end
|
34
41
|
|
data/spec/spec_helper.rb
CHANGED
@@ -29,14 +29,23 @@ when /sinatra/
|
|
29
29
|
when /unit/
|
30
30
|
rspec_pattern = 'unit/**/*_spec.rb'
|
31
31
|
require 'loga'
|
32
|
-
when /sidekiq/
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
when /sidekiq(?<version>\d+)/
|
33
|
+
case $LAST_MATCH_INFO['version']
|
34
|
+
when '51'
|
35
|
+
rspec_pattern = [
|
36
|
+
'spec/integration/sidekiq5_spec.rb',
|
37
|
+
'spec/loga/sidekiq5/**/*_spec.rb',
|
38
|
+
'spec/loga/sidekiq_spec.rb',
|
39
|
+
].join(',')
|
40
|
+
when '6'
|
41
|
+
rspec_pattern = [
|
42
|
+
'spec/integration/sidekiq6_spec.rb',
|
43
|
+
'spec/loga/sidekiq6/**/*_spec.rb',
|
44
|
+
'spec/loga/sidekiq_spec.rb',
|
45
|
+
].join(',')
|
46
|
+
else
|
47
|
+
raise 'FIXME: Unknown sidekiq - update this file.'
|
48
|
+
end
|
40
49
|
|
41
50
|
require 'sidekiq'
|
42
51
|
require 'sidekiq/cli'
|
@@ -166,6 +166,49 @@ describe Loga::Formatters::GELFFormatter do
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
+
context 'when working with sidekiq context' do
|
170
|
+
let(:options) { { message: 'Wooden house' } }
|
171
|
+
let(:message) { Loga::Event.new(options) }
|
172
|
+
let(:sidekiq_context) { { class: 'MyWorker', jid: '123' } }
|
173
|
+
|
174
|
+
before do
|
175
|
+
klass = Class.new do
|
176
|
+
class << self
|
177
|
+
attr_accessor :current
|
178
|
+
end
|
179
|
+
end
|
180
|
+
klass.current = sidekiq_context
|
181
|
+
stub_const('::Sidekiq::Context', klass)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'includes the ::Sidekiq::Context.current data' do
|
185
|
+
expect(json['_class']).to eq('MyWorker')
|
186
|
+
expect(json['_jid']).to eq('123')
|
187
|
+
end
|
188
|
+
|
189
|
+
include_examples 'valid GELF message'
|
190
|
+
|
191
|
+
describe 'overwriting sidekiq context data with manual one' do
|
192
|
+
let(:options) { { message: 'Test', data: { class: 'CoolTest' } } }
|
193
|
+
|
194
|
+
it 'uses the manual data instead of the sidekiq context' do
|
195
|
+
expect(json['_class']).to eq('CoolTest')
|
196
|
+
end
|
197
|
+
|
198
|
+
include_examples 'valid GELF message'
|
199
|
+
end
|
200
|
+
|
201
|
+
describe ':elapsed in the sidekiq context' do
|
202
|
+
let(:sidekiq_context) { { class: 'MyWorker', jid: '123', elapsed: '22.2' } }
|
203
|
+
|
204
|
+
it 'transforms it to _duration' do
|
205
|
+
expect(json['_duration']).to eq(22.2)
|
206
|
+
end
|
207
|
+
|
208
|
+
include_examples 'valid GELF message'
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
169
212
|
{
|
170
213
|
'DEBUG' => 7,
|
171
214
|
'INFO' => 6,
|
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.5.
|
4
|
+
version: 2.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Funding Circle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '1.6'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.6'
|
69
69
|
- !ruby/object:Gem::Dependency
|
@@ -281,7 +281,8 @@ files:
|
|
281
281
|
- lib/loga/railtie.rb
|
282
282
|
- lib/loga/service_version_strategies.rb
|
283
283
|
- lib/loga/sidekiq.rb
|
284
|
-
- lib/loga/
|
284
|
+
- lib/loga/sidekiq5/job_logger.rb
|
285
|
+
- lib/loga/sidekiq6/job_logger.rb
|
285
286
|
- lib/loga/tagged_logging.rb
|
286
287
|
- lib/loga/utilities.rb
|
287
288
|
- lib/loga/version.rb
|
@@ -297,9 +298,11 @@ files:
|
|
297
298
|
- spec/integration/rails/action_mailer_spec.rb
|
298
299
|
- spec/integration/rails/railtie_spec.rb
|
299
300
|
- spec/integration/rails/request_spec.rb
|
300
|
-
- spec/integration/
|
301
|
+
- spec/integration/sidekiq5_spec.rb
|
302
|
+
- spec/integration/sidekiq6_spec.rb
|
301
303
|
- spec/integration/sinatra_spec.rb
|
302
|
-
- spec/loga/
|
304
|
+
- spec/loga/sidekiq5/job_logger_spec.rb
|
305
|
+
- spec/loga/sidekiq6/job_logger_spec.rb
|
303
306
|
- spec/loga/sidekiq_spec.rb
|
304
307
|
- spec/spec_helper.rb
|
305
308
|
- spec/support/gethostname_shared.rb
|
@@ -353,9 +356,11 @@ test_files:
|
|
353
356
|
- spec/integration/rails/action_mailer_spec.rb
|
354
357
|
- spec/integration/rails/railtie_spec.rb
|
355
358
|
- spec/integration/rails/request_spec.rb
|
356
|
-
- spec/integration/
|
359
|
+
- spec/integration/sidekiq5_spec.rb
|
360
|
+
- spec/integration/sidekiq6_spec.rb
|
357
361
|
- spec/integration/sinatra_spec.rb
|
358
|
-
- spec/loga/
|
362
|
+
- spec/loga/sidekiq5/job_logger_spec.rb
|
363
|
+
- spec/loga/sidekiq6/job_logger_spec.rb
|
359
364
|
- spec/loga/sidekiq_spec.rb
|
360
365
|
- spec/spec_helper.rb
|
361
366
|
- spec/support/gethostname_shared.rb
|
@@ -1,147 +0,0 @@
|
|
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(:target) { StringIO.new }
|
23
|
-
|
24
|
-
def read_json_log(line:)
|
25
|
-
target.rewind
|
26
|
-
JSON.parse(target.each_line.drop(line - 1).first)
|
27
|
-
end
|
28
|
-
|
29
|
-
before do
|
30
|
-
Redis.current.flushall
|
31
|
-
|
32
|
-
Loga.reset
|
33
|
-
|
34
|
-
Loga.configure(
|
35
|
-
service_name: 'hello_world_app',
|
36
|
-
service_version: '1.0',
|
37
|
-
device: target,
|
38
|
-
format: :gelf,
|
39
|
-
)
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'has the proper job logger' do
|
43
|
-
job_logger = Loga::Sidekiq::JobLogger
|
44
|
-
|
45
|
-
expect(Sidekiq.options[:job_logger]).to eq job_logger
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'has the proper logger for Sidekiq.logger' do
|
49
|
-
expect(Sidekiq.logger).to eq Loga.logger
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'pushes a new element in the default queue' do
|
53
|
-
MySidekiqWorker.perform_async('Bob')
|
54
|
-
|
55
|
-
last_element = JSON.parse(Redis.current.lpop('queue:default'))
|
56
|
-
|
57
|
-
aggregate_failures do
|
58
|
-
expect(last_element['class']).to eq 'MySidekiqWorker'
|
59
|
-
expect(last_element['args']).to eq ['Bob']
|
60
|
-
expect(last_element['retry']).to eq true
|
61
|
-
expect(last_element['queue']).to eq 'default'
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
if ENV['BUNDLE_GEMFILE'] =~ /sidekiq51/
|
66
|
-
it 'has the proper logger Sidekiq::Logging.logger' do
|
67
|
-
expect(Sidekiq::Logging.logger).to eq Loga.logger
|
68
|
-
end
|
69
|
-
|
70
|
-
# https://github.com/mperham/sidekiq/blob/97363210b47a4f8a1d8c1233aaa059d6643f5040/test/test_actors.rb#L57-L79
|
71
|
-
let(:mgr) do
|
72
|
-
Class.new do
|
73
|
-
attr_reader :latest_error, :mutex, :cond
|
74
|
-
|
75
|
-
def initialize
|
76
|
-
@mutex = ::Mutex.new
|
77
|
-
@cond = ::ConditionVariable.new
|
78
|
-
end
|
79
|
-
|
80
|
-
def processor_died(_inst, err)
|
81
|
-
@latest_error = err
|
82
|
-
|
83
|
-
@mutex.synchronize { @cond.signal }
|
84
|
-
end
|
85
|
-
|
86
|
-
def processor_stopped(_inst)
|
87
|
-
@mutex.synchronize { @cond.signal }
|
88
|
-
end
|
89
|
-
|
90
|
-
def options
|
91
|
-
{
|
92
|
-
concurrency: 3,
|
93
|
-
queues: ['default'],
|
94
|
-
job_logger: Loga::Sidekiq::JobLogger,
|
95
|
-
}
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'logs the job processing event' do
|
101
|
-
MySidekiqWorker.perform_async('Bob')
|
102
|
-
|
103
|
-
require 'sidekiq/processor'
|
104
|
-
Sidekiq::Processor.new(mgr.new).start
|
105
|
-
sleep 0.5
|
106
|
-
|
107
|
-
expected_attributes = {
|
108
|
-
'_queue'=> 'default',
|
109
|
-
'_retry'=> true,
|
110
|
-
'_params'=> ['Bob'],
|
111
|
-
'_class'=> 'MySidekiqWorker',
|
112
|
-
'_type'=> 'sidekiq',
|
113
|
-
'_service.name'=> 'hello_world_app',
|
114
|
-
'_service.version'=> '1.0',
|
115
|
-
'_tags'=> '',
|
116
|
-
'level'=> 6,
|
117
|
-
'version'=> '1.1',
|
118
|
-
}
|
119
|
-
|
120
|
-
json_line = read_json_log(line: 1)
|
121
|
-
|
122
|
-
aggregate_failures do
|
123
|
-
expect(json_line).to include(expected_attributes)
|
124
|
-
|
125
|
-
%w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
|
126
|
-
expect(json_line).to have_key(key)
|
127
|
-
end
|
128
|
-
|
129
|
-
expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
|
130
|
-
end
|
131
|
-
|
132
|
-
# This was a bug - the duration was constantly incresing based on when
|
133
|
-
# the logger was created. https://github.com/FundingCircle/loga/pull/117
|
134
|
-
#
|
135
|
-
# Test that after sleeping for few seconds the duration is still under 500ms
|
136
|
-
sleep 1
|
137
|
-
|
138
|
-
MySidekiqWorker.perform_async('Bob')
|
139
|
-
|
140
|
-
sleep 1
|
141
|
-
|
142
|
-
json_line = read_json_log(line: 2)
|
143
|
-
|
144
|
-
expect(json_line['_duration']).to be < 500
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|