loga 1.4.0 → 2.0.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/.rubocop.yml +21 -9
- data/CHANGELOG.md +50 -0
- data/Guardfile +45 -0
- data/LICENSE.txt +29 -0
- data/README.md +149 -81
- data/circle.yml +5 -5
- data/lib/loga.rb +12 -8
- data/lib/loga/configuration.rb +97 -46
- data/lib/loga/event.rb +11 -0
- data/lib/loga/rack/logger.rb +21 -16
- data/lib/loga/rack/request.rb +18 -6
- data/lib/loga/railtie.rb +74 -33
- data/lib/loga/service_version_strategies.rb +19 -0
- data/lib/loga/version.rb +1 -1
- data/loga.gemspec +5 -1
- data/spec/fixtures/rails32/config/application.rb +6 -6
- data/spec/fixtures/rails40/config/application.rb +6 -6
- data/spec/fixtures/rails50/config/application.rb +6 -6
- data/spec/integration/rails/railtie_spec.rb +69 -61
- data/spec/integration/rails/request_spec.rb +17 -18
- data/spec/integration/sinatra_spec.rb +53 -14
- data/spec/support/request_spec.rb +11 -10
- data/spec/unit/loga/configuration_spec.rb +163 -57
- data/spec/unit/loga/event_spec.rb +31 -1
- data/spec/unit/loga/rack/logger_spec.rb +22 -10
- data/spec/unit/loga/rack/request_spec.rb +19 -26
- data/spec/unit/loga/service_version_strategies_spec.rb +37 -0
- data/spec/unit/loga_spec.rb +52 -15
- metadata +53 -8
- data/.rubocop_todo.yml +0 -33
- data/lib/loga/revision_strategy.rb +0 -32
- data/spec/unit/loga/revision_strategy_spec.rb +0 -41
@@ -1,68 +1,183 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Loga::Configuration do
|
4
|
-
|
5
|
-
|
4
|
+
let(:options) do
|
5
|
+
{ service_name: 'hello_world_app' }
|
6
6
|
end
|
7
7
|
|
8
|
+
subject { described_class.new(options) }
|
9
|
+
|
8
10
|
describe 'initialize' do
|
9
|
-
|
11
|
+
let(:framework_exceptions) do
|
12
|
+
%w(
|
13
|
+
ActionController::RoutingError
|
14
|
+
ActiveRecord::RecordNotFound
|
15
|
+
Sinatra::NotFound
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
allow(Loga::ServiceVersionStrategies).to receive(:call).and_return('unknown.sha')
|
21
|
+
end
|
22
|
+
|
10
23
|
context 'defaults' do
|
24
|
+
specify { expect(subject.device).to eq(STDOUT) }
|
25
|
+
specify { expect(subject.filter_exceptions).to eq(framework_exceptions) }
|
26
|
+
specify { expect(subject.filter_parameters).to eq([]) }
|
27
|
+
specify { expect(subject.format).to eq(:simple) }
|
11
28
|
specify { expect(subject.host).to eq(hostname_anchor) }
|
12
29
|
specify { expect(subject.level).to eq(:info) }
|
13
|
-
specify { expect(subject.
|
30
|
+
specify { expect(subject.service_name).to eq('hello_world_app') }
|
31
|
+
specify { expect(subject.service_version).to eq('unknown.sha') }
|
14
32
|
specify { expect(subject.sync).to eq(true) }
|
15
|
-
specify { expect(subject.
|
16
|
-
specify { expect(subject.service_name).to eq(nil) }
|
17
|
-
specify { expect(subject.service_version).to eq(:git) }
|
33
|
+
specify { expect(subject.tags).to eq([]) }
|
18
34
|
end
|
19
35
|
|
20
|
-
|
21
|
-
|
22
|
-
|
36
|
+
describe 'device' do
|
37
|
+
context 'when initialized with nil' do
|
38
|
+
let(:options) { super().merge(device: nil) }
|
39
|
+
|
40
|
+
it 'raises an error' do
|
41
|
+
expect { described_class.new(options) }
|
42
|
+
.to raise_error(Loga::ConfigurationError, 'Device cannot be blank')
|
43
|
+
end
|
23
44
|
end
|
45
|
+
end
|
24
46
|
|
25
|
-
|
26
|
-
|
47
|
+
describe 'hostname' do
|
48
|
+
context 'when hostname cannot be resolved' do
|
49
|
+
before do
|
50
|
+
allow(Socket).to receive(:gethostname).and_raise(SystemCallError, 'Something')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'uses a default hostname' do
|
54
|
+
expect(subject.host).to eq('unknown.host')
|
55
|
+
end
|
27
56
|
end
|
28
57
|
end
|
29
|
-
end
|
30
58
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
59
|
+
describe 'service_name' do
|
60
|
+
context 'when service name is missing' do
|
61
|
+
let(:options) do
|
62
|
+
{ service_: 'hello_world_app' }
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'raises an error' do
|
66
|
+
expect { subject }.to raise_error(Loga::ConfigurationError,
|
67
|
+
'Service name cannot be blank')
|
68
|
+
end
|
36
69
|
end
|
37
70
|
end
|
38
71
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
72
|
+
describe 'service_version' do
|
73
|
+
context 'when service version is missing' do
|
74
|
+
it 'uses a service version strategy' do
|
75
|
+
expect(subject.service_version).to eq('unknown.sha')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
context 'when initialized via user options' do
|
79
|
+
let(:options) { super().merge(service_version: 'v3.0.1') }
|
80
|
+
|
81
|
+
it 'sets the service version' do
|
82
|
+
expect(subject.service_version).to eq('v3.0.1')
|
83
|
+
end
|
84
|
+
end
|
45
85
|
end
|
46
86
|
|
47
|
-
describe '
|
48
|
-
|
87
|
+
describe 'format' do
|
88
|
+
context 'when initialized via user options' do
|
89
|
+
let(:options) { super().merge(format: :gelf) }
|
49
90
|
|
50
|
-
|
91
|
+
it 'sets the format' do
|
92
|
+
expect(subject.format).to eq(:gelf)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when initialized via ENV' do
|
51
97
|
before do
|
52
|
-
|
53
|
-
allow(STDERR).to receive(:write)
|
98
|
+
allow(ENV).to receive(:[]).with('LOGA_FORMAT').and_return('gelf')
|
54
99
|
end
|
55
|
-
|
56
|
-
it '
|
57
|
-
subject.
|
58
|
-
expect(logdev.dev).to eq(STDERR)
|
100
|
+
|
101
|
+
it 'sets the format' do
|
102
|
+
expect(subject.format).to eq(:gelf)
|
59
103
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when initialized via framework options' do
|
107
|
+
subject { described_class.new(options, framework_options) }
|
108
|
+
let(:framework_options) { { format: :gelf } }
|
109
|
+
|
110
|
+
it 'sets the format' do
|
111
|
+
expect(subject.format).to eq(:gelf)
|
63
112
|
end
|
64
113
|
end
|
65
114
|
|
115
|
+
context 'when initialized with user options and ENV' do
|
116
|
+
let(:options) { super().merge(format: :gelf) }
|
117
|
+
|
118
|
+
before do
|
119
|
+
allow(ENV).to receive(:[]).with('LOGA_FORMAT').and_return('simple')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'prefers user option' do
|
123
|
+
expect(subject.format).to eq(:gelf)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'when initialized with ENV and framework options' do
|
128
|
+
subject { described_class.new(options, framework_options) }
|
129
|
+
let(:framework_options) { { format: :gelf } }
|
130
|
+
|
131
|
+
before do
|
132
|
+
allow(ENV).to receive(:[]).with('LOGA_FORMAT').and_return('simple')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'prefers env' do
|
136
|
+
expect(subject.format).to eq(:simple)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe 'formatter' do
|
142
|
+
context 'when format is :gelf' do
|
143
|
+
let(:options) do
|
144
|
+
super().merge(
|
145
|
+
format: :gelf,
|
146
|
+
service_name: ' hello_world_app ',
|
147
|
+
service_version_strategies: ['1.0'],
|
148
|
+
)
|
149
|
+
end
|
150
|
+
let(:formatter) { subject.logger.formatter }
|
151
|
+
|
152
|
+
it 'uses the GELF formatter' do
|
153
|
+
expect(subject.logger.formatter).to be_a(Loga::Formatter)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'strips the service name' do
|
157
|
+
expect(formatter.instance_variable_get(:@service_name)).to eq('hello_world_app')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when format is :simple' do
|
162
|
+
let(:options) { super().merge(format: :simple) }
|
163
|
+
|
164
|
+
it 'uses the SimpleFormatter' do
|
165
|
+
expect(subject.logger.formatter).to be_a(ActiveSupport::Logger::SimpleFormatter)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'when the ActiveSupport::VERSION is unsupported' do
|
170
|
+
it 'raises an error' do
|
171
|
+
stub_const('ActiveSupport::VERSION::MAJOR', 1)
|
172
|
+
expect { described_class.new(options) }
|
173
|
+
.to raise_error(Loga::ConfigurationError, 'ActiveSupport 1 is unsupported')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'logger' do
|
179
|
+
let(:logdev) { subject.logger.instance_variable_get(:@logdev) }
|
180
|
+
|
66
181
|
{
|
67
182
|
debug: 0,
|
68
183
|
info: 1,
|
@@ -72,44 +187,35 @@ describe Loga::Configuration do
|
|
72
187
|
unknown: 5,
|
73
188
|
}.each do |sym, level|
|
74
189
|
context "when log level is #{sym}" do
|
75
|
-
|
190
|
+
let(:options) { super().merge(level: sym) }
|
191
|
+
|
76
192
|
it "uses log level #{sym}" do
|
77
|
-
subject.initialize!
|
78
193
|
expect(subject.logger.level).to eq(level)
|
79
194
|
end
|
80
195
|
end
|
81
196
|
end
|
82
197
|
|
83
198
|
context 'when sync is false' do
|
84
|
-
|
199
|
+
let(:options) { super().merge(sync: false) }
|
200
|
+
|
85
201
|
it 'uses warn log level' do
|
86
|
-
subject.initialize!
|
87
202
|
expect(logdev.dev.sync).to eq(false)
|
88
203
|
end
|
89
204
|
end
|
90
205
|
end
|
91
|
-
end
|
92
206
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
it 'returns a logger' do
|
97
|
-
expect(subject.logger).to be_a(Logger)
|
98
|
-
end
|
207
|
+
describe '#structured?' do
|
208
|
+
context 'when format is :simple' do
|
209
|
+
let(:options) { super().merge(format: :simple) }
|
99
210
|
|
100
|
-
|
101
|
-
expect(subject.logger).to respond_to(:tagged)
|
211
|
+
specify { expect(subject.structured?).to eql(false) }
|
102
212
|
end
|
103
|
-
end
|
104
213
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
214
|
+
context 'when format is :gelf' do
|
215
|
+
let(:options) { super().merge(format: :gelf) }
|
109
216
|
|
110
|
-
|
111
|
-
|
112
|
-
expect { |b| subject.configure(&b) }.to yield_with_args(subject)
|
217
|
+
specify { expect(subject.structured?).to eql(true) }
|
218
|
+
end
|
113
219
|
end
|
114
220
|
end
|
115
221
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe Loga::Event do
|
3
|
+
RSpec.describe Loga::Event, timecop: true do
|
4
4
|
describe 'initialize' do
|
5
5
|
context 'no message is passed' do
|
6
6
|
it 'sets message to an empty string' do
|
@@ -17,4 +17,34 @@ RSpec.describe Loga::Event do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
describe '#to_s' do
|
22
|
+
let(:opts) { { message: 'Hello World', timestamp: Time.now } }
|
23
|
+
subject { described_class.new(opts) }
|
24
|
+
|
25
|
+
context 'when exception' do
|
26
|
+
let(:exception) do
|
27
|
+
instance_double(StandardError, to_s: 'Some Message', backtrace: ['file'])
|
28
|
+
end
|
29
|
+
let(:opts) { super().merge(exception: exception) }
|
30
|
+
it 'outputs the message with exception' do
|
31
|
+
expect(subject.to_s)
|
32
|
+
.to eql("#{time_anchor.iso8601(3)} Hello World\nSome Message\nfile")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when no exception' do
|
37
|
+
it 'outputs the message' do
|
38
|
+
expect(subject.to_s).to eql("#{time_anchor.iso8601(3)} Hello World")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#inspect' do
|
44
|
+
subject { described_class.new message: 'Hey Siri', timestamp: Time.now }
|
45
|
+
|
46
|
+
it 'aliases to to_s' do
|
47
|
+
expect(subject.to_s).to eql(subject.inspect)
|
48
|
+
end
|
49
|
+
end
|
20
50
|
end
|
@@ -5,14 +5,22 @@ describe Loga::Rack::Logger do
|
|
5
5
|
let(:env) { Rack::MockRequest.env_for('/about_us?limit=1', options) }
|
6
6
|
let(:options) { {} }
|
7
7
|
let(:app) { ->(_env) { [response_status, {}, ''] } }
|
8
|
-
let(:logger) {
|
8
|
+
let(:logger) { instance_double(Logger, info: nil, error: nil) }
|
9
|
+
let(:tags) { [] }
|
10
|
+
|
11
|
+
let(:configuration) do
|
12
|
+
instance_double(
|
13
|
+
Loga::Configuration,
|
14
|
+
filter_exceptions: %w(ActionController::RoutingError),
|
15
|
+
filter_parameters: [],
|
16
|
+
logger: logger,
|
17
|
+
tags: tags,
|
18
|
+
)
|
19
|
+
end
|
9
20
|
|
10
|
-
subject { described_class.new(app
|
21
|
+
subject { described_class.new(app) }
|
11
22
|
|
12
|
-
before
|
13
|
-
allow(logger).to receive(:info)
|
14
|
-
allow(logger).to receive(:error)
|
15
|
-
end
|
23
|
+
before { Loga.instance_variable_set(:@configuration, configuration) }
|
16
24
|
|
17
25
|
shared_examples 'logs the event' do |details|
|
18
26
|
let(:level) { details[:level] }
|
@@ -103,11 +111,15 @@ describe Loga::Rack::Logger do
|
|
103
111
|
end
|
104
112
|
end
|
105
113
|
|
106
|
-
|
107
|
-
|
108
|
-
|
114
|
+
context 'when tags are present' do
|
115
|
+
let(:tags) { [:foo] }
|
116
|
+
|
117
|
+
it 'yields the app with tags' do
|
118
|
+
expect(logger).to receive(:tagged).with(:tag) do |&block|
|
119
|
+
expect(block.call).to eq(:response)
|
120
|
+
end
|
121
|
+
subject.call(env)
|
109
122
|
end
|
110
|
-
subject.call(env)
|
111
123
|
end
|
112
124
|
end
|
113
125
|
end
|
@@ -6,13 +6,23 @@ describe Loga::Rack::Request do
|
|
6
6
|
let(:env) { Rack::MockRequest.env_for(full_path, options) }
|
7
7
|
|
8
8
|
let(:action_controller_class) do
|
9
|
-
|
9
|
+
Class.new do
|
10
|
+
def self.name
|
11
|
+
'ApplicationController'
|
12
|
+
end
|
13
|
+
|
10
14
|
def action_name
|
11
15
|
'index'
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
20
|
+
let(:config) { instance_double Loga::Configuration, filter_parameters: [:password] }
|
21
|
+
|
22
|
+
before do
|
23
|
+
allow(Loga).to receive(:configuration).and_return(config)
|
24
|
+
end
|
25
|
+
|
16
26
|
subject { described_class.new(env) }
|
17
27
|
|
18
28
|
describe '#uuid' do
|
@@ -34,35 +44,24 @@ describe Loga::Rack::Request do
|
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
37
|
-
describe '#
|
38
|
-
|
39
|
-
|
40
|
-
context 'when ACTION_CONTROLLER_INSTANCE is present' do
|
47
|
+
describe '#controller_action_name' do
|
48
|
+
context 'when available' do
|
41
49
|
let(:options) do
|
42
|
-
{ 'action_controller.instance' =>
|
50
|
+
{ 'action_controller.instance' => action_controller_class.new }
|
43
51
|
end
|
44
|
-
|
45
|
-
|
52
|
+
|
53
|
+
it 'returns the namespaced controller name with the action_name' do
|
54
|
+
expect(subject.controller_action_name).to eq('ApplicationController#index')
|
46
55
|
end
|
47
56
|
end
|
48
57
|
|
49
|
-
context 'when
|
58
|
+
context 'when missing' do
|
50
59
|
it 'returns nil' do
|
51
|
-
expect(subject.
|
60
|
+
expect(subject.controller_action_name).to be_nil
|
52
61
|
end
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
56
|
-
describe '#action_controller' do
|
57
|
-
let(:options) do
|
58
|
-
{ 'action_controller.instance' => action_controller_class.new }
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'returns the controller with the action_name' do
|
62
|
-
expect(subject.action_controller).to eq('ApplicationController#index')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
65
|
describe '#request_id' do
|
67
66
|
let(:action_dispatch_request_id) { 'ABCD' }
|
68
67
|
let(:options) do
|
@@ -84,8 +83,6 @@ describe Loga::Rack::Request do
|
|
84
83
|
end
|
85
84
|
|
86
85
|
describe '#filtered_full_path' do
|
87
|
-
let(:config) { double :config, filter_parameters: [:password] }
|
88
|
-
|
89
86
|
let(:path) { '/hello' }
|
90
87
|
let(:query) { { 'password' => 123, 'color' => 'red' } }
|
91
88
|
let(:query_string) { Rack::Utils.build_query(query) }
|
@@ -93,10 +90,6 @@ describe Loga::Rack::Request do
|
|
93
90
|
|
94
91
|
let(:options) { { 'loga.request.original_path' => path } }
|
95
92
|
|
96
|
-
before do
|
97
|
-
allow(Loga).to receive(:configuration).and_return(config)
|
98
|
-
end
|
99
|
-
|
100
93
|
context 'request with sensitive parameters' do
|
101
94
|
it 'returns the path with sensitive parameters filtered' do
|
102
95
|
expect(subject.filtered_full_path).to eq('/hello?password=[FILTERED]&color=red')
|