loga 2.3.1 → 2.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9144c2b13cb8e6ed178d6ee24e99c9d00d4ce23bf3198d487e1803d221038874
4
- data.tar.gz: 2528a6ce89a60dccc50e22f98abd1e4e74ae3f49319adeed42a89a98ad4412f8
3
+ metadata.gz: 252a23996e615f29819c4465c083adb286278026baaf2c2d693883bf76560e24
4
+ data.tar.gz: c410a1c3442a4f66fd299c456cf62ae7cc21d8beb89ce16ba98460a0b1142b3f
5
5
  SHA512:
6
- metadata.gz: 1d9b346f49a5661ab32f3cabe4a16edba7757aa5a5d10b830758d1ca12ff70ece723014701a5ba82da7d358736c320af4a91e7dee609ba657d200905b5afdb3f
7
- data.tar.gz: e53c366b5efcb67136322cae79fa6f21fcfdbefd654cb982681067b02de0c86d02f678afe02b7e5c3db298024818ba06dd5ac4ad51f0c234ea799a906295782d
6
+ metadata.gz: bbeb468b6ddd72798c497a8f1f2f0d34acf189e9ada83d4dc6d878461e9d65b19365582deb9a9ce48886379f10e97185c551c228b4a0adc827a3d1d2d686c987
7
+ data.tar.gz: f063246243858c1645bfd9b02b50396268c0c3c72ae966d2e070f22cf2dd332635601c66ab4c550680c49eb9faaaafaae0c60e98c71fa249f14de5fd45881eb4
@@ -58,21 +58,25 @@ jobs:
58
58
  command: |
59
59
  ./tmp/cc-test-reporter sum-coverage tmp/codeclimate.*.json -o tmp/codeclimate.total.json
60
60
  ./tmp/cc-test-reporter upload-coverage -i tmp/codeclimate.total.json -r $CODECLIMATE_REPO_TOKEN
61
- ruby-2.2:
62
- docker:
63
- - image: circleci/ruby:2.2.10
64
- <<: *test_build
65
61
  ruby-2.3:
66
62
  docker:
67
- - image: circleci/ruby:2.3.7
63
+ - image: circleci/ruby:2.3
68
64
  <<: *test_build
69
65
  ruby-2.4:
70
66
  docker:
71
- - image: circleci/ruby:2.4.4
67
+ - image: circleci/ruby:2.4
72
68
  <<: *test_build
73
69
  ruby-2.5:
74
70
  docker:
75
- - image: circleci/ruby:2.5.1
71
+ - image: circleci/ruby:2.5
72
+ <<: *test_build
73
+ ruby-2.6:
74
+ docker:
75
+ - image: circleci/ruby:2.6
76
+ <<: *test_build
77
+ ruby-2.7:
78
+ docker:
79
+ - image: circleci/ruby:2.7
76
80
  <<: *test_build
77
81
  rubocop:
78
82
  <<: *basic_build
@@ -111,25 +115,31 @@ workflows:
111
115
  filters:
112
116
  tags:
113
117
  only: /.*/
114
- - ruby-2.2:
118
+ - ruby-2.3:
115
119
  filters:
116
120
  tags:
117
121
  only: /.*/
118
122
  requires:
119
123
  - build
120
- - ruby-2.3:
124
+ - ruby-2.4:
121
125
  filters:
122
126
  tags:
123
127
  only: /.*/
124
128
  requires:
125
129
  - build
126
- - ruby-2.4:
130
+ - ruby-2.5:
127
131
  filters:
128
132
  tags:
129
133
  only: /.*/
130
134
  requires:
131
135
  - build
132
- - ruby-2.5:
136
+ - ruby-2.6:
137
+ filters:
138
+ tags:
139
+ only: /.*/
140
+ requires:
141
+ - build
142
+ - ruby-2.7:
133
143
  filters:
134
144
  tags:
135
145
  only: /.*/
@@ -140,10 +150,11 @@ workflows:
140
150
  tags:
141
151
  only: /.*/
142
152
  requires:
143
- - ruby-2.2
144
153
  - ruby-2.3
145
154
  - ruby-2.4
146
155
  - ruby-2.5
156
+ - ruby-2.6
157
+ - ruby-2.7
147
158
  - push-to-rubygems:
148
159
  filters:
149
160
  tags:
@@ -152,7 +163,8 @@ workflows:
152
163
  ignore: /.*/
153
164
  requires:
154
165
  - rubocop
