loga 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,68 +1,183 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Loga::Configuration do
4
- subject do
5
- described_class.new.tap { |config| config.device = STDOUT }
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
- subject { described_class.new }
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.device).to eq(nil) }
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.filter_parameters).to eq([]) }
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
- context 'when hostname cannot be resolved' do
21
- before do
22
- allow(Socket).to receive(:gethostname).and_raise(Exception)
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
- it 'uses a default hostname' do
26
- expect(subject.host).to eq('unknown.host')
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
- describe '#initialize!' do
32
- before do
33
- subject.tap do |config|
34
- config.service_name = ' hello_world_app '
35
- config.service_version = " 1.0\n"
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
- it 'initializes the formatter with stiped service name and version' do
40
- expect(Loga::Formatter).to receive(:new)
41
- .with(service_name: 'hello_world_app',
42
- service_version: '1.0',
43
- host: hostname_anchor)
44
- subject.initialize!
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 'logger' do
48
- let(:logdev) { subject.logger.instance_variable_get(:@logdev) }
87
+ describe 'format' do
88
+ context 'when initialized via user options' do
89
+ let(:options) { super().merge(format: :gelf) }
49
90
 
50
- context 'when device is nil' do
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
- subject.device = nil
53
- allow(STDERR).to receive(:write)
98
+ allow(ENV).to receive(:[]).with('LOGA_FORMAT').and_return('gelf')
54
99
  end
55
- let(:error_message) { /Loga could not be initialized/ }
56
- it 'uses STDERR' do
57
- subject.initialize!
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
- it 'logs an error to STDERR' do
61
- expect(STDERR).to receive(:write).with(error_message)
62
- subject.initialize!
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
- before { subject.level = sym }
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
- before { subject.sync = false }
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
- describe '#logger' do
94
- context 'when initialized' do
95
- before { subject.initialize! }
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
- it 'returns a tagged logger' do
101
- expect(subject.logger).to respond_to(:tagged)
211
+ specify { expect(subject.structured?).to eql(false) }
102
212
  end
103
- end
104
213
 
105
- context 'when not initialized' do
106
- specify { expect(subject.logger).to be_nil }
107
- end
108
- end
214
+ context 'when format is :gelf' do
215
+ let(:options) { super().merge(format: :gelf) }
109
216
 
110
- describe '#configure' do
111
- it 'yields self' do
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) { double(: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, logger) }
21
+ subject { described_class.new(app) }
11
22
 
12
- before do
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
- it 'yields the app with tags' do
107
- expect(logger).to receive(:tagged).with(:tag) do |&block|
108
- expect(block.call).to eq(:response)
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
- ApplicationController = Class.new do
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 '#action_controller_instance' do
38
- let(:action_controller_instance) { action_controller_class.new }
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' => action_controller_instance }
50
+ { 'action_controller.instance' => action_controller_class.new }
43
51
  end
44
- it 'returns the instance' do
45
- expect(subject.action_controller_instance).to eq(action_controller_instance)
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 ACTION_DISPATCH_REQUEST_ID blank' do
58
+ context 'when missing' do
50
59
  it 'returns nil' do
51
- expect(subject.action_controller_instance).to be_nil
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')