berkeley_library-logging 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +388 -0
  3. data/.idea/inspectionProfiles/Project_Default.xml +17 -0
  4. data/.idea/logging.iml +139 -0
  5. data/.idea/modules.xml +8 -0
  6. data/.idea/vcs.xml +6 -0
  7. data/.rubocop.yml +271 -0
  8. data/.ruby-version +1 -0
  9. data/.simplecov +4 -0
  10. data/.travis.yml +9 -0
  11. data/CHANGES.md +12 -0
  12. data/Dockerfile +57 -0
  13. data/Gemfile +3 -0
  14. data/Jenkinsfile +21 -0
  15. data/LICENSE.md +21 -0
  16. data/README.md +47 -0
  17. data/Rakefile +31 -0
  18. data/berkeley_library-logging.gemspec +47 -0
  19. data/docker-compose.yml +15 -0
  20. data/lib/berkeley_library/logging.rb +38 -0
  21. data/lib/berkeley_library/logging/configurator.rb +41 -0
  22. data/lib/berkeley_library/logging/env.rb +38 -0
  23. data/lib/berkeley_library/logging/events.rb +39 -0
  24. data/lib/berkeley_library/logging/formatters.rb +54 -0
  25. data/lib/berkeley_library/logging/logger.rb +12 -0
  26. data/lib/berkeley_library/logging/loggers.rb +79 -0
  27. data/lib/berkeley_library/logging/module_info.rb +16 -0
  28. data/lib/berkeley_library/logging/railtie.rb +15 -0
  29. data/lib/berkeley_library/logging/tagged_logging_extensions.rb +21 -0
  30. data/rakelib/.rubocop.yml +4 -0
  31. data/rakelib/bundle.rake +8 -0
  32. data/rakelib/coverage.rake +36 -0
  33. data/rakelib/gem.rake +56 -0
  34. data/rakelib/rubocop.rake +14 -0
  35. data/rakelib/spec.rake +31 -0
  36. data/spec/.rubocop.yml +27 -0
  37. data/spec/rails/ucblit/logging/configurator_spec.rb +101 -0
  38. data/spec/rails/ucblit/logging/env_spec.rb +25 -0
  39. data/spec/rails/ucblit/logging/formatters_spec.rb +44 -0
  40. data/spec/rails/ucblit/logging/loggers_spec.rb +117 -0
  41. data/spec/rails/ucblit/logging/railtie_spec.rb +46 -0
  42. data/spec/rails/ucblit/logging_spec.rb +132 -0
  43. data/spec/rails_helper.rb +15 -0
  44. data/spec/spec_helper.rb +34 -0
  45. data/spec/standalone/ucblit/logging/configurator_spec.rb +103 -0
  46. data/spec/standalone/ucblit/logging/formatters_spec.rb +44 -0
  47. data/spec/standalone/ucblit/logging/loggers_spec.rb +278 -0
  48. data/spec/standalone/ucblit/logging_spec.rb +133 -0
  49. data/spec/standalone_helper.rb +25 -0
  50. metadata +363 -0