155
- - ruby-2.2
156
166
  - ruby-2.3
157
167
  - ruby-2.4
158
168
  - ruby-2.5
169
+ - ruby-2.6
170
+ - ruby-2.7
data/Appraisals CHANGED
@@ -8,20 +8,32 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
8
8
  end
9
9
  end
10
10
 
11
- appraise 'rails42' do
12
- gem 'rails', '~> 4.2.0'
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
15
+
16
+ appraise 'rails50' do
17
+ gem 'rails', '~> 5.0.0'
18
+ end
19
+
20
+ appraise 'rails52' do
21
+ gem 'rails', '~> 5.2.0'
22
+ end
13
23
  end
14
24
 
15
25
  appraise 'sinatra14' do
16
26
  gem 'sinatra', '~> 1.4.0'
17
27
  end
18
28
 
19
- appraise 'rails50' do
20
- gem 'rails', '~> 5.0.0'
21
- end
29
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.5.0')
30
+ appraise 'rails60' do
31
+ gem 'rails', '~> 6.0.0'
32
+ end
22
33
 
23
- appraise 'rails52' do
24
- gem 'rails', '~> 5.2.0'
34
+ appraise 'sidekiq6' do
35
+ gem 'sidekiq', '~> 6.0'
36
+ end
25
37
  end
26
38
 
27
39
  appraise 'sidekiq51' do
@@ -4,6 +4,30 @@ 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
+
11
+ ## [2.5.2] - 2020-10-21
12
+ ### Fixed
13
+ - Support for sidekiq 6
14
+
15
+ ## [2.5.1] - 2020-01-02
16
+ ### Fixed
17
+ - Fixed a long standing bug that would mask exceptions raised by the host application when serving requests. The original exception would be replaced with a `TypeError` one due to a HTTP status code not being available within `Loga::Rack::Logger`.
18
+
19
+ ## [2.5.0] - 2019-11-12
20
+ ### Added
21
+ - Add support for rails 6
22
+
23
+ ## [2.4.0] - 2019-09-03
24
+ ### Fixed
25
+ - `duration` in the `sidekiq` integration is now calculated correctly
26
+ ### Added
27
+ - Add build for ruby 2.6
28
+ ### Removed
29
+ - Remove build for ruby 2.2
30
+
7
31
  ## [2.3.1] - 2019-05-14
8
32
  ### Added
9
33
  New configuration option `hide_pii` which defaults to `true` to hide email addresses in logs that get generate when an email is sent through action_mailer
