Skip to content

Commit

Permalink
use y2*() builtins for logging
Browse files Browse the repository at this point in the history
  • Loading branch information
lslezak committed Feb 3, 2014
1 parent 66a9294 commit b72ae6e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 156 deletions.
96 changes: 32 additions & 64 deletions library/log/src/lib/log/y2logger.rb
@@ -1,77 +1,45 @@
# encoding: utf-8

require 'logger'
require 'singleton'
require 'socket'
require "logger"
require "singleton"

require "yast"

module Yast

# A Ruby Logger which logs in usual Yast y2log formatting
# A Ruby Logger which wraps Yast.y2*() calls
class Y2Logger < ::Logger

SEVERITY_MAPPING = {
DEBUG => 0,
INFO => 1,
WARN => 2,
ERROR => 3,
FATAL => 3,
UNKNOWN => 5
}

# redefine the format to the y2log format
def format_message(severity, timestamp, progname, msg)
# remove the function name from the caller location
location = caller(3).first.gsub(/:in `.*'$/, "")
"#{timestamp.strftime(datetime_format)} #{severity} #{Socket.gethostname}(#{Process.pid}) #{progname} #{location} #{msg}\n"
end

def initialize(*args)
super
self.datetime_format = "%Y-%m-%d %H:%M:%S"
self.progname = "[Ruby]"
# TODO: it does not support changing the level at runtime,
# e.g. via Shift-F7 magic key
self.level = ENV["Y2DEBUG"] == "1" ? ::Logger::DEBUG : ::Logger::INFO
end

private

# redefine severity formatting
def format_severity(severity)
"<#{SEVERITY_MAPPING[severity] || 5}>"
end
end

# Provides the global shared Y2logger instance writing to /var/log/YaST2/y2log
# (or ~/.y2log if the file is not writable).
#
# It can be used for logging external Ruby code into y2log
#
# @example Allow external code to log into y2log
# # this depends on the target library, see it's documentation how to set the logger
# FooBar::Logger.instance.log = YastLogger.instance.log
# Baz.set_logger(YastLogger.instance.log)
class YastLogger
include Singleton

Y2LOGFILE = "/var/log/YaST2/y2log"

attr_accessor :log

def initialize
# Yast compatibility - log to home if not running as root
# (of if the file is not writable)
if File.exist?(Y2LOGFILE)
log_file = File.writable?(Y2LOGFILE) ? Y2LOGFILE : "#{ENV['HOME']}/.y2log"
# location of the caller
CALL_FRAME = 2

def add(severity, progname = nil, message = nil, &block)
message = yield if block_given?

case severity
when DEBUG
Yast.y2debug(CALL_FRAME, message)
when INFO
Yast.y2milestone(CALL_FRAME, message)
when WARN
Yast.y2warning(CALL_FRAME, message)
when ERROR
Yast.y2error(CALL_FRAME, message)
when FATAL
Yast.y2error(CALL_FRAME, message)
when UNKNOWN
Yast.y2internal(CALL_FRAME, message)
else
log_file = File.writable?(File.dirname(Y2LOGFILE)) ? Y2LOGFILE : "#{ENV['HOME']}/.y2log"
Yast.y2internal(CALL_FRAME, "Unknown error level #{severity}: Error: #{message}")
end
end

# when creating the log file make sure it is readable only by the user
# (it might contain sensitive data like passwords, registration code, etc.)
File.write(log_file, "", { :perm => 0600 }) unless File.exist?(log_file)

@log = Yast::Y2Logger.new(log_file)
def initialize(*args)
# do not write to any file, the actual logging is implemented in add()
super(nil)
# process also debug messages but might not be logged in the end
self.level = ::Logger::DEBUG
end
end

Expand All @@ -90,7 +58,7 @@ def initialize
# end
module Logger
def log
YastLogger.instance.log
Y2Logger.instance
end

def self.included(base)
Expand Down
113 changes: 21 additions & 92 deletions library/log/test/y2logger_test.rb
Expand Up @@ -2,114 +2,43 @@

require_relative "../src/lib/log/y2logger.rb"

# for logging into a string instead of a file
require "stringio"
# gethostname
require "socket"

module Yast
describe Y2Logger do
before do
# log into a string to easily test the result
@log = StringIO.new
@test_logger = Y2Logger.new(@log)
end

it "logs the passed message" do
@test_logger.info "@@@ Testing @@@"
expect(log).to match "@@@ Testing @@@"
end

it "logs with [Ruby] component" do
@test_logger.info "Testing"
expect(log).to match "[Ruby]"
end

it "logs info messages with '<1>' level" do
@test_logger.info "Testing"
expect(log).to match "<1>"
end

it "logs warnings with '<2>' level" do
@test_logger.warn "Testing"
expect(log).to match "<2>"
end

it "logs errors with '<3>' level" do
@test_logger.error "Testing"
expect(log).to match "<3>"
end

it "logs fatal errors with '<3>' level" do
@test_logger.fatal "Testing"
expect(log).to match "<3>"
end

it "logs the hostname" do
@test_logger.info "Testing"
expect(log).to match Socket.gethostname
end
TEST_MESSAGE = "Testing"

it "logs the process ID (PID)" do
@test_logger.info "Testing"
expect(log).to match "(#{Process.pid})"
before do
@test_logger = Y2Logger.instance
end

it "logs the file location into the log" do
@test_logger.info "Testing"
expect(log).to match "#{__FILE__}:#{__LINE__ - 1}"
it "logs debug messages via y2debug()" do
Yast.should_receive(:y2debug).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.debug TEST_MESSAGE
end

it "logs in y2log compatible format" do
@test_logger.info "Testing"
expect(log).to match /\A\d+-\d+-\d+ \d+:\d+:\d+ <1> #{Socket.gethostname}\(#{Process.pid}\) \[Ruby\] #{__FILE__}:\d+ Testing$/
it "logs info messages via y2milestone()" do
Yast.should_receive(:y2milestone).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.info TEST_MESSAGE
end

context "when Y2DEBUG is not set" do
before do
ENV.stub(:[]).with("Y2DEBUG").and_return(nil)
@test_logger = Y2Logger.new(@log)
end

it "does not log debug messages" do
@test_logger.debug "Testing"
expect(log).to eq ""
end

it "logs info messages with '<1>' level" do
@test_logger.info "Testing"
expect(log).to match "<1>"
end
it "logs warnings via y2warning()" do
Yast.should_receive(:y2warning).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.warn TEST_MESSAGE
end

context "when Y2DEBUG is set" do
before do
ENV.stub(:[]).with("Y2DEBUG").and_return("1")
@test_logger = Y2Logger.new(@log)
end

it "logs debug messages with '<0>' level" do
@test_logger.debug "Testing"
expect(log).to match "<0>"
end

it "logs info messages with '<1>' level" do
@test_logger.info "Testing"
expect(log).to match "<1>"
end
it "logs errors via y2error()" do
Yast.should_receive(:y2error).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.error TEST_MESSAGE
end

# helper method to read the logged value
def log
# read the created log from string
@log.rewind
@log.read
it "logs fatal errors via y2error()" do
Yast.should_receive(:y2error).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.fatal TEST_MESSAGE
end
end

describe YastLogger do
it "returns a Logger class instance" do
expect(YastLogger.instance.log).to be_kind_of(::Logger)
it "handles a message passed via block" do
Yast.should_receive(:y2milestone).with(Y2Logger::CALL_FRAME, TEST_MESSAGE)
@test_logger.info { TEST_MESSAGE }
end
end

Expand Down

0 comments on commit b72ae6e

Please sign in to comment.