berkeley_library-logging 0.2.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.
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,4 @@
1
+ inherit_from: ../.rubocop.yml
2
+
3
+ Metrics/BlockLength:
4
+ Enabled: false
@@ -0,0 +1,8 @@
1
+ namespace :bundle do
2
+ desc 'Check bundled gems for vulnerabilities against the latest ruby-advisory-db'
3
+ task :audit do
4
+ require 'bundler/audit/cli'
5
+ Bundler::Audit::CLI.start ['update']
6
+ Bundler::Audit::CLI.start %w[check --ignore CVE-2015-9284]
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ require 'colorize'
2
+
3
+ namespace :simplecov do
4
+ task :check_coverage do
5
+ ENV['COVERAGE'] ||= 'true'
6
+ Rake::Task['spec'].invoke
7
+ rescue SystemExit
8
+ puts 'Code coverage analysis aborted, probably due to a previous test failure'.colorize(:red)
9
+ raise
10
+ end
11
+
12
+ task :report do
13
+ require 'simplecov'
14
+ require 'simplecov-rcov'
15
+ require 'simplecov-console'
16
+
17
+ result_sets = Dir.glob('artifacts/simplecov/**/.resultset.json')
18
+ SimpleCov.collate(result_sets) do
19
+ minimum_coverage 100
20
+ coverage_dir 'artifacts'
21
+
22
+ if ENV['GENERATE_REPORTS']
23
+ formatters = [
24
+ SimpleCov::Formatter::Console,
25
+ SimpleCov::Formatter::RcovFormatter
26
+ ]
27
+ formatter SimpleCov::Formatter::MultiFormatter.new(formatters)
28
+ else
29
+ formatter SimpleCov::Formatter::Console
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ desc 'Run all specs in spec directory, with coverage'
36
+ task coverage: %w[simplecov:check_coverage simplecov:report]
data/rakelib/gem.rake ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems/gem_runner'
2
+ require 'berkeley_library/logging/module_info'
3
+
4
+ module BerkeleyLibrary
5
+ module Logging
6
+ class << self
7
+ def project_root
8
+ @project_root ||= File.expand_path('..', __dir__)
9
+ end
10
+
11
+ def artifacts_dir
12
+ return project_root unless ENV['CI']
13
+
14
+ @artifacts_dir ||= File.join(project_root, 'artifacts')
15
+ end
16
+
17
+ def gemspec_file
18
+ @gemspec_file ||= begin
19
+ gemspec_files = Dir.glob(File.expand_path('*.gemspec', project_root))
20
+ raise ArgumentError, "Too many .gemspecs: #{gemspec_files.join(', ')}" if gemspec_files.size > 1
21
+ raise ArgumentError, 'No .gemspec file found' if gemspec_files.empty?
22
+
23
+ gemspec_files[0]
24
+ end
25
+ end
26
+
27
+ def gemspec_basename
28
+ File.basename(gemspec_file)
29
+ end
30
+
31
+ def output_file
32
+ @output_file ||= begin
33
+ gem_name = File.basename(gemspec_file, '.*')
34
+ version = BerkeleyLibrary::Logging::ModuleInfo::VERSION
35
+ basename = "#{gem_name}-#{version}.gem"
36
+ File.join(artifacts_dir, basename)
37
+ end
38
+ end
39
+
40
+ def output_file_relative
41
+ return File.basename(output_file) unless ENV['CI']
42
+
43
+ @output_file_relative ||= begin
44
+ artifacts_dir_relative = File.basename(artifacts_dir)
45
+ File.join(artifacts_dir_relative, File.basename(output_file))
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ desc "Build #{BerkeleyLibrary::Logging.gemspec_basename} as #{BerkeleyLibrary::Logging.output_file_relative}"
53
+ task :gem do
54
+ args = ['build', BerkeleyLibrary::Logging.gemspec_file, "--output=#{BerkeleyLibrary::Logging.output_file}"]
55
+ Gem::GemRunner.new.run(args)
56
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubocop'
2
+ require 'rubocop/rake_task'
3
+
4
+ desc 'Run RuboCop'
5
+ RuboCop::RakeTask.new(:rubocop) do |cop|
6
+ next unless ENV['GENERATE_REPORTS']
7
+
8
+ output = ENV['RUBOCOP_OUTPUT'] || 'artifacts/rubocop/index.html'
9
+ puts "Writing RuboCop inspection report to #{output}"
10
+
11
+ cop.verbose = false
12
+ cop.formatters = ['html']
13
+ cop.options = ['--out', output]
14
+ end
data/rakelib/spec.rake ADDED
@@ -0,0 +1,31 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ namespace :spec do
4
+ task :prepare do
5
+ if ENV['GENERATE_REPORTS']
6
+ ENV['CI_REPORTS'] = 'artifacts/rspec'
7
+
8
+ require 'ci/reporter/rake/rspec'
9
+ Rake::Task['ci:setup:rspec'].invoke
10
+ end
11
+ end
12
+
13
+ test_groups = %i[standalone rails].tap do |groups|
14
+ groups.each do |group|
15
+ desc "Run specs in spec/#{group} directory"
16
+ RSpec::Core::RakeTask.new(group) do |task|
17
+ task.rspec_opts = %w[--color --format documentation --order default]
18
+ task.pattern = "spec/#{group}/**/*_spec.rb"
19
+ end
20
+ end
21
+ end
22
+ multitask all: test_groups
23
+ end
24
+
25
+ desc 'Run all specs in spec directory'
26
+ task spec: ['spec:prepare'] do
27
+ Rake::Task['spec:all'].invoke
28
+ ensure
29
+ reports_dir = ENV['CI_REPORTS']
30
+ puts "JUnit-format XML test report written to #{reports_dir}" if reports_dir
31
+ end
data/spec/.rubocop.yml ADDED
@@ -0,0 +1,27 @@
1
+ inherit_from: ../.rubocop.yml
2
+
3
+ Style/ClassAndModuleChildren:
4
+ Enabled: false
5
+
6
+ Layout/LineLength:
7
+ Enabled: false
8
+
9
+ Metrics/BlockLength:
10
+ Enabled: false
11
+
12
+ Metrics/ClassLength:
13
+ Enabled: false
14
+
15
+ Metrics/ModuleLength:
16
+ Enabled: false
17
+
18
+ Metrics/MethodLength:
19
+ Enabled: false
20
+
21
+ ############################################################
22
+ # Added in Rubocop 0.89
23
+
24
+ # Sometimes we're testing the operator
25
+ Lint/BinaryOperatorWithIdenticalOperands:
26
+ Enabled: false
27
+
@@ -0,0 +1,101 @@
1
+ require 'rails_helper'
2
+ require 'rails'
3
+ require 'lograge'
4
+ require 'berkeley_library/logging'
5
+
6
+ module BerkeleyLibrary
7
+ module Logging
8
+ describe Configurator do
9
+ describe :configure! do
10
+
11
+ attr_reader :config
12
+
13
+ before(:each) do
14
+ app = Class.new(Rails::Application).new
15
+ allow(Rails).to receive(:application).and_return(app)
16
+ @config = app.config
17
+
18
+ config.lograge = Lograge::OrderedOptions.new
19
+ end
20
+
21
+ it 'sets the logger' do
22
+ Configurator.configure(config)
23
+ expect(config.logger).to be_a(Logging::Logger)
24
+ end
25
+
26
+ describe :configure_lograge! do
27
+ it 'enables Lograge' do
28
+ Configurator.configure(config)
29
+ lograge = config.lograge
30
+ expect(lograge.enabled).to eq(true)
31
+ end
32
+
33
+ it 'extracts request info from log events' do
34
+ Configurator.configure(config)
35
+ lograge = config.lograge
36
+
37
+ request_headers = {
38
+ 'HTTP_REFERER' => 'value from HTTP_REFERER',
39
+ 'action_dispatch.request_id' => 'value from action_dispatch.request_id',
40
+ 'action_dispatch.remote_ip' => 'value from action_dispatch.remote_ip',
41
+ 'REMOTE_ADDR' => 'value from REMOTE_ADDR',
42
+ 'HTTP_X_FORWARDED_FOR' => 'value from HTTP_X_FORWARDED_FOR',
43
+ 'HTTP_FORWARDED' => 'value from HTTP_FORWARDED'
44
+ }
45
+
46
+ expected_header_map = {
47
+ referer: 'HTTP_REFERER',
48
+ request_id: 'action_dispatch.request_id',
49
+ remote_ip: 'action_dispatch.remote_ip',
50
+ remote_addr: 'REMOTE_ADDR',
51
+ x_forwarded_for: 'HTTP_X_FORWARDED_FOR',
52
+ forwarded: 'HTTP_FORWARDED'
53
+ }
54
+
55
+ event = instance_double(ActiveSupport::Notifications::Event)
56
+ allow(event).to receive(:payload).and_return({ headers: request_headers })
57
+
58
+ custom_options = lograge.custom_options
59
+ data = custom_options.call(event)
60
+ expect(data).to be_a(Hash)
61
+ expect(data[:time]).to be_a(Time) # TODO: check for accuracy
62
+ expected_header_map.each { |xh, rh| expect(data[xh]).to eq(request_headers[rh]) }
63
+ end
64
+
65
+ it 'formats Lograge data as a hash' do
66
+ Configurator.configure(config)
67
+ lograge = config.lograge
68
+
69
+ formatter = lograge.formatter
70
+ expect(formatter.call(nil)).to eq({ msg: 'Request', request: {} })
71
+ expect(formatter.call('elvis')).to eq({ msg: 'Request', request: { msg: 'elvis' } })
72
+ some_hash = { foo: 'bar' }
73
+ expect(formatter.call(some_hash)).to eq({ msg: 'Request', request: some_hash })
74
+ end
75
+ end
76
+
77
+ describe 'Webpacker' do
78
+ it 'works if Webpacker is not present' do
79
+ expect(Object.const_defined?(:Webpacker)).to eq(false) # just to be sure
80
+ expect { Configurator.configure(config) }.not_to raise_error
81
+ end
82
+
83
+ it 'sets the Webpacker logger if Webpacker is present' do
84
+ module ::Webpacker
85
+ module Instance
86
+ class << self
87
+ attr_accessor :logger
88
+ end
89
+ end
90
+ end
91
+
92
+ Configurator.configure(config)
93
+ expect(Webpacker::Instance.logger).to eq(config.logger)
94
+ ensure
95
+ Object.send(:remove_const, :Webpacker) if defined?(Webpacker)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails_helper'
2
+
3
+ module BerkeleyLibrary
4
+ describe Logging do
5
+
6
+ attr_reader :orig_rails_env
7
+
8
+ before(:each) do
9
+ @orig_rails_env = Rails.env
10
+ end
11
+
12
+ after(:each) do
13
+ Rails.env = orig_rails_env
14
+ end
15
+
16
+ describe :env= do
17
+ it 'sets Rails.env' do
18
+ expect(defined?(Rails)).to be_truthy # just to be sure
19
+ Logging.env = 'elvis'
20
+ expect(Rails.env).to eq('elvis')
21
+ expect(Rails.env.elvis?).to eq(true)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ require 'rails_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,117 @@
1
+ require 'rails_helper'
2
+ require 'rails'
3
+
4
+ module BerkeleyLibrary
5
+ module Logging
6
+ describe Loggers do
7
+ attr_reader :orig_rails_env
8
+
9
+ before(:each) do
10
+ @orig_rails_env = Rails.env
11
+ end
12
+
13
+ after(:each) do
14
+ Rails.env = orig_rails_env
15
+ end
16
+
17
+ describe :new_json_logger do
18
+ it 'supports tagged logging' do
19
+ out = StringIO.new
20
+ logger = Loggers.new_json_logger(out)
21
+ logger = ActiveSupport::TaggedLogging.new(logger)
22
+
23
+ expected_tag = 'hello'
24
+ expected_msg = 'this is a test'
25
+
26
+ logger.tagged(expected_tag) { logger.info(expected_msg) }
27
+
28
+ logged_json = JSON.parse(out.string)
29
+ expect(logged_json['msg']).to eq(expected_msg)
30
+ expect(logged_json['tags']).to eq([expected_tag])
31
+ end
32
+ end
33
+
34
+ describe :default_logger do
35
+ before(:each) { @rails_logger = Rails.logger }
36
+ after(:each) { Rails.logger = @rails_logger }
37
+
38
+ it 'returns the Rails logger' do
39
+ expected_logger = double(::Logger)
40
+ Rails.logger = expected_logger
41
+
42
+ expect(Loggers.default_logger).to be(expected_logger)
43
+ end
44
+
45
+ it 'returns a readable $stdout logger if Rails logger is nil' do
46
+ Rails.logger = nil
47
+
48
+ actual_logger = Loggers.default_logger
49
+ expect(actual_logger).to be_a(Logger)
50
+ expect(actual_logger.formatter).to be_a(Ougai::Formatters::Readable)
51
+
52
+ logdev = actual_logger.instance_variable_get(:@logdev)
53
+ expect(logdev).to be_a(::Logger::LogDevice)
54
+ expect(logdev.dev).to be($stdout)
55
+ end
56
+
57
+ it "doesn't cache the default logger if the Rails logger is initialized later" do
58
+ Rails.logger = nil
59
+
60
+ initial_default_logger = Loggers.default_logger
61
+
62
+ expected_logger = double(::Logger)
63
+ Rails.logger = expected_logger
64
+
65
+ actual_logger = Loggers.default_logger
66
+ expect(actual_logger).not_to be(initial_default_logger)
67
+ expect(actual_logger).to be(expected_logger)
68
+ end
69
+
70
+ end
71
+
72
+ describe :new_default_logger do
73
+ attr_reader :config
74
+
75
+ before(:each) do
76
+ app = Class.new(Rails::Application).new
77
+ @config = app.config
78
+ end
79
+
80
+ it 'returns a file logger in test' do
81
+ BerkeleyLibrary::Logging.env = 'test'
82
+ logger = Loggers.new_default_logger(config)
83
+ expect(logger).not_to be_nil
84
+ logdev = logger.instance_variable_get(:@logdev)
85
+ expect(logdev.filename).to end_with('log/test.log')
86
+ end
87
+
88
+ it 'returns a stdout logger in production' do
89
+ BerkeleyLibrary::Logging.env = 'production'
90
+ stdout_orig = $stdout
91
+ stdout_tmp = StringIO.new
92
+ begin
93
+ $stdout = stdout_tmp
94
+ logger = Loggers.new_default_logger(config)
95
+ ensure
96
+ $stdout = stdout_orig
97
+ end
98
+ expect(logger).not_to be_nil
99
+ logdev = logger.instance_variable_get(:@logdev)
100
+ expect(logdev.filename).to be_nil
101
+ expect(logdev.dev).to eq(stdout_tmp)
102
+ end
103
+
104
+ it 'returns a stdout logger in development' do
105
+ BerkeleyLibrary::Logging.env = 'development'
106
+ logger = Loggers.new_default_logger(config)
107
+ expect(logger).not_to be_nil
108
+ logdev = logger.instance_variable_get(:@logdev)
109
+ expect(logdev.filename).to be_nil
110
+ expect(logdev.dev).to eq($stdout)
111
+
112
+ # TODO: come up with a succinct way to test broadcast to file
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end