data/Gemfile CHANGED
@@ -4,5 +4,5 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem 'simplecov'
7
+ gem 'simplecov', '~> 0.17.0'
8
8
  end
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://gemnasium.com/badges/github.com/FundingCircle/loga.svg)](https://gemnasium.com/github.com/FundingCircle/loga)
7
+ [![Dependency Status](https://img.shields.io/librariesio/release/rubygems/loga)](https://libraries.io/rubygems/loga)
8
8
 
9
9
  ## Description
10
10
 
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0"
6
+
7
+ group :test do
8
+ gem "simplecov"
9
+ end
10
+
11
+ gemspec path: "../"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 6.0"
6
+
7
+ group :test do
8
+ gem "simplecov"
9
+ end
10
+
11
+ gemspec path: "../"
@@ -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
- event.data ||= {}
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
@@ -44,6 +44,9 @@ module Loga
44
44
  data['user_agent'] = request.user_agent
45
45
  data['controller'] = request.controller_action_name if request.controller_action_name
46
46
  data['duration'] = duration_in_ms(started_at, Time.now)
47
+
48
+ # If data['status'] is nil we assume an exception was raised when calling the application
49
+ data['status'] ||= 500
47
50
  end
48
51
  # rubocop:enable Metrics/LineLength
49
52
 
@@ -125,7 +125,7 @@ module Loga
125
125
  def silence_rails_rack_logger
126
126
  case Rails::VERSION::MAJOR
127
127
  when 3 then require 'loga/ext/rails/rack/logger3.rb'
128
- when 4..5 then require 'loga/ext/rails/rack/logger.rb'
128
+ when 4..6 then require 'loga/ext/rails/rack/logger.rb'
129
129
  else
130
130
  raise Loga::ConfigurationError,
131
131
  "Rails #{Rails::VERSION::MAJOR} is unsupported"
@@ -1,16 +1,24 @@
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.configure_server do |config|
10
- config.options[:job_logger] = Loga::Sidekiq::JobLogger
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
- ::Sidekiq::Logging.logger = Loga.configuration.logger
21
+ ::Sidekiq.logger = Loga.configuration.logger
14
22
  end
15
23
  end
16
24
  end
@@ -1,22 +1,22 @@
1
1
  module Loga
2
- module Sidekiq
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
 
10
8
  EVENT_TYPE = 'sidekiq'.freeze
11
9
 
12
- attr_reader :started_at, :data
10
+ def started_at
11
+ @started_at ||= Time.now
12
+ end
13
13
 
14
- def initialize
15
- @started_at = Time.now
16
- @data = {}
14
+ def data
15
+ @data ||= {}
17
16
  end
18
17
 
19
18
  def call(item, _queue)
19
+ reset_data
20
20
  yield
21
21
  rescue Exception => ex # rubocop:disable Lint/RescueException
22
22
  data['exception'] = ex
@@ -29,6 +29,11 @@ module Loga
29
29
 
30
30
  private
31
31
 
32
+ def reset_data
33
+ @data = {}
34
+ @started_at = Time.now
35
+ end
36
+
32
37
  def assign_data(item)
33
38
  data['created_at'] = item['created_at']
34
39
  data['enqueued_at'] = item['enqueued_at']
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Loga
2
- VERSION = '2.3.1'.freeze
2
+ VERSION = '2.5.3'.freeze
3
3
  end
@@ -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', '~> 1.6'
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!
@@ -19,11 +19,40 @@ class MySidekiqWorker
19
19
  end
20
20
 
21
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
+
22
51
  let(:target) { StringIO.new }
23
52
 
24
- let(:json_line) do
53
+ def read_json_log(line:)
25
54
  target.rewind
26
- JSON.parse(target.read)
55
+ JSON.parse(target.each_line.drop(line - 1).first)
27
56
  end
28
57
 
29
58
  before do
@@ -40,13 +69,7 @@ describe 'Sidekiq client logger' do
40
69
  end
41
70
 
42
71
  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 Sidekiq::Logging.logger' do
49
- expect(Sidekiq::Logging.logger).to eq Loga.logger
72
+ expect(Sidekiq.options[:job_logger]).to eq Loga::Sidekiq5::JobLogger
50
73
  end
51
74
 
52
75
  it 'has the proper logger for Sidekiq.logger' do
@@ -66,45 +89,24 @@ describe 'Sidekiq client logger' do
66
89
  end
67
90
  end
68
91
 
69
- if ENV['BUNDLE_GEMFILE'] =~ /sidekiq51/
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
92
+ it 'has the proper logger Sidekiq::Logging.logger' do
93
+ expect(Sidekiq::Logging.logger).to eq Loga.logger
94
+ end
99
95
 
100
- it 'logs the job processing event' do
101
- MySidekiqWorker.perform_async('Bob')
96
+ # https://github.com/mperham/sidekiq/blob/97363210b47a4f8a1d8c1233aaa059d6643f5040/test/test_actors.rb#L57-L79
102
97
 
103
- require 'sidekiq/processor'
104
- Sidekiq::Processor.new(mgr.new).start
105
- sleep 0.5
98
+ it 'logs the job processing event' do
99
+ MySidekiqWorker.perform_async('Bob')
106
100
 
107
- expected_attributes = {
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(
108
110
  '_queue'=> 'default',
109
111
  '_retry'=> true,
110
112
  '_params'=> ['Bob'],
@@ -115,17 +117,27 @@ describe 'Sidekiq client logger' do
115
117
  '_tags'=> '',
116
118
  'level'=> 6,
117
119
  'version'=> '1.1',
118
- }
120
+ )
119
121
 
120
- aggregate_failures do
121
- expect(json_line).to include(expected_attributes)
122
-
123
- %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
124
- expect(json_line).to have_key(key)
125
- end
126
-
127
- expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
122
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
123
+ expect(json_line).to have_key(key)
128
124
  end
125
+
126
+ expect(json_line['short_message']).to match(/MySidekiqWorker with jid:*/)
129
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
130
142
  end
131
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
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
+ require 'loga/sidekiq5/job_logger'
2
3
 
3
- RSpec.describe Loga::Sidekiq::JobLogger do
4
+ RSpec.describe Loga::Sidekiq5::JobLogger do
4
5
  subject(:job_logger) { described_class.new }
5
6
 
6
7
  let(:target) { StringIO.new }
@@ -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
@@ -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 version is 5.1' do
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
- expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq::JobLogger)
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
 
@@ -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
- sidekiq_specs = [
34
- 'integration/sidekiq_spec.rb',
35
- 'spec/loga/sidekiq/**/*_spec.rb',
36
- 'spec/loga/sidekiq_spec.rb',
37
- ]
38
-
39
- rspec_pattern = sidekiq_specs.join(',')
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,
@@ -65,12 +65,13 @@ describe Loga::Rack::Logger do
65
65
  let(:exception) { StandardError.new }
66
66
  let(:logged_exception) { nil }
67
67
  let(:response_status) { 200 }
68
+ let(:exception_class) { Class.new(StandardError) }
68
69
 
69
70
  context 'when an exception is raised' do
70
- let(:app) { ->(_env) { raise exception } }
71
+ let(:app) { ->(_env) { raise exception_class } }
71
72
 
72
73
  it 'does not rescue the exception' do
73
- expect { subject.call(env) }.to raise_error(StandardError)
74
+ expect { subject.call(env) }.to raise_error(exception_class)
74
75
  end
75
76
  end
76
77
 
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.3.1
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: 2019-05-14 00:00:00.000000000 Z
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
@@ -259,7 +259,9 @@ files:
259
259
  - gemfiles/rails42.gemfile
260
260
  - gemfiles/rails50.gemfile
261
261
  - gemfiles/rails52.gemfile
262
+ - gemfiles/rails60.gemfile
262
263
  - gemfiles/sidekiq51.gemfile
264
+ - gemfiles/sidekiq6.gemfile
263
265
  - gemfiles/sinatra14.gemfile
264
266
  - gemfiles/unit.gemfile
265
267
  - lib/loga.rb
@@ -279,7 +281,8 @@ files:
279
281
  - lib/loga/railtie.rb
280
282
  - lib/loga/service_version_strategies.rb
281
283
  - lib/loga/sidekiq.rb
282
- - lib/loga/sidekiq/job_logger.rb
284
+ - lib/loga/sidekiq5/job_logger.rb
285
+ - lib/loga/sidekiq6/job_logger.rb
283
286
  - lib/loga/tagged_logging.rb
284
287
  - lib/loga/utilities.rb
285
288
  - lib/loga/version.rb
@@ -290,13 +293,16 @@ files:
290
293
  - spec/fixtures/rails42.rb
291
294
  - spec/fixtures/rails50.rb
292
295
  - spec/fixtures/rails52.rb
296
+ - spec/fixtures/rails60.rb
293
297
  - spec/fixtures/random_bin
294
298
  - spec/integration/rails/action_mailer_spec.rb
295
299
  - spec/integration/rails/railtie_spec.rb
296
300
  - spec/integration/rails/request_spec.rb
297
- - spec/integration/sidekiq_spec.rb
301
+ - spec/integration/sidekiq5_spec.rb
302
+ - spec/integration/sidekiq6_spec.rb
298
303
  - spec/integration/sinatra_spec.rb
299
- - spec/loga/sidekiq/job_logger_spec.rb
304
+ - spec/loga/sidekiq5/job_logger_spec.rb
305
+ - spec/loga/sidekiq6/job_logger_spec.rb
300
306
  - spec/loga/sidekiq_spec.rb
301
307
  - spec/spec_helper.rb
302
308
  - spec/support/gethostname_shared.rb
@@ -345,13 +351,16 @@ test_files:
345
351
  - spec/fixtures/rails42.rb
346
352
  - spec/fixtures/rails50.rb
347
353
  - spec/fixtures/rails52.rb
354
+ - spec/fixtures/rails60.rb
348
355
  - spec/fixtures/random_bin
349
356
  - spec/integration/rails/action_mailer_spec.rb
350
357
  - spec/integration/rails/railtie_spec.rb
351
358
  - spec/integration/rails/request_spec.rb
352
- - spec/integration/sidekiq_spec.rb
359
+ - spec/integration/sidekiq5_spec.rb
360
+ - spec/integration/sidekiq6_spec.rb
353
361
  - spec/integration/sinatra_spec.rb
354
- - spec/loga/sidekiq/job_logger_spec.rb
362
+ - spec/loga/sidekiq5/job_logger_spec.rb
363
+ - spec/loga/sidekiq6/job_logger_spec.rb
355
364
  - spec/loga/sidekiq_spec.rb
356
365
  - spec/spec_helper.rb
357
366
  - spec/support/gethostname_shared.rb