diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 6daf0401..1b54ec4b 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -21,6 +21,7 @@ require 'time' require 'safe_yaml/load' require 'English' require 'pathname' +require 'logger' # 3rd party require 'liquid' @@ -31,6 +32,7 @@ require 'toml' # internal requires require 'jekyll/version' require 'jekyll/utils' +require 'jekyll/log_adapter' require 'jekyll/stevenson' require 'jekyll/deprecator' require 'jekyll/configuration' @@ -116,7 +118,11 @@ module Jekyll end def self.logger - @logger ||= Stevenson.new + @logger ||= LogAdapter.new(Stevenson.new) + end + + def self.logger=(writer) + @logger = LogAdapter.new(writer) end # Public: File system root diff --git a/lib/jekyll/log_adapter.rb b/lib/jekyll/log_adapter.rb new file mode 100644 index 00000000..f22f47ad --- /dev/null +++ b/lib/jekyll/log_adapter.rb @@ -0,0 +1,104 @@ +module Jekyll + class LogAdapter + attr_reader :writer + + LOG_LEVELS = { + :debug => ::Logger::DEBUG, + :info => ::Logger::INFO, + :warn => ::Logger::WARN, + :error => ::Logger::ERROR + } + + # Public: Create a new instance of Jekyll's log writer + # + # writer - Logger compatible instance + # log_level - (optional, symbol) the log level + # + # Returns nothing + def initialize(writer, level = :info) + @writer = writer + self.log_level = level + end + + # Public: Set the log level on the writer + # + # level - (symbol) the log level + # + # Returns nothing + def log_level=(level) + writer.level = LOG_LEVELS.fetch(level) + end + + # Public: Print a jekyll debug message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def debug(topic, message = nil) + writer.debug(message(topic, message)) + end + + # Public: Print a jekyll message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def info(topic, message = nil) + writer.info(message(topic, message)) + end + + # Public: Print a jekyll message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def warn(topic, message = nil) + writer.warn(message(topic, message)) + end + + # Public: Print a jekyll error message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def error(topic, message = nil) + writer.error(message(topic, message)) + end + + # Public: Print a Jekyll error message and immediately abort the process + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail (can be omitted) + # + # Returns nothing + def abort_with(topic, message = nil) + error(topic, message) + abort + end + + private + + # Internal: Build a Jekyll topic method + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns the formatted message + def message(topic, message) + formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ') + end + + # Internal: Format the topic + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # + # Returns the formatted topic statement + def formatted_topic(topic) + "#{topic} ".rjust(20) + end + end +end diff --git a/lib/jekyll/stevenson.rb b/lib/jekyll/stevenson.rb index b50368c1..9a9f412e 100644 --- a/lib/jekyll/stevenson.rb +++ b/lib/jekyll/stevenson.rb @@ -1,102 +1,58 @@ module Jekyll - class Stevenson - attr_accessor :log_level - - LOG_LEVELS = { - debug: 0, - info: 1, - warn: 2, - error: 3 - } - - # Public: Create a new instance of Stevenson, Jekyll's logger - # - # level - (optional, symbol) the log level - # - # Returns nothing - def initialize(level = :info) - @log_level = level + class Stevenson < ::Logger + def initialize + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @logdev = $stdout + @formatter = proc do |severity, datetime, progname, msg| + "#{msg}" + end end - # Public: Print a jekyll debug message to stdout - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def debug(topic, message = nil) - $stdout.puts(message(topic, message)) if should_log(:debug) + def add(severity, message = nil, progname = nil, &block) + severity ||= UNKNOWN + @logdev = set_logdevice(severity) + + if @logdev.nil? or severity < @level + return true + end + progname ||= @progname + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.puts( + format_message(format_severity(severity), Time.now, progname, message)) + true end - # Public: Print a jekyll message to stdout - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def info(topic, message = nil) - $stdout.puts(message(topic, message)) if should_log(:info) + # Log a +WARN+ message + def warn(progname = nil, &block) + add(WARN, nil, progname.yellow, &block) end - # Public: Print a jekyll message to stderr - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def warn(topic, message = nil) - $stderr.puts(message(topic, message).yellow) if should_log(:warn) + # Log an +ERROR+ message + def error(progname = nil, &block) + add(ERROR, nil, progname.red, &block) end - # Public: Print a jekyll error message to stderr - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def error(topic, message = nil) - $stderr.puts(message(topic, message).red) if should_log(:error) + def close + # No LogDevice in use end - # Public: Print a Jekyll error message to stderr and immediately abort the process - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail (can be omitted) - # - # Returns nothing - def abort_with(topic, message = nil) - error(topic, message) - abort - end + private - # Public: Build a Jekyll topic method - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns the formatted message - def message(topic, message) - formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ') - end - - # Public: Format the topic - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # - # Returns the formatted topic statement - def formatted_topic(topic) - "#{topic} ".rjust(20) - end - - # Public: Determine whether the current log level warrants logging at the - # proposed level. - # - # level_of_message - the log level of the message (symbol) - # - # Returns true if the log level of the message is greater than or equal to - # this logger's log level. - def should_log(level_of_message) - LOG_LEVELS.fetch(log_level) <= LOG_LEVELS.fetch(level_of_message) + def set_logdevice(severity) + if severity > INFO + $stderr + else + $stdout + end end end end diff --git a/test/test_log_adapter.rb b/test/test_log_adapter.rb new file mode 100644 index 00000000..3dd3465e --- /dev/null +++ b/test/test_log_adapter.rb @@ -0,0 +1,59 @@ +require 'helper' + +class TestLogAdapter < Test::Unit::TestCase + class LoggerDouble + attr_accessor :level + end + + context "#log_level=" do + should "set the writers logging level" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + subject.log_level = :error + assert_equal Jekyll::LogAdapter::LOG_LEVELS[:error], subject.writer.level + end + end + + context "#debug" do + should "call #debug on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer) + stub(writer).debug('topic '.rjust(20) + 'log message') { true } + assert logger.debug('topic', 'log message') + end + end + + context "#info" do + should "call #info on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer) + stub(writer).info('topic '.rjust(20) + 'log message') { true } + assert logger.info('topic', 'log message') + end + end + + context "#warn" do + should "call #warn on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer) + stub(writer).warn('topic '.rjust(20) + 'log message') { true } + assert logger.warn('topic', 'log message') + end + end + + context "#error" do + should "call #error on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer) + stub(writer).error('topic '.rjust(20) + 'log message') { true } + assert logger.error('topic', 'log message') + end + end + + context "#abort_with" do + should "call #error and abort" do + logger = Jekyll::LogAdapter.new(LoggerDouble.new) + stub(logger).error('topic', 'log message') { true } + assert_raise(SystemExit) { logger.abort_with('topic', 'log message') } + end + end +end