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,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