Good afternoon.
I want to replace the Logger from Ruby core by my own simplistic Module “BasicLogging”, after years, when I had to admit that I am anyway never using
all the features of the Logger class: It is the same formater for everything and I had never use for object- or class-specific configurations. This facilitates the configuration of the logger from an application-wide configuration. I can still “mute” individual objects/classes to reduce the output.
The whole module is attached below this post.
Chances are, I oversee a bunch of problems with my code. Programming is
just a hobby. Are there obvious glitches, which I must address? In one of
my programs, my module appears to work just as I like it to.
Here a usage example for a class-level logger:
------------
Array.extend(BasicLogging)
Array.set_level(BasicLogging::INFO)
Array.info('TEST')
-----------
output:
Array [class] info 16:22:26:662619: TEST
Object-level:
------------
ar = Array.new
ar.extend(BasicLogging)
# --- no output, default level is UNKNOWN :
l = __LINE__
ar.debug(l.next.to_s << ': debug-test 0')
# output on level debug
ar.set_level(BasicLogging::DEBUG)
l = __LINE__
ar.debug(l.next.to_s << ': debug-test 1')
--------------
output:
Array debug 16:22:26:662894: 124: debug-test 1
The file basic_logging.rb contains more test-code at the bottom. Thank you
for any comments. Shoot at will.
---------------------- basic_logging.rb -------------------
#!/bin/env ruby
#encoding: UTF-8
=begin /***************************************************************************
* ©2023-2023, Michael Uplawski <news:
michael.uplawski@uplawski.eu> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the WTFPL 2.0 or later, see *
* <
http://www.wtfpl.net/about/> *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
***************************************************************************/ =end
#
# Simplified logging.
# See example code at the bottom of this file.
# Execute this file to see the output.
module BasicLogging
DEBUG = 0
INFO = 1
WARN = 2
ERROR = 3
FATAL = 4
UNKNOWN = nil
# this is mainly for the translation of method calls into log levels
Levels = {:debug => DEBUG, :info => INFO, :warn => WARN, :error => ERROR,
:fatal => FATAL, :unknown => UNKNOWN}
@@log_level = UNKNOWN
@@target = STDOUT
@@muted = []
# do not log, if caller is obj (class or instance)
def self.mute(obj)
name = obj.class == Class ? obj.name.dup : obj.class.name
@@muted << name
end
def self.is_muted?(obj)
name = obj.class == Class ? obj.name.dup : obj.class.name
@@muted.include?(name)
end
# set the log level
def set_level(lv)
if lv.respond_to?(:to_str)
lv = Levels[lv.to_sym]
end
if(!lv || (lv.respond_to?(:to_int) && lv >= DEBUG && lv <= FATAL) )
@@log_level = lv
else
STDERR.puts __FILE__.dup << ": ERROR : invalid log level \"" << lv.to_s << "\""
STDERR.puts "Keepinng old log level " << Levels.keys.detect {| k| Levels[k] == @@log_level}.to_s
end
end
# set the log target
def set_target(tg)
if tg.respond_to?(:to_io)
@@target = tg
elsif(!File::exist?(tg) || ( File.file?(tg) && File.writable?(tg) ) )
@@target = File.open(tg, 'w+')
else
STDERR.puts __FILE__.dup << ': ERROR : target ' << tg << ' cannot be set'
STDERR.puts "Keeping old target " << @@target.inspect
return
end
end
# Output of log messages, depending on the log level
# and the name of the alias method which is actually called.
def log(message)
if !BasicLogging.is_muted?(self)
# how has this method been called?
mlevel = __callee__
if Levels.has_key?(mlevel) && Levels[mlevel] <= FATAL
# output only for levels equal or above the value that corresponds to
# the calling alias.
format_log( message, mlevel) if @@log_level && Levels[mlevel] >= @@log_level
else
STDERR.puts __FILE__.dup << ": ERROR : invalid log level \"" << mlevel.to_s << "\""
end
end
end
alias :debug :log
alias :info :log
alias :warn :log
alias :error :log
alias :fatal :log
private
# 1 format for all loggers.
def format_log(message, mlevel)
# indicate if a class or an instance of a class is calling.
name = self.class == Class ? self.name.dup << ' [class]' : self.class.name
@@target.puts '' << name << ' ' << mlevel.to_s << ' ' << Time.now.strftime("%H:%M:%S:%6N") << ': ' << message
end
end
#---------test: execute file----------
if $0 == __FILE__
Array.extend(BasicLogging)
Array.set_level(BasicLogging::INFO)
Array.info('TEST')
ar = Array.new
ar.extend(BasicLogging)
# --- no output :
l = __LINE__
ar.debug(l.next.to_s << ': debug-test 0')
# output
ar.set_level(BasicLogging::DEBUG)
l = __LINE__
ar.debug(l.next.to_s << ': debug-test 1')
obj = Object.new
obj.extend(BasicLogging)
obj.set_level(BasicLogging::DEBUG)
puts "--------debug-----------"
obj.debug('debug')
obj.info('info')
obj.warn('warn')
obj.error('error')
obj.fatal('fatal')
puts "--------info-----------"
obj.set_level("info")
obj.debug('debug')
obj.info('info')
obj.warn('warn')
obj.error('error')
obj.fatal('fatal')
puts "--------fatal-----------"
obj.set_level("fatal")
obj.debug('debug')
obj.info('info')
obj.warn('warn')
obj.error('error')
obj.fatal('fatal')
puts "--------UNKNOWN-----------"
obj.set_level(nil)
obj.debug('debug')
obj.info('info')
obj.warn('warn')
obj.error('error')
obj.fatal('fatal')
puts " ------ Output into file ----"
obj.set_target "/tmp/test_log.log"
puts " ------ INFO -----------"
obj.set_level BasicLogging::INFO
obj.info('info output')
obj.info('info output 2')
puts "---------- invalid -------"
obj.set_target "/dev/sr0"
obj.set_level "power"
end
# EOF
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)