loga 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -0
- data/.codeclimate.yml +3 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +29 -2
- data/Appraisals +4 -0
- data/CHANGELOG.md +4 -0
- data/Guardfile +14 -0
- data/README.md +4 -0
- data/gemfiles/sidekiq51.gemfile +11 -0
- data/lib/loga.rb +4 -3
- data/lib/loga/ext/core/tempfile.rb +1 -1
- data/lib/loga/formatters/simple_formatter.rb +1 -3
- data/lib/loga/rack/request.rb +2 -2
- data/lib/loga/sidekiq.rb +16 -0
- data/lib/loga/sidekiq/job_logger.rb +62 -0
- data/lib/loga/utilities.rb +1 -1
- data/lib/loga/version.rb +1 -1
- data/loga.gemspec +4 -2
- data/spec/fixtures/rails32.rb +1 -0
- data/spec/integration/rails/railtie_spec.rb +9 -8
- data/spec/integration/rails/request_spec.rb +2 -2
- data/spec/integration/sidekiq_spec.rb +131 -0
- data/spec/integration/sinatra_spec.rb +17 -16
- data/spec/loga/sidekiq/job_logger_spec.rb +115 -0
- data/spec/loga/sidekiq_spec.rb +53 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/helpers.rb +8 -1
- data/spec/support/request_spec.rb +4 -4
- data/spec/support/timecop_shared.rb +1 -0
- data/spec/unit/loga/configuration_spec.rb +7 -4
- data/spec/unit/loga/event_spec.rb +2 -2
- data/spec/unit/loga/formatters/gelf_formatter_spec.rb +7 -5
- data/spec/unit/loga/formatters/simple_formatter_spec.rb +3 -0
- data/spec/unit/loga/log_subscribers/action_mailer_spec.rb +14 -13
- data/spec/unit/loga/parameter_filter_spec.rb +1 -1
- data/spec/unit/loga/rack/logger_spec.rb +10 -6
- data/spec/unit/loga/rack/request_spec.rb +4 -3
- data/spec/unit/loga/utilities_spec.rb +3 -3
- data/spec/unit/loga_spec.rb +6 -3
- metadata +44 -7
@@ -24,23 +24,10 @@ end
|
|
24
24
|
RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
25
25
|
let(:io) { StringIO.new }
|
26
26
|
let(:format) {}
|
27
|
-
before do
|
28
|
-
Loga.reset
|
29
|
-
Loga.configure(
|
30
|
-
device: io,
|
31
|
-
filter_parameters: [:password],
|
32
|
-
format: format,
|
33
|
-
service_name: 'hello_world_app',
|
34
|
-
service_version: '1.0',
|
35
|
-
tags: [:uuid, 'TEST_TAG'],
|
36
|
-
)
|
37
|
-
end
|
38
|
-
|
39
27
|
let(:last_log_entry) do
|
40
28
|
io.rewind
|
41
29
|
JSON.parse(io.read)
|
42
30
|
end
|
43
|
-
|
44
31
|
let(:app) do
|
45
32
|
Rack::Builder.new do
|
46
33
|
use Loga::Rack::RequestId
|
@@ -49,13 +36,26 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
49
36
|
end
|
50
37
|
end
|
51
38
|
|
39
|
+
before do
|
40
|
+
Loga.reset
|
41
|
+
Loga.configure(
|
42
|
+
device: io,
|
43
|
+
filter_parameters: [:password],
|
44
|
+
format: format,
|
45
|
+
service_name: 'hello_world_app',
|
46
|
+
service_version: '1.0',
|
47
|
+
tags: [:uuid, 'TEST_TAG'],
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
52
51
|
context 'when RACK_ENV is production', if: ENV['RACK_ENV'].eql?('production') do
|
53
52
|
let(:format) { :gelf }
|
53
|
+
|
54
54
|
include_examples 'request logger'
|
55
55
|
|
56
56
|
it 'does not include the controller name and action' do
|
57
57
|
get '/ok'
|
58
|
-
expect(last_log_entry).
|
58
|
+
expect(last_log_entry).not_to include('_request.controller')
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -85,7 +85,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
85
85
|
allow(Process).to receive(:pid).and_return(999)
|
86
86
|
end
|
87
87
|
|
88
|
-
|
88
|
+
describe 'get request' do
|
89
89
|
it 'logs the request' do
|
90
90
|
get '/ok', { username: 'yoshi' }, 'HTTP_X_REQUEST_ID' => '700a6a01'
|
91
91
|
|
@@ -93,7 +93,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
|
96
|
+
describe 'request with redirect' do
|
97
97
|
let(:data) do
|
98
98
|
super().merge(
|
99
99
|
'status' => 302,
|
@@ -101,6 +101,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
|
|
101
101
|
'params' => {},
|
102
102
|
)
|
103
103
|
end
|
104
|
+
|
104
105
|
it 'specifies the original path' do
|
105
106
|
get '/new', {}, 'HTTP_X_REQUEST_ID' => '700a6a01'
|
106
107
|
expect(last_log_entry).to eql("I, #{time_pid_tags} GET /new 302 in 0ms type=request #{data_as_text}\n")
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Loga::Sidekiq::JobLogger do
|
4
|
+
subject(:job_logger) { described_class.new }
|
5
|
+
|
6
|
+
let(:target) { StringIO.new }
|
7
|
+
|
8
|
+
let(:json_line) do
|
9
|
+
target.rewind
|
10
|
+
JSON.parse(target.read.split("\n").last)
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
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
|
+
describe '#call' do
|
25
|
+
context 'when the job passess successfully' do
|
26
|
+
let(:item_data) do
|
27
|
+
{
|
28
|
+
'class' => 'HardWorker',
|
29
|
+
'args' => ['asd'],
|
30
|
+
'retry' => true,
|
31
|
+
'queue' => 'default',
|
32
|
+
'jid' => '591f6f66ee0d218fb451dfb6',
|
33
|
+
'created_at' => 1_528_799_582.904939,
|
34
|
+
'enqueued_at' => 1_528_799_582.9049861,
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has the the required attributes on call' do
|
39
|
+
job_logger.call(item_data, 'queue') do
|
40
|
+
# something
|
41
|
+
end
|
42
|
+
|
43
|
+
expected_body = {
|
44
|
+
'version' => '1.1',
|
45
|
+
'level' => 6,
|
46
|
+
'_type' => 'sidekiq',
|
47
|
+
'_created_at' => 1_528_799_582.904939,
|
48
|
+
'_enqueued_at' => 1_528_799_582.9049861,
|
49
|
+
'_jid' => '591f6f66ee0d218fb451dfb6',
|
50
|
+
'_retry' => true,
|
51
|
+
'_queue' => 'default',
|
52
|
+
'_service.name' => 'hello_world_app',
|
53
|
+
'_class' => 'HardWorker',
|
54
|
+
'_service.version' => '1.0',
|
55
|
+
'_tags' => '',
|
56
|
+
'_params' => ['asd'],
|
57
|
+
}
|
58
|
+
|
59
|
+
aggregate_failures do
|
60
|
+
expect(json_line).to include(expected_body)
|
61
|
+
expect(json_line['timestamp']).to be_a(Float)
|
62
|
+
expect(json_line['host']).to be_a(String)
|
63
|
+
expect(json_line['short_message']).to match(/HardWorker with jid:*/)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when the job fails' do
|
69
|
+
let(:item_data) do
|
70
|
+
{
|
71
|
+
'class' => 'HardWorker',
|
72
|
+
'args' => ['asd'],
|
73
|
+
'retry' => true,
|
74
|
+
'queue' => 'default',
|
75
|
+
'jid' => '591f6f66ee0d218fb451dfb6',
|
76
|
+
'created_at' => 1_528_799_582.904939,
|
77
|
+
'enqueued_at' => 1_528_799_582.9049861,
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'has the the required attributes on call' do
|
82
|
+
failed_job = lambda do
|
83
|
+
job_logger.call(item_data, 'queue') do
|
84
|
+
raise StandardError
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
expected_body = {
|
89
|
+
'version' => '1.1',
|
90
|
+
'level' => 4,
|
91
|
+
'_type' => 'sidekiq',
|
92
|
+
'_created_at' => 1_528_799_582.904939,
|
93
|
+
'_enqueued_at' => 1_528_799_582.9049861,
|
94
|
+
'_jid' => '591f6f66ee0d218fb451dfb6',
|
95
|
+
'_retry' => true,
|
96
|
+
'_queue' => 'default',
|
97
|
+
'_service.name' => 'hello_world_app',
|
98
|
+
'_class' => 'HardWorker',
|
99
|
+
'_service.version' => '1.0',
|
100
|
+
'_tags' => '',
|
101
|
+
'_params' => ['asd'],
|
102
|
+
'_exception' => 'StandardError',
|
103
|
+
}
|
104
|
+
|
105
|
+
aggregate_failures do
|
106
|
+
expect(&failed_job).to raise_error(StandardError)
|
107
|
+
expect(json_line).to include(expected_body)
|
108
|
+
expect(json_line['timestamp']).to be_a(Float)
|
109
|
+
expect(json_line['host']).to be_a(String)
|
110
|
+
expect(json_line['short_message']).to match(/HardWorker with jid:*/)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Loga::Sidekiq do
|
4
|
+
describe '.configure_logging' do
|
5
|
+
context 'when sidekiq version is 5.1' do
|
6
|
+
it 'gets invoked on Loga.configure' do
|
7
|
+
allow(described_class).to receive(:configure_logging)
|
8
|
+
|
9
|
+
Loga.reset
|
10
|
+
|
11
|
+
Loga.configure(
|
12
|
+
service_name: 'hello_world_app',
|
13
|
+
service_version: '1.0',
|
14
|
+
device: StringIO.new,
|
15
|
+
format: :gelf,
|
16
|
+
)
|
17
|
+
|
18
|
+
expect(described_class).to have_received(:configure_logging)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'assigns our custom sidekiq job logger' do
|
22
|
+
Loga.reset
|
23
|
+
|
24
|
+
Loga.configure(
|
25
|
+
service_name: 'hello_world_app',
|
26
|
+
service_version: '1.0',
|
27
|
+
device: StringIO.new,
|
28
|
+
format: :gelf,
|
29
|
+
)
|
30
|
+
|
31
|
+
expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq::JobLogger)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
shared_examples 'a blank change' do
|
36
|
+
it 'does nothing' do
|
37
|
+
expect(described_class.configure_logging).to be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when sidekiq is not defined' do
|
42
|
+
before { hide_const('Sidekiq') }
|
43
|
+
|
44
|
+
it_behaves_like 'a blank change'
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when sidekiq version is 4.2' do
|
48
|
+
before { stub_const('::Sidekiq::VERSION', '4.2') }
|
49
|
+
|
50
|
+
it_behaves_like 'a blank change'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,9 @@ require 'simplecov'
|
|
9
9
|
|
10
10
|
SimpleCov.start do
|
11
11
|
command_name "ruby-#{RUBY_VERSION}-#{File.basename(ENV['BUNDLE_GEMFILE'], '.gemfile')}"
|
12
|
+
|
13
|
+
# Exclude specs from showing up in the code coverage report.
|
14
|
+
add_filter 'spec/'
|
12
15
|
end
|
13
16
|
|
14
17
|
case ENV['BUNDLE_GEMFILE']
|
@@ -26,6 +29,18 @@ when /sinatra/
|
|
26
29
|
when /unit/
|
27
30
|
rspec_pattern = 'unit/**/*_spec.rb'
|
28
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(',')
|
40
|
+
|
41
|
+
require 'sidekiq'
|
42
|
+
require 'sidekiq/cli'
|
43
|
+
require 'loga'
|
29
44
|
else
|
30
45
|
raise 'BUNDLE_GEMFILE is unknown. Ensure the appraisal is present in Appraisals'
|
31
46
|
end
|
@@ -35,4 +50,10 @@ RSpec.configure do |config|
|
|
35
50
|
config.include Rack::Test::Methods
|
36
51
|
|
37
52
|
config.pattern = rspec_pattern
|
53
|
+
|
54
|
+
config.mock_with :rspec do |mocks|
|
55
|
+
mocks.allow_message_expectations_on_nil = false
|
56
|
+
mocks.transfer_nested_constants = true
|
57
|
+
mocks.verify_doubled_constant_names = true
|
58
|
+
end
|
38
59
|
end
|
data/spec/support/helpers.rb
CHANGED
@@ -7,6 +7,13 @@ module Helpers
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def time_anchor_unix
|
10
|
-
BigDecimal
|
10
|
+
BigDecimal('1450150205.123')
|
11
|
+
end
|
12
|
+
|
13
|
+
def stub_loga
|
14
|
+
loga = class_double(Loga).as_stubbed_const
|
15
|
+
logger = instance_double(Logger)
|
16
|
+
allow(loga).to receive(:logger).and_return(logger)
|
17
|
+
loga
|
11
18
|
end
|
12
19
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
RSpec.shared_examples 'request logger' do
|
2
|
-
|
2
|
+
describe 'get request' do
|
3
3
|
it 'logs the request' do
|
4
4
|
get '/ok',
|
5
5
|
{ username: 'yoshi' },
|
@@ -27,7 +27,7 @@ RSpec.shared_examples 'request logger' do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
describe 'post request' do
|
31
31
|
let(:json_response) { JSON.parse(last_response.body) }
|
32
32
|
|
33
33
|
it 'logs the request' do
|
@@ -63,7 +63,7 @@ RSpec.shared_examples 'request logger' do
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
|
66
|
+
describe 'request with redirect' do
|
67
67
|
it 'specifies the original path' do
|
68
68
|
get '/new', {}, 'HTTP_USER_AGENT' => 'Chrome', 'HTTP_X_REQUEST_ID' => '471a34dc'
|
69
69
|
|
@@ -182,7 +182,7 @@ RSpec.shared_examples 'request logger' do
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
describe 'when the request uploads a binary file'
|
185
|
+
describe 'when the request uploads a binary file' do
|
186
186
|
it 'logs the request' do
|
187
187
|
post '/users?username=yoshi',
|
188
188
|
bob_file: Rack::Test::UploadedFile.new('spec/fixtures/random_bin')
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Loga::Configuration do
|
4
|
+
subject { described_class.new(options) }
|
5
|
+
|
4
6
|
let(:options) do
|
5
7
|
{ service_name: 'hello_world_app' }
|
6
8
|
end
|
7
9
|
|
8
|
-
subject { described_class.new(options) }
|
9
|
-
|
10
10
|
describe 'initialize' do
|
11
11
|
let(:framework_exceptions) do
|
12
12
|
%w[
|
@@ -75,6 +75,7 @@ describe Loga::Configuration do
|
|
75
75
|
expect(subject.service_version).to eq('unknown.sha')
|
76
76
|
end
|
77
77
|
end
|
78
|
+
|
78
79
|
context 'when initialized via user options' do
|
79
80
|
let(:options) { super().merge(service_version: 'v3.0.1') }
|
80
81
|
|
@@ -105,6 +106,7 @@ describe Loga::Configuration do
|
|
105
106
|
|
106
107
|
context 'when initialized via framework options' do
|
107
108
|
subject { described_class.new(options, framework_options) }
|
109
|
+
|
108
110
|
let(:framework_options) { { format: :gelf } }
|
109
111
|
|
110
112
|
it 'sets the format' do
|
@@ -126,6 +128,7 @@ describe Loga::Configuration do
|
|
126
128
|
|
127
129
|
context 'when initialized with ENV and framework options' do
|
128
130
|
subject { described_class.new(options, framework_options) }
|
131
|
+
|
129
132
|
let(:framework_options) { { format: :gelf } }
|
130
133
|
|
131
134
|
before do
|
@@ -200,13 +203,13 @@ describe Loga::Configuration do
|
|
200
203
|
context 'when format is :simple' do
|
201
204
|
let(:options) { super().merge(format: :simple) }
|
202
205
|
|
203
|
-
specify { expect(subject.structured?).to
|
206
|
+
specify { expect(subject.structured?).to be(false) }
|
204
207
|
end
|
205
208
|
|
206
209
|
context 'when format is :gelf' do
|
207
210
|
let(:options) { super().merge(format: :gelf) }
|
208
211
|
|
209
|
-
specify { expect(subject.structured?).to
|
212
|
+
specify { expect(subject.structured?).to be(true) }
|
210
213
|
end
|
211
214
|
end
|
212
215
|
end
|
@@ -2,13 +2,13 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Loga::Event, timecop: true do
|
4
4
|
describe 'initialize' do
|
5
|
-
context 'no message is passed' do
|
5
|
+
context 'when no message is passed' do
|
6
6
|
it 'sets message to an empty string' do
|
7
7
|
expect(subject.message).to eq ''
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
context 'message is passed' do
|
11
|
+
context 'when message is passed' do
|
12
12
|
let(:message) { "stuff \xC2".force_encoding 'ASCII-8BIT' }
|
13
13
|
let(:subject) { described_class.new message: message }
|
14
14
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Loga::Formatters::GELFFormatter do
|
4
|
+
subject { described_class.new(params) }
|
5
|
+
|
4
6
|
let(:service_name) { 'loga' }
|
5
7
|
let(:service_version) { '725e032a' }
|
6
8
|
let(:host) { 'www.example.com' }
|
@@ -12,8 +14,6 @@ describe Loga::Formatters::GELFFormatter do
|
|
12
14
|
}
|
13
15
|
end
|
14
16
|
|
15
|
-
subject { described_class.new(params) }
|
16
|
-
|
17
17
|
shared_examples 'valid GELF message' do
|
18
18
|
it 'includes the required fields' do
|
19
19
|
expect(json).to include('version' => '1.1',
|
@@ -51,6 +51,7 @@ describe Loga::Formatters::GELFFormatter do
|
|
51
51
|
|
52
52
|
context 'when the message parameter is a nil' do
|
53
53
|
let(:message) { nil }
|
54
|
+
|
54
55
|
it 'the short_message is empty' do
|
55
56
|
expect(json['short_message']).to eq('')
|
56
57
|
end
|
@@ -90,7 +91,7 @@ describe Loga::Formatters::GELFFormatter do
|
|
90
91
|
|
91
92
|
context 'when the Event has a timestamp' do
|
92
93
|
let(:time) { Time.new(2010, 12, 15, 9, 30, 5.323, '+02:00') }
|
93
|
-
let(:time_in_unix) { BigDecimal
|
94
|
+
let(:time_in_unix) { BigDecimal('1292398205.323') }
|
94
95
|
let(:options) { { timestamp: time } }
|
95
96
|
|
96
97
|
it 'uses the Event timestamp' do
|
@@ -105,7 +106,7 @@ describe Loga::Formatters::GELFFormatter do
|
|
105
106
|
end
|
106
107
|
|
107
108
|
context 'when the Event no type' do
|
108
|
-
specify { expect(json).
|
109
|
+
specify { expect(json).not_to include('_type') }
|
109
110
|
end
|
110
111
|
|
111
112
|
context 'when the Event has an exception' do
|
@@ -121,6 +122,7 @@ describe Loga::Formatters::GELFFormatter do
|
|
121
122
|
|
122
123
|
context 'when the backtrace is larger than 10 lines' do
|
123
124
|
let(:backtrace) { ('a'..'z').to_a }
|
125
|
+
|
124
126
|
it 'truncates the backtrace' do
|
125
127
|
expect(json['_exception.backtrace']).to eq("a\nb\nc\nd\ne\nf\ng\nh\ni\nj")
|
126
128
|
end
|
@@ -128,7 +130,7 @@ describe Loga::Formatters::GELFFormatter do
|
|
128
130
|
end
|
129
131
|
|
130
132
|
context 'when the Event has no exception' do
|
131
|
-
specify { expect(json).
|
133
|
+
specify { expect(json).not_to include(/_exception.+/) }
|
132
134
|
end
|
133
135
|
|
134
136
|
context 'when the Event has data' do
|