# Author:: Nicolas Pouillard . # Copyright:: Copyright (c) 2004, 2005 Uttk team. All rights reserved. # License:: LGPL # $Id: /w/fey/uttk/trunk/lib/uttk/logger.rb 21879 2006-02-19T01:31:34.473073Z pouillar $ require 'observer' require 'uttk/logger/severity' require 'uttk/logger/to_uttk_log' require 'uttk/logger/section_node' require 'uttk/logger/path' require 'uttk/logger/verbosity' require 'uttk/logger/backend' module Uttk class Logger include Observable OPTIONS = [ :ordered, :type ] MESSAGES = [ :new_leaf, :close ] class UnsupportedMessage < ArgumentError end class DuplicatedPath < Exception def initialize ( backend_path, path ) @backend_path = backend_path @path = path end def to_s path, bpath = @path.to_s, @backend_path.to_s path += ' vs ' + bpath if path != bpath "The path notification flow contains two leaves at the same path (#{path})" end end def initialize ( *observers ) @path = Logger::Path.new observers.flatten.each { |obs| add_observer(obs) } @sections = [] @section_tree = SectionNode.new('all') @severity_level = Severity::DEBUG @nb_log_msg = 0 @verbosity = Verbosity.new @verbosity_level = 0 @closed = false end def initialize_copy ( o ) super # FIXME share less information @path = o.path end def chpath ( path ) @path = path.dup end attr_reader :severity_level def severity_level=(severity_level) if severity_level.is_a?(String) severity_level = Severity.const_get(severity_level.to_s.upcase) end if Severity.lower <= severity_level and severity_level <= Severity.higher @severity_level = severity_level else raise(ArgumentError, "`#{severity_level}' - invalid severity level") end end attr_accessor :section_tree def active_section(active, *section_names) @sections = @section_tree.set_active_section(active, *section_names) nil end def active_section?(section_name) @sections.include?(section_name) end attr_reader :verbosity def verbosity=(verbosity) unless verbosity.is_a?(Verbosity) raise(ArgumentError, "`#{verbosity}' - must inherit from Verbosity") end @verbosity = verbosity end attr_reader :verbosity_level def verbosity_level=(verbosity_level) if verbosity_level < 0 raise(ArgumentError, "`#{verbosity_level}' - " + 'verbosity level must be positive or null') end @verbosity_level = verbosity_level end def log(severity_level=nil, *section_names, &block) severity_level ||= Severity.higher return true if severity_level < @severity_level if section_names.empty? format_log_message(severity_level, section_names, &block) else section_names.each do |s| if @sections.include?(s) format_log_message(severity_level, section_names, &block) break end end end end def format_log_message(severity_level, section_names, &block) message = block[] return if message.nil? name = Severity.label(severity_level) name += "_#@nb_log_msg" unless @nb_log_msg.zero? new_node(name) do if @verbosity_level == 0 message.to_uttk_log(self) else for i in 1..@verbosity_level do @verbosity.class.level_fields(i).each do |field| self[field] = @verbosity.send(field, severity_level, section_names) end end self['message'] = message end end @nb_log_msg += 1 end protected :format_log_message class Upper def initialize ( &block ) @called = false @block = block end def up return if @called @called = true @block[] end alias_method :call, :up alias_method :[], :up end def new_node ( node, options=nil, &block ) unless options.nil? options.each_key do |k| unless OPTIONS.include? k raise ArgumentError, "bad option: #{k}" end end end unless node.is_a? Symbol or node.is_a? Integer or node == '' node = node.to_s.to_sym end if block up_path = @path.dup else result = upper end begin @path << Segment.new(node, options) rescue Exception => ex if block secret_up up_path else result.up end raise ex end if block begin block[] ensure @path = up_path end else result end end def upper # The local variable up_path is very important !!! up_path = @path.dup Upper.new { @path = up_path } end def new_leaf ( x ) notif :new_leaf, @path.dup, x end def []= ( k, v ) v.to_uttk_log_with_key(k, self) end def << ( v ) v.to_uttk_log(self) end class BadMethodFormat < ArgumentError end def method_missing ( key, *a, &b ) str = key.to_s if str =~ /^(.*)=$/ self[$1.to_sym] = a[0] elsif b begin args = make_log_arguments(str) args += a self.log(*args, &b) rescue BadMethodFormat super end else super end end def make_log_arguments(str) if str =~ /^([A-Za-z0-9]+)(.*)$/ severity, tail = $1, $2 args = [] begin args << Severity.const_get(severity.upcase) rescue NameError raise(NameError, "`#{severity}' - unknown severity level") end while tail =~ /^_([A-Za-z0-9]+)(.*)$/ args << $1 tail = $2 end args else raise(BadMethodFormat, "`#{str}' - bad format") end end def path @path.dup end def path_size @path.size end def to_s "#<#{self.class} path=#{path}>" end alias :inspect :to_s # r = Logger.new # r.new_node(:root) # r.update(:new_node, [:another_path], :foo) # it's now correct def update ( msg, *args ) notif(msg, *args) if MESSAGES.include? msg end def notif ( *args ) changed notify_observers(*args) end private :notif, :notify_observers, :changed def close return if @closed @path = Logger::Path.new unless @path.empty? notif :close @closed = true end def self.make_notif ( n ) if n.size == 3 n else [:new_leaf, n[0].to_logger_path, n[1]] end end def self.make_notifs ( ns ) ns.map { |n| make_notif(n) } end end # class Logger end # module Uttk