@@ -0,0 +1,46 @@
1
+ require 'rails_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Logging
5
+ describe Railtie do
6
+ attr_reader :app
7
+ attr_reader :config
8
+
9
+ before(:each) do
10
+ @app = Class.new(Rails::Application).new
11
+ allow(Rails).to receive(:application).and_return(app)
12
+ @config = app.config
13
+ end
14
+
15
+ describe 'initializer' do
16
+ attr_reader :logging_initializer
17
+ attr_reader :bootstrap_logger_initializer
18
+
19
+ before(:each) do
20
+ expected_file, _line = Module.const_source_location(BerkeleyLibrary::Logging::Railtie.name)
21
+ @logging_initializer = app.initializers.find do |init|
22
+ block = init.block
23
+ file, _line = block.source_location
24
+ file == expected_file
25
+ end
26
+ @bootstrap_logger_initializer = app.initializers.find { |init| init.name == :initialize_logger }
27
+ end
28
+
29
+ it 'is added to the Rails application' do
30
+ expect(logging_initializer).not_to be_nil
31
+ end
32
+
33
+ it 'runs before the bootstrap logger initializer' do
34
+ expect(logging_initializer.before).to eq(bootstrap_logger_initializer.name)
35
+ end
36
+
37
+ it 'sets the logger' do
38
+ logging_initializer.run(app)
39
+ bootstrap_logger_initializer.run(app)
40
+
41
+ expect(Rails.logger).to be_a(BerkeleyLibrary::Logging::Logger)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ require 'rails_helper'
2
+
3
+ module BerkeleyLibrary
4
+ describe Logging do
5
+
6
+ def logger_defined?
7
+ Logging.instance_variable_defined?(:@logger)
8
+ end
9
+
10
+ def reset_logger!
11
+ Logging.instance_variable_set(:@logger, @logging_logger_orig)
12
+ end
13
+
14
+ def undefine_logger!
15
+ Logging.send(:remove_instance_variable, :@logger) if logger_defined?
16
+ end
17
+
18
+ def new_mock_logger
19
+ mock_logger_class = Class.new do
20
+ def respond_to?(*args)
21
+ %i[debug info warn error].include?(args[0].to_sym) || super
22
+ end
23
+ end
24
+
25
+ mock_logger_class.new
26
+ end
27
+
28
+ before(:each) do
29
+ @rails_logger_orig = Rails.logger
30
+ Rails.logger = new_mock_logger
31
+
32
+ if (@logger_was_defined = logger_defined?)
33
+ @logging_logger_orig = Logging.instance_variable_get(:@logger)
34
+ undefine_logger!
35
+ end
36
+ end
37
+
38
+ after(:each) do
39
+ @logger_was_defined ? reset_logger! : undefine_logger!
40
+ Rails.logger = @rails_logger_orig
41
+ end
42
+
43
+ describe 'class methods' do
44
+ describe :logger do
45
+ it 'returns the default logger' do
46
+ logger = Logging::Loggers.default_logger
47
+ expect(Logging.logger).to be(logger)
48
+ end
49
+
50
+ it 'returns a set logger' do
51
+ logger = new_mock_logger
52
+ Logging.logger = logger
53
+ expect(Logging.logger).to be(logger)
54
+ end
55
+
56
+ end
57
+
58
+ describe :logger= do
59
+ it 'rejects a non-logger' do
60
+ original_logger = Logging.logger
61
+ expect { Logging.logger = Object.new }.to raise_error(ArgumentError)
62
+ expect(Logging.logger).to be(original_logger)
63
+ end
64
+
65
+ it 'can be reset to default with nil' do
66
+ default_logger = Logging::Loggers.default_logger
67
+ Logging.logger = new_mock_logger
68
+ Logging.logger = nil
69
+ expect(Logging.logger).to be(default_logger)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe 'included' do
75
+ attr_reader :logificator
76
+
77
+ before(:each) do
78
+ @logificator = Object.new
79
+ @logificator.singleton_class.include(Logging)
80
+ end
81
+
82
+ describe :logger do
83
+ it 'returns the default logger' do
84
+ logger = Logging::Loggers.default_logger
85
+ expect(logificator.logger).to be(logger)
86
+ end
87
+
88
+ it 'returns a set logger' do
89
+ logger = new_mock_logger
90
+ logificator.logger = logger
91
+ expect(logificator.logger).to be(logger)
92
+ end
93
+
94
+ it 'returns a logger set via the class method' do
95
+ logger = new_mock_logger
96
+ Logging.logger = logger
97
+ expect(logificator.logger).to be(logger)
98
+ end
99
+ end
100
+
101
+ describe :logger= do
102
+ it 'sets the shared logger via the class method' do
103
+ logger = new_mock_logger
104
+ logificator.logger = logger
105
+ expect(Logging.logger).to be(logger)
106
+ end
107
+
108
+ it 'sets a shared logger accessible via another including class' do
109
+ logger = new_mock_logger
110
+ Object.new.tap do |logificator2|
111
+ logificator2.singleton_class.send(:include, Logging)
112
+ logificator2.logger = logger
113
+ end
114
+ expect(logificator.logger).to be(logger)
115
+ end
116
+
117
+ it 'rejects a non-logger' do
118
+ original_logger = logificator.logger
119
+ expect { logificator.logger = Object.new }.to raise_error(ArgumentError)
120
+ expect(logificator.logger).to be(original_logger)
121
+ end
122
+
123
+ it 'can be reset to default with nil' do
124
+ default_logger = Logging::Loggers.default_logger
125
+ logificator.logger = new_mock_logger
126
+ logificator.logger = nil
127
+ expect(logificator.logger).to be(default_logger)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,15 @@
1
+ # ------------------------------------------------------------
2
+ # RSpec
3
+
4
+ require 'spec_helper'
5
+
6
+ # ------------------------------------------------------------
7
+ # Rails
8
+
9
+ require 'rails'
10
+ Rails.env = 'test'
11
+
12
+ # ------------------------------------------------------------
13
+ # Code under test
14
+
15
+ require 'berkeley_library/logging'
@@ -0,0 +1,34 @@
1
+ # ------------------------------------------------------------
2
+ # SimpleCov
3
+
4
+ if ENV['COVERAGE']
5
+ require 'simplecov'
6
+
7
+ spec_root = File.realpath(__dir__)
8
+ spec_group_re = %r{(?<=^#{spec_root}/)[^/]+(?=/)}
9
+
10
+ RSpec.configure do |config|
11
+ config.before(:each) do |example|
12
+ abs_path = File.realpath(example.metadata[:absolute_file_path])
13
+ match_data = spec_group_re.match(abs_path)
14
+ raise ArgumentError, "Unable to determine group for example at #{abs_path}" unless match_data
15
+
16
+ spec_group = match_data[0]
17
+ SimpleCov.command_name(spec_group)
18
+ SimpleCov.coverage_dir("artifacts/simplecov/#{spec_group}")
19
+ end
20
+ end
21
+ end
22
+
23
+ # ------------------------------------------------------------
24
+ # RSpec
25
+
26
+ RSpec.configure do |config|
27
+ config.color = true
28
+ config.tty = true
29
+ config.formatter = :documentation
30
+ config.mock_with :rspec do |mocks|
31
+ mocks.verify_partial_doubles = true
32
+ end
33
+ config.shared_context_metadata_behavior = :apply_to_host_groups
34
+ end
@@ -0,0 +1,103 @@
1
+ require 'standalone_helper'
2
+ require 'ostruct'
3
+ require 'lograge'
4
+
5
+ module BerkeleyLibrary
6
+ module Logging
7
+ describe Configurator do
8
+ describe :configure! do
9
+ attr_reader :config
10
+
11
+ before(:each) do
12
+ @stdout_orig = $stdout
13
+ $stdout = StringIO.new
14
+
15
+ @config = OpenStruct.new
16
+ config.lograge = Lograge::OrderedOptions.new
17
+ end
18
+
19
+ after(:each) do
20
+ $stdout = @stdout_orig
21
+ end
22
+
23
+ it 'sets the logger' do
24
+ Configurator.configure(config)
25
+ expect(config.logger).to be_a(Logging::Logger)
26
+ end
27
+
28
+ describe :configure_lograge! do
29
+ it 'enables Lograge' do
30
+ Configurator.configure(config)
31
+ lograge = config.lograge
32
+ expect(lograge.enabled).to eq(true)
33
+ end
34
+
35
+ it 'extracts request info from log events' do
36
+ Configurator.configure(config)
37
+ lograge = config.lograge
38
+
39
+ request_headers = {
40
+ 'HTTP_REFERER' => 'value from HTTP_REFERER',
41
+ 'action_dispatch.request_id' => 'value from action_dispatch.request_id',
42
+ 'action_dispatch.remote_ip' => 'value from action_dispatch.remote_ip',
43
+ 'REMOTE_ADDR' => 'value from REMOTE_ADDR',
44
+ 'HTTP_X_FORWARDED_FOR' => 'value from HTTP_X_FORWARDED_FOR',
45
+ 'HTTP_FORWARDED' => 'value from HTTP_FORWARDED'
46
+ }
47
+
48
+ expected_header_map = {
49
+ referer: 'HTTP_REFERER',
50
+ request_id: 'action_dispatch.request_id',
51
+ remote_ip: 'action_dispatch.remote_ip',
52
+ remote_addr: 'REMOTE_ADDR',
53
+ x_forwarded_for: 'HTTP_X_FORWARDED_FOR',
54
+ forwarded: 'HTTP_FORWARDED'
55
+ }
56
+
57
+ event = instance_double(ActiveSupport::Notifications::Event)
58
+ allow(event).to receive(:payload).and_return({ headers: request_headers })
59
+
60
+ custom_options = lograge.custom_options
61
+ data = custom_options.call(event)
62
+ expect(data).to be_a(Hash)
63
+ expect(data[:time]).to be_a(Time) # TODO: check for accuracy
64
+ expected_header_map.each { |xh, rh| expect(data[xh]).to eq(request_headers[rh]) }
65
+ end
66
+
67
+ it 'formats Lograge data as a hash' do
68
+ Configurator.configure(config)
69
+ lograge = config.lograge
70
+
71
+ formatter = lograge.formatter
72
+ expect(formatter.call(nil)).to eq({ msg: 'Request', request: {} })
73
+ expect(formatter.call('elvis')).to eq({ msg: 'Request', request: { msg: 'elvis' } })
74
+ some_hash = { foo: 'bar' }
75
+ expect(formatter.call(some_hash)).to eq({ msg: 'Request', request: some_hash })
76
+ end
77
+ end
78
+
79
+ describe 'Webpacker' do
80
+ it 'works if Webpacker is not present' do
81
+ expect(Object.const_defined?(:Webpacker)).to eq(false) # just to be sure
82
+ expect { Configurator.configure(config) }.not_to raise_error
83
+ end
84
+
85
+ it 'sets the Webpacker logger if Webpacker is present' do
86
+ module ::Webpacker
87
+ module Instance
88
+ class << self
89
+ attr_accessor :logger
90
+ end
91
+ end
92
+ end
93
+
94
+ Configurator.configure(config)
95
+ expect(Webpacker::Instance.logger).to eq(config.logger)
96
+ ensure
97
+ Object.send(:remove_const, :Webpacker) if defined?(Webpacker)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,44 @@
1
+ require 'standalone_helper'
2
+ require 'json'
3
+ require 'berkeley_library/logging'
4
+
5
+ module BerkeleyLibrary
6
+ module Logging
7
+ describe Formatters do
8
+ describe :new_json_formatter do
9
+ it 'supports tagged logging' do
10
+ out = StringIO.new
11
+ logger = Logger.new(out)
12
+ logger.formatter = Formatters.new_json_formatter
13
+
14
+ logger = ActiveSupport::TaggedLogging.new(logger)
15
+
16
+ expected_tag = 'hello'
17
+ expected_msg = 'this is a test'
18
+
19
+ logger.tagged(expected_tag) { logger.info(expected_msg) }
20
+
21
+ logged_json = JSON.parse(out.string)
22
+ expect(logged_json['msg']).to eq(expected_msg)
23
+ expect(logged_json['tags']).to eq([expected_tag])
24
+ end
25
+ end
26
+
27
+ describe :ensure_hash do
28
+ it 'returns an empty hash for nil' do
29
+ expect(Formatters.ensure_hash(nil)).to eq({})
30
+ end
31
+
32
+ it 'returns the original hash for a hash' do
33
+ original_hash = { a: 1, b: 2 }
34
+ expect(Formatters.ensure_hash(original_hash)).to equal(original_hash)
35
+ end
36
+
37
+ it 'wraps anything else in a hash' do
38
+ message = 'this is a message'
39
+ expect(Formatters.ensure_hash(message)).to eq({ msg: message })
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,278 @@
1
+ require 'standalone_helper'
2
+ require 'json'
3
+
4
+ module BerkeleyLibrary
5
+ module Logging
6
+ describe Loggers do
7
+ attr_reader :out
8
+
9
+ # rubocop:disable Lint/ConstantDefinitionInBlock
10
+ before(:each) do
11
+ @out = StringIO.new
12
+ class ::TestError < StandardError; end
13
+ end
14
+ # rubocop:enable Lint/ConstantDefinitionInBlock
15
+
16
+ after(:each) do
17
+ Object.send(:remove_const, :TestError)
18
+ end
19
+
20
+ describe :new_json_logger do
21
+ it 'supports tagged logging' do
22
+ logger = Loggers.new_json_logger(out)
23
+ logger = ActiveSupport::TaggedLogging.new(logger)
24
+
25
+ expected_tag = 'hello'
26
+ expected_msg = 'this is a test'
27
+
28
+ logger.tagged(expected_tag) { logger.info(expected_msg) }
29
+
30
+ logged_json = JSON.parse(out.string)
31
+ expect(logged_json['msg']).to eq(expected_msg)
32
+ expect(logged_json['tags']).to eq([expected_tag])
33
+ end
34
+
35
+ it 'logs an error as a hash' do
36
+ msg = 'Help I am trapped in a unit test'
37
+
38
+ begin
39
+ raise TestError, msg
40
+ rescue TestError => e
41
+ ex = e
42
+ Loggers.new_json_logger(out).error(e)
43
+ end
44
+
45
+ logged_json = JSON.parse(out.string)
46
+ expect(logged_json['msg']).to eq(msg)
47
+ err_json = logged_json['err']
48
+ expect(err_json).to be_a(Hash)
49
+ expect(err_json['name']).to eq(TestError.name)
50
+ expect(err_json['message']).to eq(msg)
51
+
52
+ err_stack = err_json['stack']
53
+ backtrace = ex.backtrace
54
+ expect(backtrace).not_to be_nil # just to be sure
55
+ backtrace.each do |line|
56
+ expect(err_stack).to include(line)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe :default_logger do
62
+ it 'returns a readable $stdout logger' do
63
+ logger = Loggers.default_logger
64
+ expect(logger).to be_a(Logger)
65
+ expect(logger.formatter).to be_a(Ougai::Formatters::Readable)
66
+
67
+ logdev = logger.instance_variable_get(:@logdev)
68
+ expect(logdev).to be_a(::Logger::LogDevice)
69
+ expect(logdev.dev).to eq($stdout)
70
+ end
71
+
72
+ describe 'errors' do
73
+ it 'logs an error alone with cause and backtrace' do
74
+ msg = 'Help I am trapped in a unit test'
75
+
76
+ begin
77
+ raise TestError, msg
78
+ rescue TestError => e
79
+ ex = e
80
+ Loggers.new_readable_logger(out).error(e)
81
+ end
82
+
83
+ logged_txt = out.string
84
+ expect(logged_txt).to include(msg)
85
+ expect(logged_txt).to include(TestError.name)
86
+ backtrace = ex.backtrace
87
+ expect(backtrace).not_to be_nil # just to be sure
88
+ backtrace.each do |line|
89
+ expect(logged_txt).to include(line)
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ describe 'messages with text and data' do
96
+ it 'logs an arbitrary hash in a reasonable way' do
97
+ out = StringIO.new
98
+ msg_txt = 'message text'
99
+ msg_h = {
100
+ foo: 'Foo',
101
+ bar: 'Bar',
102
+ baz: 'Baz'
103
+ }
104
+ Loggers.new_readable_logger(out).info(msg_txt, msg_h)
105
+
106
+ logged_txt = out.string
107
+ expect(logged_txt).to include(msg_txt)
108
+ msg_h.each do |k, v|
109
+ expect(logged_txt).to include(k.inspect)
110
+ expect(logged_txt).to include(v.inspect)
111
+ end
112
+ end
113
+
114
+ it 'logs something with #to_hash as a hash' do
115
+ out = StringIO.new
116
+ msg_txt = 'message text'
117
+ msg_h = {
118
+ foo: 'Foo',
119
+ bar: 'Bar',
120
+ baz: 'Baz'
121
+ }
122
+ msg_obj = Object.new
123
+ msg_obj.singleton_class.define_method(:to_hash) { msg_h }
124
+
125
+ Loggers.new_readable_logger(out).info(msg_txt, msg_obj)
126
+
127
+ logged_txt = out.string
128
+ expect(logged_txt).to include(msg_txt)
129
+ msg_h.each do |k, v|
130
+ expect(logged_txt).to include(k.inspect)
131
+ expect(logged_txt).to include(v.inspect)
132
+ end
133
+ end
134
+
135
+ it 'logs an error with cause and backtrace' do
136
+ msg_txt = 'message text'
137
+ ex_msg = 'Help I am trapped in a unit test'
138
+
139
+ begin
140
+ raise TestError, ex_msg
141
+ rescue TestError => e
142
+ ex = e
143
+ Loggers.new_readable_logger(out).error(msg_txt, e)
144
+ end
145
+
146
+ logged_txt = out.string
147
+ expect(logged_txt).to include(msg_txt)
148
+ expect(logged_txt).to include(ex_msg)
149
+ expect(logged_txt).to include(TestError.name)
150
+ backtrace = ex.backtrace
151
+ expect(backtrace).not_to be_nil # just to be sure
152
+ backtrace.each do |line|
153
+ expect(logged_txt).to include(line)
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ describe 'messages with data and no text' do
160
+ it 'logs an arbitrary hash in a reasonable way' do
161
+ out = StringIO.new
162
+ msg_h = {
163
+ foo: 'Foo',
164
+ bar: 'Bar',
165
+ baz: 'Baz'
166
+ }
167
+ Loggers.new_readable_logger(out).info(msg_h)
168
+ logged_txt = out.string
169
+ msg_h.each do |k, v|
170
+ expect(logged_txt).to include(k.inspect)
171
+ expect(logged_txt).to include(v.inspect)
172
+ end
173
+ end
174
+
175
+ it 'logs something with #to_hash as a hash' do
176
+ out = StringIO.new
177
+ msg_h = {
178
+ foo: 'Foo',
179
+ bar: 'Bar',
180
+ baz: 'Baz'
181
+ }
182
+ msg_obj = Object.new
183
+ msg_obj.singleton_class.define_method(:to_hash) { msg_h }
184
+
185
+ Loggers.new_readable_logger(out).info(msg_obj)
186
+
187
+ logged_txt = out.string
188
+ msg_h.each do |k, v|
189
+ expect(logged_txt).to include(k.inspect)
190
+ expect(logged_txt).to include(v.inspect)
191
+ end
192
+ end
193
+
194
+ it 'logs an error with cause and backtrace' do
195
+ ex_msg = 'Help I am trapped in a unit test'
196
+
197
+ begin
198
+ raise TestError, ex_msg
199
+ rescue TestError => e
200
+ ex = e
201
+ Loggers.new_readable_logger(out).error(e)
202
+ end
203
+
204
+ logged_txt = out.string
205
+ expect(logged_txt).to include(ex_msg)
206
+ expect(logged_txt).to include(TestError.name)
207
+ backtrace = ex.backtrace
208
+ expect(backtrace).not_to be_nil # just to be sure
209
+ backtrace.each do |line|
210
+ expect(logged_txt).to include(line)
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ end
217
+
218
+ describe :new_default_logger do
219
+ attr_reader :config
220
+
221
+ before(:each) do
222
+ @config = OpenStruct.new
223
+ end
224
+
225
+ after(:each) do
226
+ BerkeleyLibrary::Logging.instance_variable_set(:@env, nil)
227
+ end
228
+
229
+ it 'returns a readable $stdout logger if given no config' do
230
+ logger = Loggers.new_default_logger
231
+ expect(logger).not_to be_nil
232
+ expect(logger).to be_a(Logger)
233
+ expect(logger.formatter).to be_a(Ougai::Formatters::Readable)
234
+ end
235
+
236
+ it 'returns a file logger in test' do
237
+ BerkeleyLibrary::Logging.env = 'test'
238
+ logger = Loggers.new_default_logger(config)
239
+ expect(logger).not_to be_nil
240
+ logdev = logger.instance_variable_get(:@logdev)
241
+ expect(logdev.filename).to end_with('log/test.log')
242
+ end
243
+
244
+ it 'returns a stdout logger in production' do
245
+ BerkeleyLibrary::Logging.env = 'production'
246
+ stdout_orig = $stdout
247
+ stdout_tmp = StringIO.new
248
+ begin
249
+ $stdout = stdout_tmp
250
+ logger = Loggers.new_default_logger(config)
251
+ ensure
252
+ $stdout = stdout_orig
253
+ end
254
+ expect(logger).not_to be_nil
255
+ logdev = logger.instance_variable_get(:@logdev)
256
+ expect(logdev.filename).to be_nil
257
+ expect(logdev.dev).to eq(stdout_tmp)
258
+ end
259
+
260
+ it 'returns a stdout logger in development' do
261
+ BerkeleyLibrary::Logging.env = 'development'
262
+ logger = Loggers.new_default_logger(config)
263
+ expect(logger).not_to be_nil
264
+ logdev = logger.instance_variable_get(:@logdev)
265
+ expect(logdev.filename).to be_nil
266
+ expect(logdev.dev).to eq($stdout)
267
+
268
+ # TODO: come up with a succinct way to test broadcast to file
269
+ end
270
+
271
+ it 'fails on an unsupported environment' do
272
+ BerkeleyLibrary::Logging.env = 'some-unsupported-environment'
273
+ expect { Loggers.new_default_logger(config) }.to raise_error(ArgumentError)
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end