My work involvs writing many small command line applications (scripts) in ruby. One of the common things among them is logging. The application should produce adequate log messages, so that one could follow the application flow. Ruby has a very good logger in standard library. You can use it like that:

1
2
3
4
5
6
7
8
9
10
require 'logger'

# initialize
logger = Logger.new(STDOUT)
logger.level = Logger::WARN

# write messtages
logger.debug("Created logger")
logger.info("Program started")
logger.warn("Nothing to do!")

But the applications most of the time are consisting from several classes. But at the same time we do want to use the same logger in every part of our application, we do not want to initialize the logger inside every class. That means we have either to pass the logger to the object, when it’s created, or user global variables ($logger). Either approach seemed not so comfortable for me.

In fact this is a perfect place for the singleton pattern. I have a following class now in my applications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require 'forwardable'
require 'logger'

class Log
	extend SingleForwardable

	def self.init( log: $stdout, verbose: 0 )
		@logger = Logger.new log
		@logger.level = Logger::ERROR - verbose
		@logger.formatter = proc do |severity, datetime, progname, msg|
			if log == $stdout
				"#{msg}\n"
			else
				"[#{datetime.strftime '%FT%T%:z'}] #{severity} #{msg}\n"
			end
		end
		@logger
	end

	def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unknown, :level

	private

	def self.logger
		@logger ||= self.init
	end
end

Now logger can be called from any place in the application and it doesn’t have to be initialized beforehand.

1
2
3
Log.debug("Created logger")
Log.info("Program started")
Log.warn("Nothing to do!")
comments powered by Disqus