From: "lroland@..." Date: 2005-11-11T01:07:13+09:00 Subject: Re: Logging vs. Exceptions Here is parts of the code, newly rewritten from Perl to Ruby. The code is a basic scanner that uses clamav to scan a file for virus and logs errors using log4r. The 'scan' method handles the scan result and the 'talk' method speaks to clamav using a socket. The talk method may throw exceptions but, the scan function tries to grab these, logs whatever goes wrong and generally tries to be as exception safe as possible. Also I use a customized exception class that logs all exceptions using the same log4r object as the rest of the code. What I would like is to get some opinions/comments on is how to mingle logging and exception handling together in a usable and yet clean way (I personally do not feel that the code below is as clean as it could be but I am still looking for the correct division of responsibility between the logging and exception code) --------- require 'socket' require 'log4r' include Log4r class ClamScanner attr_accessor :socket, :result, :logger def initialize() @logger = Logger['test'] @socket = '/var/run/clamav/clamd.sock' end def check() res = talk("PING") if(res !~ /PONG$/) @logger.error("check failed: erroneous clam answer: #{res}") return false end return true end def scan( path ) if(@result) @logger.info("reusing previous scan result: <#{@result}> of: <#{path}>") else begin res = talk("SCAN #{path}") rescue ScannerException @logger.error("unable to scan message: #{path}") return nil end if(res =~ /: (.*) FOUND$/) @logger.info("virus: #{$1} in: #{path}") @result = $1; elsif(res =~ /: (Zip module failure) ERROR$/) @logger.info("broken zip in #{path}") @result = 'Broken.ZIP.Archive' elsif(res =~ /OK$/) @logger.info("no virus detected in: #{path}") @result = 'clean' else @logger.error("unknwon clam resource problem: #{$1}: when scanning: #{path}") return nil end end return @result end def talk( msg ) data = nil session = nil if msg.nil? raise ScannerException.new("method called with empty args") end begin session = UNIXSocket.new(@socket) session.send(msg,0) data = session.recvfrom(512)[0] # [0] ensures we only get clam's respons session.close() rescue raise ScannerException.new("unable to use socket: #{@socket}") end return data end end # exception class that logs using our log4r object class ScannerException < RuntimeError logger = nil def initialize( message ) @logger = Logger['test'] @logger.error(message) end end # create logger logger = Logger.new('test') outputter = Outputter.stdout outputter.formatter = PatternFormatter.new(:pattern => "[%l] %d :: %m") logger.outputters = outputter # test scanner clamav = ClamScanner.new(); if !clamav.check() logger.error("woop error") exit(1) end # scan this file file = '/path/to/test.file' res = clamav.scan( file ) --------- Regards Lars Roland