Skip to content

Commit

Permalink
Merge branch 'huydx-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
jodosha committed Jan 26, 2015
2 parents 46aa113 + 9bc8ea9 commit 290a47d
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/lotus.rb
@@ -1,6 +1,7 @@
require 'lotus/version'
require 'lotus/application'
require 'lotus/container'
require 'lotus/logger'

# A complete web framework for Ruby
#
Expand Down
12 changes: 12 additions & 0 deletions lib/lotus/loader.rb
Expand Up @@ -2,6 +2,7 @@
require 'lotus/utils/kernel'
require 'lotus/utils/string'
require 'lotus/routes'
require 'lotus/logger'
require 'lotus/routing/default'
require 'lotus/action/cookies'
require 'lotus/action/session'
Expand Down Expand Up @@ -40,6 +41,7 @@ def configure_frameworks!
_configure_model_framework! if defined?(Lotus::Model)
_configure_controller_framework!
_configure_view_framework!
_configure_static_variables!
end

def _configure_controller_framework!
Expand Down Expand Up @@ -87,6 +89,16 @@ def _configure_model_framework!
end
end

def _configure_static_variables!
_initialize_logger!
end

def _initialize_logger!
unless application_module.const_defined?('Logger', false)
logger = Lotus::Logger.new(application_module.to_s)
application_module.const_set('Logger', logger)
end
end

def load_frameworks!
_load_view_framework!
Expand Down
94 changes: 94 additions & 0 deletions lib/lotus/logger.rb
@@ -0,0 +1,94 @@
require 'logger'
require 'lotus/utils/string'

module Lotus
# Lotus default logger
#
# Implement with the same interface of ruby std ::Logger
# Opts out the logdev parameter and always use STDOUT as outout device
#
# Lotus logger also has the app tag concept, which used to identify
# which application the log come from.
#
# Lotus Logger default comes with Lotus application
# and uses name of highest module namespace as app_tag
#
# When stands alone, Lotus Logger tries to infer app tag from highest namespace
# When has no namespace, Lotus Logger takes [Shared] as default app tag
#
# @example
# #1 Logger with namespace
# module TestApp
# class AppLogger << Lotus::Logger; end
# def log
# Applogger.new.info('foo')
# #=> output: I, [2015-01-10T21:55:12.727259 #80487] INFO -- [TestApp] : foo
# end
# end
#
# #2 Logger without namespace
# class AppLogger < Lotus::Logger
# end
# Applogger.new.info('foo')
# #=> output: I, [2015-01-10T21:55:12.727259 #80487] INFO -- [Shared] : foo
#
# #3 Logger inside a lotus application
# module LotusModule
# class App < Lotus::Application
# load!
# end
# end
# LotusModule::Logger.info('foo')
# => output: I, [2015-01-10T21:55:12.727259 #80487] INFO -- [LotusModule] : foo
#
# @see ::Logger
#
# @since 0.2.1
class Logger < ::Logger

attr_accessor :app_tag

# Override Ruby's Logger#initialize
#
# @param logdev is default to STDOUT
#
# @since 0.2.1
def initialize(app_tag=nil, *args)
super(STDOUT, *args)
@app_tag = app_tag
@formatter = Lotus::Logger::Formatter.new.tap { |f| f.app_tag = self.app_tag }
end

# app_tag is the identification of current app
# app_tag is default to use highest namespace if current namespace
# if app_tag is blank, lotus use default app_tag 'shared'
# @param logdev is default to STDOUT
#
# @since 0.2.1
def app_tag
@app_tag || _app_tag_from_namespace || _default_app_tag
end

class Formatter < ::Logger::Formatter
attr_accessor :app_tag

def call(severity, time, progname, msg)
time = time.utc
progname = "[#{@app_tag}] #{progname}"
super(severity, time, progname, msg)
end
end

private
def _app_tag_from_namespace
class_name = self.class.name
return nil unless class_name.index('::')

Utils::String.new(class_name).namespace
end

