From a6be687bcf813868505c469b1ee0ffdb50e557ff Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 11:49:48 -0800 Subject: [PATCH 01/25] initial commit with one test --- .gitignore | 9 +++++++++ .travis.yml | 4 ++++ Gemfile | 4 ++++ Rakefile | 4 ++++ demo_logger.gemspec | 31 +++++++++++++++++++++++++++++++ lib/demo_logger.rb | 5 +++++ lib/demo_logger/logger.rb | 21 +++++++++++++++++++++ lib/demo_logger/version.rb | 3 +++ spec/demo_logger_spec.rb | 10 ++++++++++ 9 files changed, 91 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Gemfile create mode 100644 Rakefile create mode 100644 demo_logger.gemspec create mode 100644 lib/demo_logger.rb create mode 100644 lib/demo_logger/logger.rb create mode 100644 lib/demo_logger/version.rb create mode 100644 spec/demo_logger_spec.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cb6eeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eefb68f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: ruby +rvm: + - 1.9.3 +before_install: gem install bundler -v 1.10.6 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..6092753 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in demo_logger.gemspec +gemspec diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..a81dfd8 --- /dev/null +++ b/Rakefile @@ -0,0 +1,4 @@ +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) diff --git a/demo_logger.gemspec b/demo_logger.gemspec new file mode 100644 index 0000000..5b0bb44 --- /dev/null +++ b/demo_logger.gemspec @@ -0,0 +1,31 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'demo_logger/version' + +Gem::Specification.new do |spec| + spec.name = 'demo_logger' + spec.version = DemoLogger::VERSION + spec.authors = ['john-crimmins'] + spec.email = ['john.crimmins@gmail.com'] + + spec.summary = 'An example logger layered on top of the built in Ruby logger' + spec.description = '' + spec.homepage = 'http://github.com/CivJ/demo_logger' + + # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or + # delete this section to allow pushing this gem to any host. + if spec.respond_to?(:metadata) + spec.metadata['allowed_push_host'] = 'http://mygemserver.com' + else + raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' + end + + spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + spec.require_paths = ['lib'] + + spec.add_development_dependency 'bundler', '~> 1.10' + spec.add_development_dependency 'rake', '~> 10.0' + spec.add_development_dependency 'should_not', '~> 1.1' + spec.add_development_dependency 'rspec', '~> 3.3' +end diff --git a/lib/demo_logger.rb b/lib/demo_logger.rb new file mode 100644 index 0000000..d850964 --- /dev/null +++ b/lib/demo_logger.rb @@ -0,0 +1,5 @@ +require "demo_logger/version" + +module DemoLogger + # Your code goes here... +end diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb new file mode 100644 index 0000000..3b08a30 --- /dev/null +++ b/lib/demo_logger/logger.rb @@ -0,0 +1,21 @@ +require 'logger' + +module DemoLogger + class Logger < Logger + + # TODO: option for file.sync + # @param config [Hash] config options + # @option config [String] :filename + # @option config [Symbol] :level + # @option config [String] :path + # @option config [Boolean] :stdout + def initialize(config = {}) + log_file_name = config[:filename] || 'logger.log' + log_file_path = config[:path] || '.' + log_file = File.open(File.join(File.expand_path(log_file_path), log_file_name), 'a') + super(log_file) + + self.level = config[:level] || Logger::DEBUG + end + end +end diff --git a/lib/demo_logger/version.rb b/lib/demo_logger/version.rb new file mode 100644 index 0000000..838efe2 --- /dev/null +++ b/lib/demo_logger/version.rb @@ -0,0 +1,3 @@ +module DemoLogger + VERSION = "0.1.0" +end diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb new file mode 100644 index 0000000..7cd274a --- /dev/null +++ b/spec/demo_logger_spec.rb @@ -0,0 +1,10 @@ +require_relative '../lib/demo_logger/logger' + +describe DemoLogger::Logger do + context '::new' do + it 'creates a default instance properly' do + logger = DemoLogger::Logger.new + expect(logger).to_not be_nil + end + end +end From bd837efcb7ac75c54842da0f211e2e9a04d449ec Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 12:02:38 -0800 Subject: [PATCH 02/25] Add Rubocop --- .rubocop.yml | 12 ++++++++++++ Rakefile | 5 +++++ demo_logger.gemspec | 4 +++- lib/demo_logger.rb | 7 ++----- lib/demo_logger/logger.rb | 1 - lib/demo_logger/version.rb | 2 +- 6 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..eef987e --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,12 @@ +Encoding: + Enabled: false + +LineLength: + Enabled: true + Max: 128 + +Documentation: + Enabled: false + +MethodLength: + Enabled: false diff --git a/Rakefile b/Rakefile index a81dfd8..fe2b78a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,9 @@ require 'bundler/gem_tasks' require 'rspec/core/rake_task' +require 'rubocop/rake_task' RSpec::Core::RakeTask.new(:spec) +RuboCop::RakeTask.new + +desc 'Run style check, tests and build' +task full_build: [:rubocop, :spec] diff --git a/demo_logger.gemspec b/demo_logger.gemspec index 5b0bb44..86980fb 100644 --- a/demo_logger.gemspec +++ b/demo_logger.gemspec @@ -18,9 +18,10 @@ Gem::Specification.new do |spec| if spec.respond_to?(:metadata) spec.metadata['allowed_push_host'] = 'http://mygemserver.com' else - raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' + fail 'RubyGems 2.0 or newer is required to protect against public gem pushes.' end + # rubocop:disable Style/RegexpLiteral spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.require_paths = ['lib'] @@ -28,4 +29,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'should_not', '~> 1.1' spec.add_development_dependency 'rspec', '~> 3.3' + spec.add_development_dependency 'rubocop', '= 0.26.0' end diff --git a/lib/demo_logger.rb b/lib/demo_logger.rb index d850964..8649fbe 100644 --- a/lib/demo_logger.rb +++ b/lib/demo_logger.rb @@ -1,5 +1,2 @@ -require "demo_logger/version" - -module DemoLogger - # Your code goes here... -end +require_relative 'demo_logger/version' +require_relative 'demo_logger/logger' diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb index 3b08a30..439af84 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/logger.rb @@ -2,7 +2,6 @@ module DemoLogger class Logger < Logger - # TODO: option for file.sync # @param config [Hash] config options # @option config [String] :filename diff --git a/lib/demo_logger/version.rb b/lib/demo_logger/version.rb index 838efe2..e016160 100644 --- a/lib/demo_logger/version.rb +++ b/lib/demo_logger/version.rb @@ -1,3 +1,3 @@ module DemoLogger - VERSION = "0.1.0" + VERSION = '0.1.0' end From 9807e28d564676874417abeb20d4bdc38e4f93b6 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 12:09:51 -0800 Subject: [PATCH 03/25] Add test --- lib/demo_logger/logger.rb | 7 ++++--- spec/demo_logger_spec.rb | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb index 439af84..39f3b24 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/logger.rb @@ -2,6 +2,7 @@ module DemoLogger class Logger < Logger + attr_accessor :log_file # TODO: option for file.sync # @param config [Hash] config options # @option config [String] :filename @@ -11,10 +12,10 @@ class Logger < Logger def initialize(config = {}) log_file_name = config[:filename] || 'logger.log' log_file_path = config[:path] || '.' - log_file = File.open(File.join(File.expand_path(log_file_path), log_file_name), 'a') - super(log_file) + @log_file = File.open(File.join(File.expand_path(log_file_path), log_file_name), 'a') + super(@log_file) - self.level = config[:level] || Logger::DEBUG + self.level = config[:level] || Logger::WARN end end end diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index 7cd274a..8c2baca 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -5,6 +5,8 @@ it 'creates a default instance properly' do logger = DemoLogger::Logger.new expect(logger).to_not be_nil + expect(logger.level).to be Logger::WARN + expect(logger.log_file.path).to eql File.expand_path('.', 'logger.log') end end end From b3110ee8331dea80208f991ad3d76d637f924e50 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 12:32:54 -0800 Subject: [PATCH 04/25] Add rm:tmp task. Use path only --- Rakefile | 9 ++++++++- lib/demo_logger/logger.rb | 8 +++----- spec/demo_logger_spec.rb | 15 ++++++++++----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Rakefile b/Rakefile index fe2b78a..8f58ca6 100644 --- a/Rakefile +++ b/Rakefile @@ -5,5 +5,12 @@ require 'rubocop/rake_task' RSpec::Core::RakeTask.new(:spec) RuboCop::RakeTask.new +namespace :rm do + desc 'Remove ./tmp files' + task :tmp do + FileUtils.rm_rf('tmp') + end +end + desc 'Run style check, tests and build' -task full_build: [:rubocop, :spec] +task full_build: [:'rm:tmp', :rubocop, :spec] diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb index 39f3b24..a939c3b 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/logger.rb @@ -5,14 +5,12 @@ class Logger < Logger attr_accessor :log_file # TODO: option for file.sync # @param config [Hash] config options - # @option config [String] :filename # @option config [Symbol] :level - # @option config [String] :path + # @option config [String] :path defaults to File.join(Dir.pwd, 'logger.log') # @option config [Boolean] :stdout def initialize(config = {}) - log_file_name = config[:filename] || 'logger.log' - log_file_path = config[:path] || '.' - @log_file = File.open(File.join(File.expand_path(log_file_path), log_file_name), 'a') + log_file_path = config[:path] || File.join(Dir.pwd, 'logger.log') + @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') super(@log_file) self.level = config[:level] || Logger::WARN diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index 8c2baca..0ddd975 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -2,11 +2,16 @@ describe DemoLogger::Logger do context '::new' do - it 'creates a default instance properly' do - logger = DemoLogger::Logger.new - expect(logger).to_not be_nil - expect(logger.level).to be Logger::WARN - expect(logger.log_file.path).to eql File.expand_path('.', 'logger.log') + context 'configuration settings' do + # let(:output_file) { File.join(__FILE__, '..', 'tmp', 'spec_output.log') } + # let(:config) { {path: output_file} } + + it 'creates a default instance properly' do + logger = DemoLogger::Logger.new + expect(logger).to_not be_nil + expect(logger.level).to be Logger::WARN + expect(logger.log_file.path).to eql File.join(Dir.pwd, 'logger.log') + end end end end From 66c866d8fc3ac451bc56311d0aef3d17f218d5bc Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 13:33:27 -0800 Subject: [PATCH 05/25] Add config from file and readme --- README.md | 24 +++++++++++++++++++++++- config/config.yml | 2 ++ demo_logger.gemspec | 3 +++ lib/demo_logger/logger.rb | 12 +++++++++++- spec/demo_logger_spec.rb | 8 +++----- 5 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 config/config.yml diff --git a/README.md b/README.md index 4fc450c..bf4d674 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # demo_logger -Experimenting with logging features + +This logging library layers additional features on top of Ruby's [logger](http://ruby-doc.org/stdlib-2.2.3/libdoc/logger/rdoc/Logger.html) + +## Installation + +Add this line to your application's Gemfile: + + gem 'demo_logger' + +And then execute: + + $ bundle install + +Or install it yourself as: + + $ gem install orb_logger + +## Usage + +Out of the box: + + logger = OrbLogger::OrbLogger.new + diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..1c3e444 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,2 @@ +:demo_logger: + :level: info diff --git a/demo_logger.gemspec b/demo_logger.gemspec index 86980fb..37eb227 100644 --- a/demo_logger.gemspec +++ b/demo_logger.gemspec @@ -12,6 +12,7 @@ Gem::Specification.new do |spec| spec.summary = 'An example logger layered on top of the built in Ruby logger' spec.description = '' spec.homepage = 'http://github.com/CivJ/demo_logger' + spec.required_ruby_version = '~> 2.2' # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or # delete this section to allow pushing this gem to any host. @@ -25,6 +26,8 @@ Gem::Specification.new do |spec| spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.require_paths = ['lib'] + spec.add_runtime_dependency 'clean_config', '= 0.0.2' + spec.add_development_dependency 'bundler', '~> 1.10' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'should_not', '~> 1.1' diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb index a939c3b..9af8a4f 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/logger.rb @@ -1,7 +1,10 @@ require 'logger' +require 'clean_config' # I wrote this open-source library module DemoLogger class Logger < Logger + include CleanConfig::Configurable + attr_accessor :log_file # TODO: option for file.sync # @param config [Hash] config options @@ -9,11 +12,18 @@ class Logger < Logger # @option config [String] :path defaults to File.join(Dir.pwd, 'logger.log') # @option config [Boolean] :stdout def initialize(config = {}) + config.merge!(CleanConfig::Configuration.instance[:demo_logger]) + puts "config: #{config}" log_file_path = config[:path] || File.join(Dir.pwd, 'logger.log') @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') + super(@log_file) - self.level = config[:level] || Logger::WARN + level = nil + if config[:level] + level = Kernel.const_get("Logger::#{config[:level].upcase}") + end + self.level = level || Logger::WARN end end end diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index 0ddd975..330193f 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -1,15 +1,13 @@ +require 'clean_config' require_relative '../lib/demo_logger/logger' describe DemoLogger::Logger do context '::new' do context 'configuration settings' do - # let(:output_file) { File.join(__FILE__, '..', 'tmp', 'spec_output.log') } - # let(:config) { {path: output_file} } - - it 'creates a default instance properly' do + it 'creates an instance from a config file' do logger = DemoLogger::Logger.new expect(logger).to_not be_nil - expect(logger.level).to be Logger::WARN + expect(logger.level).to be Logger::INFO expect(logger.log_file.path).to eql File.join(Dir.pwd, 'logger.log') end end From d08af719da40f7bdd9bf4cf40716f472895d32f9 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 14:51:54 -0800 Subject: [PATCH 06/25] Add a dummy project for e2e testing --- README.md | 32 ++++++++++++++++++++++++---- spec/demo_logger_spec.rb | 2 +- spec/e2e/core/.gitignore | 19 +++++++++++++++++ spec/e2e/core/.rspec | 1 + spec/e2e/core/.rubocop.yml | 22 +++++++++++++++++++ spec/e2e/core/Gemfile | 5 +++++ spec/e2e/core/Rakefile | 1 + spec/e2e/core/config/config.yml | 2 ++ spec/e2e/core/core.gemspec | 32 ++++++++++++++++++++++++++++ spec/e2e/core/lib/core.rb | 2 ++ spec/e2e/core/lib/core/core_one.rb | 13 +++++++++++ spec/e2e/core/lib/core/version.rb | 3 +++ spec/e2e/core/lib/tasks.rb | 17 +++++++++++++++ spec/e2e/core/spec/spec_helper.rb | 6 ++++++ spec/e2e/core/spec/unit/core_spec.rb | 15 +++++++++++++ spec/e2e/core_config_spec.rb | 14 ++++++++++++ 16 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 spec/e2e/core/.gitignore create mode 100644 spec/e2e/core/.rspec create mode 100644 spec/e2e/core/.rubocop.yml create mode 100644 spec/e2e/core/Gemfile create mode 100644 spec/e2e/core/Rakefile create mode 100644 spec/e2e/core/config/config.yml create mode 100644 spec/e2e/core/core.gemspec create mode 100644 spec/e2e/core/lib/core.rb create mode 100644 spec/e2e/core/lib/core/core_one.rb create mode 100644 spec/e2e/core/lib/core/version.rb create mode 100644 spec/e2e/core/lib/tasks.rb create mode 100644 spec/e2e/core/spec/spec_helper.rb create mode 100644 spec/e2e/core/spec/unit/core_spec.rb create mode 100644 spec/e2e/core_config_spec.rb diff --git a/README.md b/README.md index bf4d674..d32df9b 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,35 @@ And then execute: Or install it yourself as: - $ gem install orb_logger + $ gem install demo_logger ## Usage -Out of the box: - - logger = OrbLogger::OrbLogger.new +``` +require 'demo_logger' +logger = DemoLogger::DemoLogger.new +``` + +## Configuration + +Configuration is provided via [clean_config](https://github.com/opower/clean_config), an open source library I wrote. +Please see that project's README for details on how configuration works. + +A basic example is + +``` +# /config/config.yml <-- this location is required +:demo_logger: + :level: info +``` + +## E2E tests +The tests at `spec/e2e/core/spec/unit` show what happens when you actually include this as a separate library +into a new project. That is the appropriate location to test config value overrides and 'require' statements. Those +tests should guarantee this project will work as expected from another project. + +The `Core` project is just a dummy project that depends on `DemoLogger`. The only files of interest are: + +* spec/e2e/core/lib/core.rb +* spec/e2e/core/spec/unit/core_spec.rb diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index 330193f..ce2ce36 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -8,7 +8,7 @@ logger = DemoLogger::Logger.new expect(logger).to_not be_nil expect(logger.level).to be Logger::INFO - expect(logger.log_file.path).to eql File.join(Dir.pwd, 'logger.log') + expect(logger.log_file.path).to eq File.join(Dir.pwd, 'logger.log') end end end diff --git a/spec/e2e/core/.gitignore b/spec/e2e/core/.gitignore new file mode 100644 index 0000000..8e32418 --- /dev/null +++ b/spec/e2e/core/.gitignore @@ -0,0 +1,19 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +vendor +*.log diff --git a/spec/e2e/core/.rspec b/spec/e2e/core/.rspec new file mode 100644 index 0000000..82b8369 --- /dev/null +++ b/spec/e2e/core/.rspec @@ -0,0 +1 @@ +--require spec_helper \ No newline at end of file diff --git a/spec/e2e/core/.rubocop.yml b/spec/e2e/core/.rubocop.yml new file mode 100644 index 0000000..9014652 --- /dev/null +++ b/spec/e2e/core/.rubocop.yml @@ -0,0 +1,22 @@ +#do not check for utf stamp +Encoding: + Enabled: false + +LineLength: + Enabled: true + Max: 128 + +# The rubocop Documentation cop is a duplicate of the Reek IrresponsibleModule +# check. +Documentation: + Enabled: false + +CaseIndentation: + Enabled: false + +# MethodLength is superseded by Reek's TooManyStatements smell-finder. +MethodLength: + Enabled: false + +CyclomaticComplexity: + Max: 10 diff --git a/spec/e2e/core/Gemfile b/spec/e2e/core/Gemfile new file mode 100644 index 0000000..3ec53f8 --- /dev/null +++ b/spec/e2e/core/Gemfile @@ -0,0 +1,5 @@ +source :rubygems + +# Specify your gem's dependencies in core.gemspec +gemspec +gem 'demo_logger', path: '../../..' diff --git a/spec/e2e/core/Rakefile b/spec/e2e/core/Rakefile new file mode 100644 index 0000000..e3256e3 --- /dev/null +++ b/spec/e2e/core/Rakefile @@ -0,0 +1 @@ +require_relative 'lib/tasks' diff --git a/spec/e2e/core/config/config.yml b/spec/e2e/core/config/config.yml new file mode 100644 index 0000000..1956dba --- /dev/null +++ b/spec/e2e/core/config/config.yml @@ -0,0 +1,2 @@ +:demo_logger: + :level: debug diff --git a/spec/e2e/core/core.gemspec b/spec/e2e/core/core.gemspec new file mode 100644 index 0000000..6ff81aa --- /dev/null +++ b/spec/e2e/core/core.gemspec @@ -0,0 +1,32 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../lib/core/version', __FILE__) + +Gem::Specification.new do |spec| + spec.name = 'core' + spec.version = Core::VERSION + spec.summary = 'TODO: Write a short summary. Required.' + spec.description = 'TODO: Write a longer description. Optional.' + spec.homepage = 'http://github.com/your-GH-name-here/core' + + spec.authors = ['foo'] + spec.email = ['your-email-here@email.com'] + + # This gem will work with 1.9.3 or greater... + spec.required_ruby_version = '>= 1.9.3' + + spec.license = 'MIT' + + # file attributes... + spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR) + spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) } + spec.test_files = spec.files.grep(/^(test|spec|features)\//) + spec.require_paths = ['lib'] + + spec.add_development_dependency('rake', '~> 10.1') + spec.add_development_dependency('rspec', ' 2.14.1') + spec.add_development_dependency('rspec-extra-formatters', ' 0.4') + spec.add_development_dependency('rubocop', ' 0.24.0') + spec.add_development_dependency('simplecov', ' 0.7.1') + spec.add_development_dependency('rspec', '~> 3.1') + +end diff --git a/spec/e2e/core/lib/core.rb b/spec/e2e/core/lib/core.rb new file mode 100644 index 0000000..25b8e66 --- /dev/null +++ b/spec/e2e/core/lib/core.rb @@ -0,0 +1,2 @@ +require_relative 'core/version' +require_relative 'core/core_one' diff --git a/spec/e2e/core/lib/core/core_one.rb b/spec/e2e/core/lib/core/core_one.rb new file mode 100644 index 0000000..3a70fc0 --- /dev/null +++ b/spec/e2e/core/lib/core/core_one.rb @@ -0,0 +1,13 @@ +require 'demo_logger' +require 'clean_config' + +module Core + class CoreOne + include CleanConfig::Configurable + attr_reader :logger + + def initialize + @logger = DemoLogger::Logger.new + end + end +end diff --git a/spec/e2e/core/lib/core/version.rb b/spec/e2e/core/lib/core/version.rb new file mode 100644 index 0000000..f876a84 --- /dev/null +++ b/spec/e2e/core/lib/core/version.rb @@ -0,0 +1,3 @@ +module Core + VERSION = '0.0.1' +end diff --git a/spec/e2e/core/lib/tasks.rb b/spec/e2e/core/lib/tasks.rb new file mode 100644 index 0000000..68ab4e5 --- /dev/null +++ b/spec/e2e/core/lib/tasks.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env rake +require 'rubocop/rake_task' +require 'rspec/core/rake_task' + +desc 'Run RuboCop on lib and spec directories, Gemfile, gemspec, Rakefile; Override with a space delimited list' +RuboCop::RakeTask.new(:rubocop, :pattern) do |task, args| + task.patterns = ['lib/**/*.rb', 'spec/**/*.rb', 'Gemfile', '*.gemspec', 'Rakefile'] + task.patterns = args[:pattern].split(' ') if args[:pattern] # override default pattern + # don't abort rake on failure + task.fail_on_error = false +end + +namespace :spec do + RSpec::Core::RakeTask.new(:unit) do |task| + task.pattern = 'spec/**/*_spec.rb' + end +end diff --git a/spec/e2e/core/spec/spec_helper.rb b/spec/e2e/core/spec/spec_helper.rb new file mode 100644 index 0000000..3281aab --- /dev/null +++ b/spec/e2e/core/spec/spec_helper.rb @@ -0,0 +1,6 @@ +RSpec.configure do |config| + # Only accept expect syntax do not allow old should syntax + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/e2e/core/spec/unit/core_spec.rb b/spec/e2e/core/spec/unit/core_spec.rb new file mode 100644 index 0000000..a3441ad --- /dev/null +++ b/spec/e2e/core/spec/unit/core_spec.rb @@ -0,0 +1,15 @@ +require 'logger' +require_relative '../../lib/core/core_one' + +module Core + describe CoreOne do + context 'demo_logger layers config' do + it 'Core can override demo_logger configuration' do + core_one = CoreOne.new + + puts "core_cone logger level: #{core_one.logger.level}" + expect(core_one.logger.level).to eql(Logger::DEBUG) + end + end + end +end diff --git a/spec/e2e/core_config_spec.rb b/spec/e2e/core_config_spec.rb new file mode 100644 index 0000000..66c74c5 --- /dev/null +++ b/spec/e2e/core_config_spec.rb @@ -0,0 +1,14 @@ +require 'English' + +module CleanConfig + describe Configuration do + it 'should load correct config when required in a different gem' do + Dir.chdir(File.join('spec', 'e2e', 'core')) do + Bundler.with_clean_env do + `bundle install && bundle exec rake spec:unit` + end + end + expect($CHILD_STATUS.success?).to be true + end + end +end From b0591d60a996781d4034fb1c7a13dc530f6be873 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 14:54:39 -0800 Subject: [PATCH 07/25] More readme --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d32df9b..4c9db5b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,13 @@ Or install it yourself as: $ gem install demo_logger +## Building +See tasks: `bundle exec rake -T` + +Full build: `bundle exec rake full_build` + +Tests only: `bundle exec rake spec` + ## Usage ``` @@ -31,7 +38,7 @@ Please see that project's README for details on how configuration works. A basic example is ``` -# /config/config.yml <-- this location is required +# /config/config.yml <-- this location is required :demo_logger: :level: info ``` From 4fc96d6da659ba0fa4665d9d95be3e4aadcbfbdc Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 15:08:52 -0800 Subject: [PATCH 08/25] Fix config/rspec problem --- .travis.yml | 2 +- Rakefile | 2 +- spec/demo_logger_spec.rb | 4 ++++ spec/e2e/core/spec/unit/core_spec.rb | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eefb68f..6fb8618 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ language: ruby rvm: - - 1.9.3 + - 2.2.3 before_install: gem install bundler -v 1.10.6 diff --git a/Rakefile b/Rakefile index 8f58ca6..811a96a 100644 --- a/Rakefile +++ b/Rakefile @@ -13,4 +13,4 @@ namespace :rm do end desc 'Run style check, tests and build' -task full_build: [:'rm:tmp', :rubocop, :spec] +task full_build: [:'rm:tmp', :rubocop, :spec, :build] diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index ce2ce36..f53f7ab 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -5,6 +5,10 @@ context '::new' do context 'configuration settings' do it 'creates an instance from a config file' do + + # Reset the configuration by hand during an Rspec run. + CleanConfig::Configuration.instance.merge!(demo_logger: { level: 'info' }) + logger = DemoLogger::Logger.new expect(logger).to_not be_nil expect(logger.level).to be Logger::INFO diff --git a/spec/e2e/core/spec/unit/core_spec.rb b/spec/e2e/core/spec/unit/core_spec.rb index a3441ad..1bb499f 100644 --- a/spec/e2e/core/spec/unit/core_spec.rb +++ b/spec/e2e/core/spec/unit/core_spec.rb @@ -5,6 +5,9 @@ module Core describe CoreOne do context 'demo_logger layers config' do it 'Core can override demo_logger configuration' do + + # Reset the configuration by hand during an Rspec run. + CleanConfig::Configuration.instance.merge!(demo_logger: { level: 'debug' }) core_one = CoreOne.new puts "core_cone logger level: #{core_one.logger.level}" From 06e468d316757e75cb2cb4e6daa9351ee25212be Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 15:16:13 -0800 Subject: [PATCH 09/25] Modify travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6fb8618..ff326d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,4 @@ language: ruby rvm: - 2.2.3 before_install: gem install bundler -v 1.10.6 +script: bundle install && bundle exec rake full_build From 66ef5cfff158b19d72723314d9f38b9e93378e06 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 15:37:55 -0800 Subject: [PATCH 10/25] Comments --- lib/demo_logger/logger.rb | 16 ++++++++++++---- spec/demo_logger_spec.rb | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/logger.rb index 9af8a4f..189df94 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/logger.rb @@ -2,21 +2,29 @@ require 'clean_config' # I wrote this open-source library module DemoLogger + # TODO: rename to FileLogger class Logger < Logger include CleanConfig::Configurable + # TODO: #log and #info are the same. How should log map onto logger's api? + alias_method :log, :info + alias_method :severe, :fatal attr_accessor :log_file - # TODO: option for file.sync + # @param config [Hash] config options - # @option config [Symbol] :level - # @option config [String] :path defaults to File.join(Dir.pwd, 'logger.log') - # @option config [Boolean] :stdout + # @option config [String] :file + # @option config [String] :stdout + # @option config [String] :email def initialize(config = {}) config.merge!(CleanConfig::Configuration.instance[:demo_logger]) puts "config: #{config}" log_file_path = config[:path] || File.join(Dir.pwd, 'logger.log') @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') + # We are setting sync = true because of the following line from the spec: + # "You need to build a framework that will continuously dump the logger output to a file." + # This sounds like an instruction to avoid buffering, so that's what we're doing. + @log_file.sync = true super(@log_file) level = nil diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index f53f7ab..4a711a8 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -16,4 +16,10 @@ end end end + + context 'sanity check api behavior' do + context 'log level should be honored' do + + end + end end From 42b6b12299a9325283eb54e4f8071d8b987c3f65 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 15:41:24 -0800 Subject: [PATCH 11/25] Rename file. Prepare for logger hirearchy --- lib/demo_logger.rb | 2 +- lib/demo_logger/{logger.rb => file_logger.rb} | 4 ++-- spec/demo_logger_spec.rb | 6 +++--- spec/e2e/core/lib/core/core_one.rb | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename lib/demo_logger/{logger.rb => file_logger.rb} (94%) diff --git a/lib/demo_logger.rb b/lib/demo_logger.rb index 8649fbe..66f051a 100644 --- a/lib/demo_logger.rb +++ b/lib/demo_logger.rb @@ -1,2 +1,2 @@ require_relative 'demo_logger/version' -require_relative 'demo_logger/logger' +require_relative 'demo_logger/file_logger' diff --git a/lib/demo_logger/logger.rb b/lib/demo_logger/file_logger.rb similarity index 94% rename from lib/demo_logger/logger.rb rename to lib/demo_logger/file_logger.rb index 189df94..573e097 100644 --- a/lib/demo_logger/logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -3,7 +3,7 @@ module DemoLogger # TODO: rename to FileLogger - class Logger < Logger + class FileLogger < Logger include CleanConfig::Configurable # TODO: #log and #info are the same. How should log map onto logger's api? @@ -31,7 +31,7 @@ def initialize(config = {}) if config[:level] level = Kernel.const_get("Logger::#{config[:level].upcase}") end - self.level = level || Logger::WARN + self.level = level || FileLogger::WARN end end end diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb index 4a711a8..7ce5f61 100644 --- a/spec/demo_logger_spec.rb +++ b/spec/demo_logger_spec.rb @@ -1,7 +1,7 @@ require 'clean_config' -require_relative '../lib/demo_logger/logger' +require_relative '../lib/demo_logger/file_logger' -describe DemoLogger::Logger do +describe DemoLogger::FileLogger do context '::new' do context 'configuration settings' do it 'creates an instance from a config file' do @@ -9,7 +9,7 @@ # Reset the configuration by hand during an Rspec run. CleanConfig::Configuration.instance.merge!(demo_logger: { level: 'info' }) - logger = DemoLogger::Logger.new + logger = DemoLogger::FileLogger.new expect(logger).to_not be_nil expect(logger.level).to be Logger::INFO expect(logger.log_file.path).to eq File.join(Dir.pwd, 'logger.log') diff --git a/spec/e2e/core/lib/core/core_one.rb b/spec/e2e/core/lib/core/core_one.rb index 3a70fc0..3e5ce39 100644 --- a/spec/e2e/core/lib/core/core_one.rb +++ b/spec/e2e/core/lib/core/core_one.rb @@ -7,7 +7,7 @@ class CoreOne attr_reader :logger def initialize - @logger = DemoLogger::Logger.new + @logger = DemoLogger::FileLogger.new end end end From b04298c66947e6dc292c17ed3e55b735a90d81a2 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 16:15:24 -0800 Subject: [PATCH 12/25] Fix tests after refactor --- config/config.yml | 2 +- lib/demo_logger/file_logger.rb | 27 +++++---------------------- spec/demo_logger_spec.rb | 25 ------------------------- spec/e2e/core/config/config.yml | 3 ++- spec/e2e/core/spec/unit/core_spec.rb | 20 +++++++++----------- spec/file_logger_spec.rb | 13 +++++++++++++ 6 files changed, 30 insertions(+), 60 deletions(-) delete mode 100644 spec/demo_logger_spec.rb create mode 100644 spec/file_logger_spec.rb diff --git a/config/config.yml b/config/config.yml index 1c3e444..018beda 100644 --- a/config/config.yml +++ b/config/config.yml @@ -1,2 +1,2 @@ :demo_logger: - :level: info + :file: info diff --git a/lib/demo_logger/file_logger.rb b/lib/demo_logger/file_logger.rb index 573e097..c40758d 100644 --- a/lib/demo_logger/file_logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -1,24 +1,12 @@ require 'logger' -require 'clean_config' # I wrote this open-source library +require 'clean_config' module DemoLogger - # TODO: rename to FileLogger class FileLogger < Logger - include CleanConfig::Configurable - - # TODO: #log and #info are the same. How should log map onto logger's api? - alias_method :log, :info - alias_method :severe, :fatal attr_accessor :log_file - - # @param config [Hash] config options - # @option config [String] :file - # @option config [String] :stdout - # @option config [String] :email - def initialize(config = {}) - config.merge!(CleanConfig::Configuration.instance[:demo_logger]) - puts "config: #{config}" - log_file_path = config[:path] || File.join(Dir.pwd, 'logger.log') + # @param level [Hash] log level for files + def initialize(level) + log_file_path = File.join(Dir.pwd, 'logger.log') @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') # We are setting sync = true because of the following line from the spec: @@ -26,12 +14,7 @@ def initialize(config = {}) # This sounds like an instruction to avoid buffering, so that's what we're doing. @log_file.sync = true super(@log_file) - - level = nil - if config[:level] - level = Kernel.const_get("Logger::#{config[:level].upcase}") - end - self.level = level || FileLogger::WARN + self.level = level || Logger::WARN end end end diff --git a/spec/demo_logger_spec.rb b/spec/demo_logger_spec.rb deleted file mode 100644 index 7ce5f61..0000000 --- a/spec/demo_logger_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'clean_config' -require_relative '../lib/demo_logger/file_logger' - -describe DemoLogger::FileLogger do - context '::new' do - context 'configuration settings' do - it 'creates an instance from a config file' do - - # Reset the configuration by hand during an Rspec run. - CleanConfig::Configuration.instance.merge!(demo_logger: { level: 'info' }) - - logger = DemoLogger::FileLogger.new - expect(logger).to_not be_nil - expect(logger.level).to be Logger::INFO - expect(logger.log_file.path).to eq File.join(Dir.pwd, 'logger.log') - end - end - end - - context 'sanity check api behavior' do - context 'log level should be honored' do - - end - end -end diff --git a/spec/e2e/core/config/config.yml b/spec/e2e/core/config/config.yml index 1956dba..dc9b670 100644 --- a/spec/e2e/core/config/config.yml +++ b/spec/e2e/core/config/config.yml @@ -1,2 +1,3 @@ :demo_logger: - :level: debug + :level: + :file: debug diff --git a/spec/e2e/core/spec/unit/core_spec.rb b/spec/e2e/core/spec/unit/core_spec.rb index 1bb499f..db5db20 100644 --- a/spec/e2e/core/spec/unit/core_spec.rb +++ b/spec/e2e/core/spec/unit/core_spec.rb @@ -3,16 +3,14 @@ module Core describe CoreOne do - context 'demo_logger layers config' do - it 'Core can override demo_logger configuration' do - - # Reset the configuration by hand during an Rspec run. - CleanConfig::Configuration.instance.merge!(demo_logger: { level: 'debug' }) - core_one = CoreOne.new - - puts "core_cone logger level: #{core_one.logger.level}" - expect(core_one.logger.level).to eql(Logger::DEBUG) - end - end + # context 'demo_logger layers config' do + # it 'Core can override demo_logger configuration' do + # + # core_one = CoreOne.new + # + # puts "core_cone logger level: #{core_one.logger.level}" + # expect(core_one.logger.level).to eql(Logger::DEBUG) + # end + # end end end diff --git a/spec/file_logger_spec.rb b/spec/file_logger_spec.rb new file mode 100644 index 0000000..c5245aa --- /dev/null +++ b/spec/file_logger_spec.rb @@ -0,0 +1,13 @@ +require 'clean_config' +require_relative '../lib/demo_logger/file_logger' + +describe DemoLogger::FileLogger do + context '::new' do + it 'creates an instance' do + logger = DemoLogger::FileLogger.new(Logger::INFO) + expect(logger).to_not be_nil + expect(logger.level).to eq Logger::INFO + expect(logger.log_file.path).to eq File.join(Dir.pwd, 'logger.log') + end + end +end From 2f93438b682c49af1fa902f41d78b98344f8d45d Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 17:29:49 -0800 Subject: [PATCH 13/25] Add multiloger tests with one File logger --- config/config.yml | 2 ++ lib/demo_logger.rb | 1 + lib/demo_logger/file_logger.rb | 1 + lib/demo_logger/multi_logger.rb | 64 +++++++++++++++++++++++++++++++++ spec/multi_logger_spec.rb | 22 ++++++++++++ spec/spec_helper.rb | 8 +++++ 6 files changed, 98 insertions(+) create mode 100644 lib/demo_logger/multi_logger.rb create mode 100644 spec/multi_logger_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/config/config.yml b/config/config.yml index 018beda..a3811c7 100644 --- a/config/config.yml +++ b/config/config.yml @@ -1,2 +1,4 @@ +# debug info log warn severe + :demo_logger: :file: info diff --git a/lib/demo_logger.rb b/lib/demo_logger.rb index 66f051a..f4b6a63 100644 --- a/lib/demo_logger.rb +++ b/lib/demo_logger.rb @@ -1,2 +1,3 @@ require_relative 'demo_logger/version' +require_relative 'demo_logger/multi_logger' require_relative 'demo_logger/file_logger' diff --git a/lib/demo_logger/file_logger.rb b/lib/demo_logger/file_logger.rb index c40758d..8f6047e 100644 --- a/lib/demo_logger/file_logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -14,6 +14,7 @@ def initialize(level) # This sounds like an instruction to avoid buffering, so that's what we're doing. @log_file.sync = true super(@log_file) + puts "LEVEL: #{level}" self.level = level || Logger::WARN end end diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb new file mode 100644 index 0000000..6303d21 --- /dev/null +++ b/lib/demo_logger/multi_logger.rb @@ -0,0 +1,64 @@ +require 'logger' +require 'clean_config' +require_relative 'file_logger' + +module DemoLogger + class MultiLogger + include CleanConfig::Configurable + attr_reader :logs + + # TODO: :log and :info are the same. How should #log map onto logger's api? + LOGGER_CONST_MAP = { + debug: Logger::DEBUG, # 0 + info: Logger::INFO, # 1 + log: Logger::INFO, + warn: Logger::WARN, # 2 + severe: Logger::FATAL # 4 + } + + # @param config [Hash] config options + # @option config [String] :file log level for file + # @option config [String] :stdout log level for stdout + # @option config [String] :email log level for email + def initialize(config = {}) + # defaults = { file: 'warn', stdout: 'warn', email: 'email' } + defaults = { demo_logger: { file: 'warn' } } + config = defaults.merge(CleanConfig::Configuration.instance).merge(config) + @logs = [] + file_level = config[:demo_logger][:file] + @logs << FileLogger.new(const_get(file_level)) if file_level + # level = nil + # if config[:level] + # level = Kernel.const_get("Logger::#{config[:level].upcase}") + # end + # self.level = level || Logger::WARN + end + + def debug + end + + def info + end + + def log + end + + def warn + end + + def severe + end + + # rubocop:disable Style/TrailingWhitespace + + private + + def const_get(level) + LOGGER_CONST_MAP[level.downcase.to_sym] + end + + def from_config(key) + CleanConfig::Configuration.instance[:demo_logger][key] + end + end +end diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb new file mode 100644 index 0000000..e2ff39f --- /dev/null +++ b/spec/multi_logger_spec.rb @@ -0,0 +1,22 @@ +require_relative '../lib/demo_logger/multi_logger' +require_relative 'spec_helper' + +describe DemoLogger::MultiLogger do + context 'logger creation' do + it 'creates all loggers based on config file' do + # See config/config.yml + logger = DemoLogger::MultiLogger.new + expect(logger.logs.length).to eq 1 + expect(logger.logs[0].class).to eql DemoLogger::FileLogger + end + + it 'creates all loggers based on method parameters' do + # See config/config.yml + logger = DemoLogger::MultiLogger.new(demo_logger: { file: 'severe' }) + expect(logger.logs.length).to eq 1 + log = logger.logs.first + expect(log.class).to eql DemoLogger::FileLogger + expect(log.level).to eql Logger::FATAL + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..a4b4605 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,8 @@ +require 'rspec' + +RSpec.configure do |config| + # Only accept expect syntax do not allow old should syntax + config.expect_with :rspec do |c| + c.syntax = :expect + end +end From b78268c7133903675c76adb45b0455c30c1b856e Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 17:45:43 -0800 Subject: [PATCH 14/25] Add stdout logger --- config/config.yml | 1 + lib/demo_logger.rb | 1 + lib/demo_logger/file_logger.rb | 3 +-- lib/demo_logger/multi_logger.rb | 17 ++++++++--------- lib/demo_logger/stdout_logger.rb | 11 +++++++++++ spec/multi_logger_spec.rb | 27 +++++++++++++++++++++------ 6 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 lib/demo_logger/stdout_logger.rb diff --git a/config/config.yml b/config/config.yml index a3811c7..10ff910 100644 --- a/config/config.yml +++ b/config/config.yml @@ -2,3 +2,4 @@ :demo_logger: :file: info + :stdout: debug diff --git a/lib/demo_logger.rb b/lib/demo_logger.rb index f4b6a63..b6a57f4 100644 --- a/lib/demo_logger.rb +++ b/lib/demo_logger.rb @@ -1,3 +1,4 @@ require_relative 'demo_logger/version' require_relative 'demo_logger/multi_logger' require_relative 'demo_logger/file_logger' +require_relative 'demo_logger/stdout_logger' diff --git a/lib/demo_logger/file_logger.rb b/lib/demo_logger/file_logger.rb index 8f6047e..e058b43 100644 --- a/lib/demo_logger/file_logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -4,7 +4,7 @@ module DemoLogger class FileLogger < Logger attr_accessor :log_file - # @param level [Hash] log level for files + # @param level [String] log level for files def initialize(level) log_file_path = File.join(Dir.pwd, 'logger.log') @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') @@ -14,7 +14,6 @@ def initialize(level) # This sounds like an instruction to avoid buffering, so that's what we're doing. @log_file.sync = true super(@log_file) - puts "LEVEL: #{level}" self.level = level || Logger::WARN end end diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb index 6303d21..9b6f40e 100644 --- a/lib/demo_logger/multi_logger.rb +++ b/lib/demo_logger/multi_logger.rb @@ -1,7 +1,7 @@ require 'logger' require 'clean_config' require_relative 'file_logger' - +require_relative 'stdout_logger' module DemoLogger class MultiLogger include CleanConfig::Configurable @@ -22,16 +22,15 @@ class MultiLogger # @option config [String] :email log level for email def initialize(config = {}) # defaults = { file: 'warn', stdout: 'warn', email: 'email' } - defaults = { demo_logger: { file: 'warn' } } + defaults = { + demo_logger: { file: 'warn', stdout: 'warn' } + } config = defaults.merge(CleanConfig::Configuration.instance).merge(config) @logs = [] file_level = config[:demo_logger][:file] - @logs << FileLogger.new(const_get(file_level)) if file_level - # level = nil - # if config[:level] - # level = Kernel.const_get("Logger::#{config[:level].upcase}") - # end - # self.level = level || Logger::WARN + stdout_level = config[:demo_logger][:stdout] + @logs << FileLogger.new(translate_level(file_level)) + @logs << StdoutLogger.new(translate_level(stdout_level)) end def debug @@ -53,7 +52,7 @@ def severe private - def const_get(level) + def translate_level(level) LOGGER_CONST_MAP[level.downcase.to_sym] end diff --git a/lib/demo_logger/stdout_logger.rb b/lib/demo_logger/stdout_logger.rb new file mode 100644 index 0000000..1aedf46 --- /dev/null +++ b/lib/demo_logger/stdout_logger.rb @@ -0,0 +1,11 @@ +require 'logger' + +module DemoLogger + class StdoutLogger < Logger + # @param level [String] log level for STDOUT + def initialize(level) + super(STDOUT) + self.level = level + end + end +end diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index e2ff39f..bf1018f 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -6,17 +6,32 @@ it 'creates all loggers based on config file' do # See config/config.yml logger = DemoLogger::MultiLogger.new - expect(logger.logs.length).to eq 1 + expect(logger.logs.length).to eq 2 expect(logger.logs[0].class).to eql DemoLogger::FileLogger + + file_log = logger.logs[0] + expect(file_log.class).to eql DemoLogger::FileLogger + expect(file_log.level).to eql Logger::INFO + + std_log = logger.logs[1] + expect(std_log.class).to eql DemoLogger::StdoutLogger + expect(std_log.level).to eql Logger::DEBUG end it 'creates all loggers based on method parameters' do # See config/config.yml - logger = DemoLogger::MultiLogger.new(demo_logger: { file: 'severe' }) - expect(logger.logs.length).to eq 1 - log = logger.logs.first - expect(log.class).to eql DemoLogger::FileLogger - expect(log.level).to eql Logger::FATAL + config = { + demo_logger: { file: 'severe', stdout: 'severe' } } + logger = DemoLogger::MultiLogger.new(config) + expect(logger.logs.length).to eq 2 + + file_log = logger.logs[0] + expect(file_log.class).to eql DemoLogger::FileLogger + expect(file_log.level).to eql Logger::FATAL + + std_log = logger.logs[1] + expect(std_log.class).to eql DemoLogger::StdoutLogger + expect(std_log.level).to eql Logger::FATAL end end end From 880983e1675afaf578c5ef6d9bb6e3e84522d49f Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 17:52:28 -0800 Subject: [PATCH 15/25] Use map insetad of list --- lib/demo_logger/multi_logger.rb | 9 ++++++--- spec/multi_logger_spec.rb | 9 ++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb index 9b6f40e..86cd210 100644 --- a/lib/demo_logger/multi_logger.rb +++ b/lib/demo_logger/multi_logger.rb @@ -15,6 +15,9 @@ class MultiLogger warn: Logger::WARN, # 2 severe: Logger::FATAL # 4 } + FILE = :file + STDOUT = :stdout + EMAIL = :email # @param config [Hash] config options # @option config [String] :file log level for file @@ -26,11 +29,11 @@ def initialize(config = {}) demo_logger: { file: 'warn', stdout: 'warn' } } config = defaults.merge(CleanConfig::Configuration.instance).merge(config) - @logs = [] + @logs = {} file_level = config[:demo_logger][:file] stdout_level = config[:demo_logger][:stdout] - @logs << FileLogger.new(translate_level(file_level)) - @logs << StdoutLogger.new(translate_level(stdout_level)) + @logs[MultiLogger::FILE] = FileLogger.new(translate_level(file_level)) + @logs[MultiLogger::STDOUT] = StdoutLogger.new(translate_level(stdout_level)) end def debug diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index bf1018f..d3b72ea 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -7,13 +7,12 @@ # See config/config.yml logger = DemoLogger::MultiLogger.new expect(logger.logs.length).to eq 2 - expect(logger.logs[0].class).to eql DemoLogger::FileLogger - file_log = logger.logs[0] + file_log = logger.logs[DemoLogger::MultiLogger::FILE] expect(file_log.class).to eql DemoLogger::FileLogger expect(file_log.level).to eql Logger::INFO - std_log = logger.logs[1] + std_log = logger.logs[DemoLogger::MultiLogger::STDOUT] expect(std_log.class).to eql DemoLogger::StdoutLogger expect(std_log.level).to eql Logger::DEBUG end @@ -25,11 +24,11 @@ logger = DemoLogger::MultiLogger.new(config) expect(logger.logs.length).to eq 2 - file_log = logger.logs[0] + file_log = logger.logs[DemoLogger::MultiLogger::FILE] expect(file_log.class).to eql DemoLogger::FileLogger expect(file_log.level).to eql Logger::FATAL - std_log = logger.logs[1] + std_log = logger.logs[DemoLogger::MultiLogger::STDOUT] expect(std_log.class).to eql DemoLogger::StdoutLogger expect(std_log.level).to eql Logger::FATAL end From a2c50008c462805552ddff1cfa72ab1ba4ac5dac Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 18:27:22 -0800 Subject: [PATCH 16/25] Add test for output --- lib/demo_logger/multi_logger.rb | 17 ++++++++++++----- spec/multi_logger_spec.rb | 16 ++++++++++++++++ spec/spec_helper.rb | 20 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb index 86cd210..fe9135c 100644 --- a/lib/demo_logger/multi_logger.rb +++ b/lib/demo_logger/multi_logger.rb @@ -36,19 +36,26 @@ def initialize(config = {}) @logs[MultiLogger::STDOUT] = StdoutLogger.new(translate_level(stdout_level)) end - def debug + # TODO: We could probably clean this up with something clever. + def debug(message) + @logs.each { |_type, log| log.debug(message) } end - def info + def info(message) + @logs.each { |_type, log| log.info(message) } end - def log + # TODO: how should we map #log onto Logger's api? + def log(message) + @logs.each { |_type, log| log.info(message) } end - def warn + def warn(message) + @logs.each { |_type, log| log.warn(message) } end - def severe + def severe(message) + @logs.each { |_type, log| log.fatal(message) } end # rubocop:disable Style/TrailingWhitespace diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index d3b72ea..ea356fa 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -21,6 +21,7 @@ # See config/config.yml config = { demo_logger: { file: 'severe', stdout: 'severe' } } + logger = DemoLogger::MultiLogger.new(config) expect(logger.logs.length).to eq 2 @@ -32,5 +33,20 @@ expect(std_log.class).to eql DemoLogger::StdoutLogger expect(std_log.level).to eql Logger::FATAL end + + it 'logs to all loggers' do + config = { + demo_logger: { file: 'severe', stdout: 'debug' } } + + logger = DemoLogger::MultiLogger.new(config) + + # TODO: Make the stdout capturing work. + logger.debug('TEST') + + # We can check the file immediately because sync is turned on. + log_file = logger.logs[DemoLogger::MultiLogger::FILE].log_file + file_contents = File.read(log_file) + expect(file_contents).to be_empty + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a4b4605..5f15b65 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'rspec' +require 'stringio' RSpec.configure do |config| # Only accept expect syntax do not allow old should syntax @@ -6,3 +7,22 @@ c.syntax = :expect end end + +# TODO: This is not working in my tests as expected. +def capture_stdout(&blk) + old = $stdout + $stdout = fake = StringIO.new + blk.call + fake.string +ensure + $stdout = old +end + +def capture_stderr(&blk) + old = $stderr + $stderr = fake = StringIO.new + blk.call + fake.string +ensure + $stderr = old +end From 4be2956d51e73600470fe56c27615609a3989832 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 18:31:26 -0800 Subject: [PATCH 17/25] Another expectation --- spec/multi_logger_spec.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index ea356fa..754820b 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -34,19 +34,26 @@ expect(std_log.level).to eql Logger::FATAL end - it 'logs to all loggers' do + # TODO: We can probably break this up into smaller tests. + it 'logs to all loggers at appropriate levels' do config = { demo_logger: { file: 'severe', stdout: 'debug' } } logger = DemoLogger::MultiLogger.new(config) # TODO: Make the stdout capturing work. - logger.debug('TEST') + logger.debug('TEST-DEBUG') # We can check the file immediately because sync is turned on. log_file = logger.logs[DemoLogger::MultiLogger::FILE].log_file file_contents = File.read(log_file) expect(file_contents).to be_empty + + expected_contents = 'TEST-SEVERE' + logger.severe(expected_contents) + + file_contents = File.read(log_file) + expect(file_contents).to include(expected_contents) end end end From 67095db8efff73db0368b662b1a73e6926ac2d3a Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 19:04:28 -0800 Subject: [PATCH 18/25] Refactor test. Add email class. --- Rakefile | 8 +++---- config/config.yml | 2 ++ lib/demo_logger/email_logger.rb | 20 ++++++++++++++++++ lib/demo_logger/file_logger.rb | 8 +++++-- lib/demo_logger/stdout_logger.rb | 2 +- spec/file_logger_spec.rb | 2 +- spec/multi_logger_spec.rb | 36 ++++++++++++++++++-------------- 7 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 lib/demo_logger/email_logger.rb diff --git a/Rakefile b/Rakefile index 811a96a..8d54cba 100644 --- a/Rakefile +++ b/Rakefile @@ -6,11 +6,11 @@ RSpec::Core::RakeTask.new(:spec) RuboCop::RakeTask.new namespace :rm do - desc 'Remove ./tmp files' - task :tmp do - FileUtils.rm_rf('tmp') + desc 'Remove ./log files' + task :log do + FileUtils.rm_rf('log') end end desc 'Run style check, tests and build' -task full_build: [:'rm:tmp', :rubocop, :spec, :build] +task full_build: [:'rm:log', :rubocop, :spec, :build] diff --git a/config/config.yml b/config/config.yml index 10ff910..75e4c0a 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,3 +3,5 @@ :demo_logger: :file: info :stdout: debug + :email: severe + diff --git a/lib/demo_logger/email_logger.rb b/lib/demo_logger/email_logger.rb new file mode 100644 index 0000000..1145e65 --- /dev/null +++ b/lib/demo_logger/email_logger.rb @@ -0,0 +1,20 @@ +require 'logger' +require 'stringio' + +module DemoLogger + class EmailLogger < Logger + attribute_reader :@email_file + + def initialize(level) + @email_file = File.new(File.join(Dir.pwd, 'email.log')) + + # We are setting sync = true because of the following line from the spec: + # "You need to build a framework that will continuously dump the logger output to a file." + # This sounds like an instruction to avoid buffering, so that's what we're doing. + # Obvious performance hit. + @email_file.sync = true + super(@email_file) + self.level = level || Logger::WARN + end + end +end diff --git a/lib/demo_logger/file_logger.rb b/lib/demo_logger/file_logger.rb index e058b43..f050190 100644 --- a/lib/demo_logger/file_logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -1,17 +1,21 @@ require 'logger' require 'clean_config' +require 'fileutils' module DemoLogger class FileLogger < Logger attr_accessor :log_file # @param level [String] log level for files def initialize(level) - log_file_path = File.join(Dir.pwd, 'logger.log') - @log_file = File.open(File.join(File.expand_path(log_file_path)), 'a') + log_file_dir = File.expand_path(File.join(Dir.pwd, 'log')) + FileUtils.mkdir_p(log_file_dir) + log_file_path = File.join(log_file_dir, 'logger.log') + @log_file = File.open(File.join(log_file_path), 'a') # We are setting sync = true because of the following line from the spec: # "You need to build a framework that will continuously dump the logger output to a file." # This sounds like an instruction to avoid buffering, so that's what we're doing. + # Obvious performance hit. @log_file.sync = true super(@log_file) self.level = level || Logger::WARN diff --git a/lib/demo_logger/stdout_logger.rb b/lib/demo_logger/stdout_logger.rb index 1aedf46..c29124b 100644 --- a/lib/demo_logger/stdout_logger.rb +++ b/lib/demo_logger/stdout_logger.rb @@ -5,7 +5,7 @@ class StdoutLogger < Logger # @param level [String] log level for STDOUT def initialize(level) super(STDOUT) - self.level = level + self.level = level || Logger::WARN end end end diff --git a/spec/file_logger_spec.rb b/spec/file_logger_spec.rb index c5245aa..6bbd0c4 100644 --- a/spec/file_logger_spec.rb +++ b/spec/file_logger_spec.rb @@ -7,7 +7,7 @@ logger = DemoLogger::FileLogger.new(Logger::INFO) expect(logger).to_not be_nil expect(logger.level).to eq Logger::INFO - expect(logger.log_file.path).to eq File.join(Dir.pwd, 'logger.log') + expect(logger.log_file.path).to eq File.join(Dir.pwd, 'log', 'logger.log') end end end diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index 754820b..7edc850 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -34,26 +34,30 @@ expect(std_log.level).to eql Logger::FATAL end - # TODO: We can probably break this up into smaller tests. - it 'logs to all loggers at appropriate levels' do + context 'logging levels' do + config = { demo_logger: { file: 'severe', stdout: 'debug' } } - logger = DemoLogger::MultiLogger.new(config) - - # TODO: Make the stdout capturing work. - logger.debug('TEST-DEBUG') - + let(:logger) { DemoLogger::MultiLogger.new(config) } # We can check the file immediately because sync is turned on. - log_file = logger.logs[DemoLogger::MultiLogger::FILE].log_file - file_contents = File.read(log_file) - expect(file_contents).to be_empty - - expected_contents = 'TEST-SEVERE' - logger.severe(expected_contents) - - file_contents = File.read(log_file) - expect(file_contents).to include(expected_contents) + let(:log_file) { logger.logs[DemoLogger::MultiLogger::FILE].log_file } + + # TODO: check email files + it 'debug to all logs correctly' do + # TODO: Make the stdout capturing work. + logger.debug('TEST-DEBUG') + + file_contents = File.read(log_file) + expect(file_contents).to be_empty + end + + it 'severe to all logs correctly' do + expected_contents = 'TEST-SEVERE' + logger.severe(expected_contents) + file_contents = File.read(log_file) + expect(file_contents).to include(expected_contents) + end end end end From 575f0424b65e4b3288a739b4f05a012645743137 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 19:40:57 -0800 Subject: [PATCH 19/25] Fix tests --- lib/demo_logger/email_logger.rb | 12 +++++++++--- lib/demo_logger/file_logger.rb | 1 + lib/demo_logger/multi_logger.rb | 10 +++++++--- spec/e2e/core/config/config.yml | 1 - spec/multi_logger_spec.rb | 26 +++++++++++++++++++++++--- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/demo_logger/email_logger.rb b/lib/demo_logger/email_logger.rb index 1145e65..77272e7 100644 --- a/lib/demo_logger/email_logger.rb +++ b/lib/demo_logger/email_logger.rb @@ -2,11 +2,17 @@ require 'stringio' module DemoLogger + # TODO: This could share a common superclass with FileLogger class EmailLogger < Logger - attribute_reader :@email_file + attr_reader :email_file + attr_accessor :send_mail - def initialize(level) - @email_file = File.new(File.join(Dir.pwd, 'email.log')) + def initialize(level, send_mail = false) + log_file_dir = File.expand_path(File.join(Dir.pwd, 'email')) + FileUtils.mkdir_p(log_file_dir) + log_file_path = File.join(log_file_dir, 'logger.log') + @email_file = File.open(File.join(log_file_path), 'a') + @send_mail = send_mail # We are setting sync = true because of the following line from the spec: # "You need to build a framework that will continuously dump the logger output to a file." diff --git a/lib/demo_logger/file_logger.rb b/lib/demo_logger/file_logger.rb index f050190..15f5326 100644 --- a/lib/demo_logger/file_logger.rb +++ b/lib/demo_logger/file_logger.rb @@ -5,6 +5,7 @@ module DemoLogger class FileLogger < Logger attr_accessor :log_file + # @param level [String] log level for files def initialize(level) log_file_dir = File.expand_path(File.join(Dir.pwd, 'log')) diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb index fe9135c..637269a 100644 --- a/lib/demo_logger/multi_logger.rb +++ b/lib/demo_logger/multi_logger.rb @@ -2,6 +2,8 @@ require 'clean_config' require_relative 'file_logger' require_relative 'stdout_logger' +require_relative 'email_logger' + module DemoLogger class MultiLogger include CleanConfig::Configurable @@ -24,19 +26,21 @@ class MultiLogger # @option config [String] :stdout log level for stdout # @option config [String] :email log level for email def initialize(config = {}) - # defaults = { file: 'warn', stdout: 'warn', email: 'email' } defaults = { - demo_logger: { file: 'warn', stdout: 'warn' } + demo_logger: { file: 'warn', stdout: 'warn', email: 'warn' } } config = defaults.merge(CleanConfig::Configuration.instance).merge(config) + @logs = {} file_level = config[:demo_logger][:file] stdout_level = config[:demo_logger][:stdout] + email_level = config[:demo_logger][:email] @logs[MultiLogger::FILE] = FileLogger.new(translate_level(file_level)) @logs[MultiLogger::STDOUT] = StdoutLogger.new(translate_level(stdout_level)) + @logs[MultiLogger::EMAIL] = EmailLogger.new(translate_level(email_level)) end - # TODO: We could probably clean this up with something clever. + # TODO: We could probably clean this repetition up with something clever like Forwardable. def debug(message) @logs.each { |_type, log| log.debug(message) } end diff --git a/spec/e2e/core/config/config.yml b/spec/e2e/core/config/config.yml index dc9b670..479d220 100644 --- a/spec/e2e/core/config/config.yml +++ b/spec/e2e/core/config/config.yml @@ -1,3 +1,2 @@ :demo_logger: - :level: :file: debug diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index 7edc850..82b8bb2 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -4,9 +4,15 @@ describe DemoLogger::MultiLogger do context 'logger creation' do it 'creates all loggers based on config file' do + # See config/config.yml + # Because of the way Rspec loads/evals tests we have to babysit our + # configuration object. We would not have to do this outside of Rspec. + config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } + CleanConfig::Configuration.instance.merge!(config) + logger = DemoLogger::MultiLogger.new - expect(logger.logs.length).to eq 2 + expect(logger.logs.length).to eq 3 file_log = logger.logs[DemoLogger::MultiLogger::FILE] expect(file_log.class).to eql DemoLogger::FileLogger @@ -15,15 +21,19 @@ std_log = logger.logs[DemoLogger::MultiLogger::STDOUT] expect(std_log.class).to eql DemoLogger::StdoutLogger expect(std_log.level).to eql Logger::DEBUG + + email_log = logger.logs[DemoLogger::MultiLogger::EMAIL] + expect(email_log.class).to eql DemoLogger::EmailLogger + expect(email_log.level).to eql Logger::FATAL end it 'creates all loggers based on method parameters' do # See config/config.yml config = { - demo_logger: { file: 'severe', stdout: 'severe' } } + demo_logger: { file: 'severe', stdout: 'severe', email: 'info' } } logger = DemoLogger::MultiLogger.new(config) - expect(logger.logs.length).to eq 2 + expect(logger.logs.length).to eq 3 file_log = logger.logs[DemoLogger::MultiLogger::FILE] expect(file_log.class).to eql DemoLogger::FileLogger @@ -32,6 +42,10 @@ std_log = logger.logs[DemoLogger::MultiLogger::STDOUT] expect(std_log.class).to eql DemoLogger::StdoutLogger expect(std_log.level).to eql Logger::FATAL + + email_log = logger.logs[DemoLogger::MultiLogger::EMAIL] + expect(email_log.class).to eql DemoLogger::EmailLogger + expect(email_log.level).to eql Logger::INFO end context 'logging levels' do @@ -45,6 +59,9 @@ # TODO: check email files it 'debug to all logs correctly' do + config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } + CleanConfig::Configuration.instance.merge!(config) + # TODO: Make the stdout capturing work. logger.debug('TEST-DEBUG') @@ -53,6 +70,9 @@ end it 'severe to all logs correctly' do + config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } + CleanConfig::Configuration.instance.merge!(config) + expected_contents = 'TEST-SEVERE' logger.severe(expected_contents) file_contents = File.read(log_file) From 744feadf79979ca2a074879005b4c337cb4f6881 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 19:54:43 -0800 Subject: [PATCH 20/25] Add email tests --- .gitignore | 2 ++ Rakefile | 7 ++++++- lib/demo_logger/email_logger.rb | 1 + spec/multi_logger_spec.rb | 11 ++++++++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0cb6eeb..b63a4e1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ /pkg/ /spec/reports/ /tmp/ +/email/ +/log/ diff --git a/Rakefile b/Rakefile index 8d54cba..41aef7a 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,12 @@ namespace :rm do task :log do FileUtils.rm_rf('log') end + + desc 'Remove ./email files' + task :email do + FileUtils.rm_rf('email') + end end desc 'Run style check, tests and build' -task full_build: [:'rm:log', :rubocop, :spec, :build] +task full_build: [:'rm:log', :'rm:email', :rubocop, :spec, :build] diff --git a/lib/demo_logger/email_logger.rb b/lib/demo_logger/email_logger.rb index 77272e7..762507a 100644 --- a/lib/demo_logger/email_logger.rb +++ b/lib/demo_logger/email_logger.rb @@ -3,6 +3,7 @@ module DemoLogger # TODO: This could share a common superclass with FileLogger + # TODO: Sending emails class EmailLogger < Logger attr_reader :email_file attr_accessor :send_mail diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index 82b8bb2..a03a6d1 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -48,6 +48,8 @@ expect(email_log.level).to eql Logger::INFO end + # TODO: have each test cleanup after itself. Right now the rake tasks + # are doing one big cleanup before the tests run. context 'logging levels' do config = { @@ -56,8 +58,8 @@ let(:logger) { DemoLogger::MultiLogger.new(config) } # We can check the file immediately because sync is turned on. let(:log_file) { logger.logs[DemoLogger::MultiLogger::FILE].log_file } + let(:email_file) { logger.logs[DemoLogger::MultiLogger::FILE].log_file } - # TODO: check email files it 'debug to all logs correctly' do config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } CleanConfig::Configuration.instance.merge!(config) @@ -67,6 +69,9 @@ file_contents = File.read(log_file) expect(file_contents).to be_empty + + email_contents = File.read(email_file) + expect(email_contents).to be_empty end it 'severe to all logs correctly' do @@ -75,8 +80,12 @@ expected_contents = 'TEST-SEVERE' logger.severe(expected_contents) + file_contents = File.read(log_file) expect(file_contents).to include(expected_contents) + + email_contents = File.read(email_file) + expect(email_contents).to include(expected_contents) end end end From e4ccd2b36188eaa05fbe0e31e704213661729cf4 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 19:59:02 -0800 Subject: [PATCH 21/25] Fix e2e test --- spec/e2e/core/config/config.yml | 2 ++ spec/e2e/core/lib/core/core_one.rb | 2 +- spec/e2e/core/spec/unit/core_spec.rb | 36 +++++++++++++++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/spec/e2e/core/config/config.yml b/spec/e2e/core/config/config.yml index 479d220..5043627 100644 --- a/spec/e2e/core/config/config.yml +++ b/spec/e2e/core/config/config.yml @@ -1,2 +1,4 @@ :demo_logger: :file: debug + :email: debug + :stdout: debug diff --git a/spec/e2e/core/lib/core/core_one.rb b/spec/e2e/core/lib/core/core_one.rb index 3e5ce39..dc7e058 100644 --- a/spec/e2e/core/lib/core/core_one.rb +++ b/spec/e2e/core/lib/core/core_one.rb @@ -7,7 +7,7 @@ class CoreOne attr_reader :logger def initialize - @logger = DemoLogger::FileLogger.new + @logger = DemoLogger::MultiLogger.new end end end diff --git a/spec/e2e/core/spec/unit/core_spec.rb b/spec/e2e/core/spec/unit/core_spec.rb index db5db20..6a6b3c3 100644 --- a/spec/e2e/core/spec/unit/core_spec.rb +++ b/spec/e2e/core/spec/unit/core_spec.rb @@ -3,14 +3,32 @@ module Core describe CoreOne do - # context 'demo_logger layers config' do - # it 'Core can override demo_logger configuration' do - # - # core_one = CoreOne.new - # - # puts "core_cone logger level: #{core_one.logger.level}" - # expect(core_one.logger.level).to eql(Logger::DEBUG) - # end - # end + context 'demo_logger layers config' do + it 'Core can override demo_logger configuration' do + + # See config/config.yml + # Because of the way Rspec loads/evals tests we have to babysit our + # configuration object. We would not have to do this outside of Rspec. + config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } + CleanConfig::Configuration.instance.merge!(config) + + core_one = CoreOne.new + + logger = core_one.logger + expect(logger.logs.length).to eq 3 + + file_log = logger.logs[DemoLogger::MultiLogger::FILE] + expect(file_log.class).to eql DemoLogger::FileLogger + expect(file_log.level).to eql Logger::INFO + + std_log = logger.logs[DemoLogger::MultiLogger::STDOUT] + expect(std_log.class).to eql DemoLogger::StdoutLogger + expect(std_log.level).to eql Logger::DEBUG + + email_log = logger.logs[DemoLogger::MultiLogger::EMAIL] + expect(email_log.class).to eql DemoLogger::EmailLogger + expect(email_log.level).to eql Logger::FATAL + end + end end end From 47c97a9b5395b8965b5a8ef0d891f293e924d4c0 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 20:10:23 -0800 Subject: [PATCH 22/25] Readme --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c9db5b..92d7a71 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/CivJ/demo_logger.svg?branch=zen_hw)](https://travis-ci.org/CivJ/demo_logger) + # demo_logger This logging library layers additional features on top of Ruby's [logger](http://ruby-doc.org/stdlib-2.2.3/libdoc/logger/rdoc/Logger.html) @@ -48,8 +50,11 @@ The tests at `spec/e2e/core/spec/unit` show what happens when you actually inclu into a new project. That is the appropriate location to test config value overrides and 'require' statements. Those tests should guarantee this project will work as expected from another project. -The `Core` project is just a dummy project that depends on `DemoLogger`. The only files of interest are: +The `Core` project is just a dummy project that depends on `demo-logger`. The only files of interest are: * spec/e2e/core/lib/core.rb * spec/e2e/core/spec/unit/core_spec.rb +## TODOs +Most of the obvious improvements that I am intentionally omitting are marked with TODO. Also, I have omitted the actual +sending of email, but have hopefully designed this in a way such that it would be easy to plug in. From 5879887c837a9d77ba29d8fc64153c833f5bcfe1 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sat, 6 Feb 2016 20:14:17 -0800 Subject: [PATCH 23/25] Readme --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 92d7a71..f086b3f 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,9 @@ The `Core` project is just a dummy project that depends on `demo-logger`. The on * spec/e2e/core/spec/unit/core_spec.rb ## TODOs -Most of the obvious improvements that I am intentionally omitting are marked with TODO. Also, I have omitted the actual -sending of email, but have hopefully designed this in a way such that it would be easy to plug in. +Most of the obvious improvements that I am intentionally omitting are marked with TODO. + +### Other omissions given that this is an interview question +* Actually sending email: It should be easy to plug this in. Let's talk about it! +* Integrating with a log aggregator (e.g. Logstash, Splunk) +* Making everything configurable From cb15c8968cfc7175d2966203f8ae7f9881827a27 Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sun, 7 Feb 2016 09:50:11 -0800 Subject: [PATCH 24/25] Readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f086b3f..f817295 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,13 @@ Tests only: `bundle exec rake spec` require 'demo_logger' logger = DemoLogger::DemoLogger.new ``` + +## Design Overview +I use a `MultiLogger` class to dispatch messages to `FileLogger`, `StdoutLogger` and `EmailLogger`. Each of these +classes depends on Ruby's built in `Logger` class. The logic is pretty simple, each class will always relieve all +the logging calls, but will ignore or execute them according to their configuration. Please see `lib/demo_logger` +for the implementation. Tests are located at `spec/`. + ## Configuration @@ -62,3 +69,4 @@ Most of the obvious improvements that I am intentionally omitting are marked wit * Actually sending email: It should be easy to plug this in. Let's talk about it! * Integrating with a log aggregator (e.g. Logstash, Splunk) * Making everything configurable +* Aging log files (the underlying library supports this) From 5842c5cdce30cbf8f0ac8fd6c44f443fa2cad40f Mon Sep 17 00:00:00 2001 From: john-crimmins Date: Sun, 7 Feb 2016 09:58:11 -0800 Subject: [PATCH 25/25] Heh. Fogot to close. --- lib/demo_logger/multi_logger.rb | 4 ++++ lib/demo_logger/stdout_logger.rb | 4 ++++ spec/multi_logger_spec.rb | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/lib/demo_logger/multi_logger.rb b/lib/demo_logger/multi_logger.rb index 637269a..974f0fc 100644 --- a/lib/demo_logger/multi_logger.rb +++ b/lib/demo_logger/multi_logger.rb @@ -62,6 +62,10 @@ def severe(message) @logs.each { |_type, log| log.fatal(message) } end + def close + @logs.each { |_type, log| log.close } + end + # rubocop:disable Style/TrailingWhitespace private diff --git a/lib/demo_logger/stdout_logger.rb b/lib/demo_logger/stdout_logger.rb index c29124b..4f22482 100644 --- a/lib/demo_logger/stdout_logger.rb +++ b/lib/demo_logger/stdout_logger.rb @@ -7,5 +7,9 @@ def initialize(level) super(STDOUT) self.level = level || Logger::WARN end + + # Don't close STDOUT. + def close + end end end diff --git a/spec/multi_logger_spec.rb b/spec/multi_logger_spec.rb index a03a6d1..936a383 100644 --- a/spec/multi_logger_spec.rb +++ b/spec/multi_logger_spec.rb @@ -25,6 +25,8 @@ email_log = logger.logs[DemoLogger::MultiLogger::EMAIL] expect(email_log.class).to eql DemoLogger::EmailLogger expect(email_log.level).to eql Logger::FATAL + + logger.close end it 'creates all loggers based on method parameters' do @@ -46,6 +48,8 @@ email_log = logger.logs[DemoLogger::MultiLogger::EMAIL] expect(email_log.class).to eql DemoLogger::EmailLogger expect(email_log.level).to eql Logger::INFO + + logger.close end # TODO: have each test cleanup after itself. Right now the rake tasks @@ -60,6 +64,7 @@ let(:log_file) { logger.logs[DemoLogger::MultiLogger::FILE].log_file } let(:email_file) { logger.logs[DemoLogger::MultiLogger::FILE].log_file } + after(:each) { logger.close } it 'debug to all logs correctly' do config = { demo_logger: { file: 'info', stdout: 'debug', email: 'severe' } } CleanConfig::Configuration.instance.merge!(config)