def _default_app_tag
'Shared'
end
end
end
14 changes: 14 additions & 0 deletions test/loader_test.rb
Expand Up @@ -15,6 +15,7 @@
assert defined?(CoffeeShop::Entity), 'expected CoffeeShop::Entity'
assert defined?(CoffeeShop::Repository), 'expected CoffeeShop::Repository'
assert defined?(CoffeeShop::Presenter), 'expected CoffeeShop::Presenter'
assert defined?(CoffeeShop::Logger), 'expected CoffeeShop::Logger'
end

it 'generates per application classes' do
Expand Down Expand Up @@ -74,6 +75,19 @@
@application.middleware.must_be_kind_of(Lotus::Middleware)
end
end

describe 'logger' do
it 'has app module name along with log output' do
output =
stub_stdout_constant do
module CocacolaShop
class Application < Lotus::Application; load!; end
end
CocacolaShop::Logger.info 'foo'
end
output.must_match /CocacolaShop/
end
end
end

# describe 'finalization' do
Expand Down
73 changes: 73 additions & 0 deletions test/logger_test.rb
@@ -0,0 +1,73 @@
require 'test_helper'

describe Lotus::Logger do

before do
#clear defined class
Object.send(:remove_const, :TestLogger) if Object.constants.include?(:TestLogger)
end

it 'like std logger, sets log level to info by default' do
class TestLogger < Lotus::Logger; end
TestLogger.new.info?.must_equal true
end

it 'always use STDOUT' do
output =
stub_stdout_constant do
class TestLogger < Lotus::Logger; end
logger = TestLogger.new
logger.info('foo')
end
output.must_match /foo/
end

it 'has app_tag when log' do
output =
stub_stdout_constant do
module App; class TestLogger < Lotus::Logger; end; end
logger = App::TestLogger.new
logger.info('foo')
end
output.must_match /App/
end

it 'has default app tag when not in any namespace' do
class TestLogger < Lotus::Logger; end
TestLogger.new.send(:app_tag).must_equal 'Shared'
end

it 'infers apptag from namespace' do
module App2
class TestLogger < Lotus::Logger;end
class Bar
def hoge
TestLogger.new.send(:app_tag).must_equal 'App2'
end
end
end
App2::Bar.new.hoge
end

it 'uses custom app_tag from override class' do
class TestLogger < Lotus::Logger; def app_tag; 'bar'; end; end
output =
stub_stdout_constant do
TestLogger.new.info('')
end
output.must_match /bar/
end

it 'has format "#{Severity}, [%Y-%m-%dT%H:%M:%S.%6N #{Pid}] #{Severity} -- [#{app_tag}] : #{message}\n"' do
format = "%Y-%m-%dT%H:%M:%S.%6N "
stub_time_now do
strtime = Time.now.strftime(format)
output =
stub_stdout_constant do
class TestLogger < Lotus::Logger;end
TestLogger.new.info('foo')
end
output.must_equal "I, [1988-09-01T00:00:00.000000 ##{Process.pid}] INFO -- [Shared] : foo\n"
end
end
end
31 changes: 30 additions & 1 deletion test/test_helper.rb
Expand Up @@ -105,5 +105,34 @@ def dependencies
end

DependenciesReporter.new.run
require 'fixtures'

def stub_stdout_constant
begin_block = <<-BLOCK
original_verbosity = $VERBOSE
$VERBOSE = nil
origin_stdout = STDOUT
STDOUT = StringIO.new
BLOCK
TOPLEVEL_BINDING.eval begin_block

yield
return_str = STDOUT.string

ensure_block = <<-BLOCK
STDOUT = origin_stdout
$VERBOSE = original_verbosity
BLOCK
TOPLEVEL_BINDING.eval ensure_block

return_str
end


def stub_time_now
Time.stub :now, Time.utc(1988, 9, 1, 0, 0, 0) do
yield
end
end

require 'fixtures'

0 comments on commit 290a47d

Please sign in to comment.