Tanga.com is now using Spread for logging. Right now, only mongrel / rails and lighttpd are logging to spread. It’s cool because:
- The database and application servers don’t need to waste time logging files to disk, they just make one function call that mutlicasts the log data to spread and that’s it.
- One or more dedicated (or semi-dedicated) machines can listen to the spread network and capture the log data and write it to the log files.
- Unified logging mechanism.
- Should scale up very well.
One thing I’m curious about—it would be neat to somehow extend syslog to be able to write to the spread network.
I had to write two small pieces of software to get the logging to spread.
- For lighttpd, here’s a patch against 1.4.16
- For mongrel / rails, below is a small Logger class that’s a dropin replacement for the standard logger. It’s largely stolen from Eric Hodel’s SyslogLogger. You’ll need to have the rb_spread library as well.
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
require 'logger' require 'spread' class SpreadLogger LOGGER_MAP = { :unknown => :alert, :fatal => :err, :error => :warning, :warn => :notice, :info => :info, :debug => :debug, } LOGGER_LEVEL_MAP = {} LOGGER_MAP.each_key do |key| LOGGER_LEVEL_MAP[key] = Logger.const_get key.to_s.upcase end LEVEL_LOGGER_MAP = {} LOGGER_LEVEL_MAP.invert.each do |level, severity| LEVEL_LOGGER_MAP[level] = LOGGER_MAP[severity] end def self.make_methods(meth) eval <<-EOM, nil, __FILE__, __LINE__ + 1 def #{meth}(message = nil) begin return true if #{LOGGER_LEVEL_MAP[meth]} < 1 log_message(#{LOGGER_LEVEL_MAP[meth]}, message) rescue Exception => e end return true end def #{meth}? @level <= Logger::#{meth.to_s.upcase} end EOM end LOGGER_MAP.each_key do |level| make_methods level end attr_accessor :level def log_message severity, msg begin initialize_spread msg = clean(msg) msg = "#{Time.now.strftime("%b %d %H:%M:%S")} #{Socket.gethostname.split('.').first} rails[#{$PID}]: #{msg.gsub(/\n/, '').lstrip} \n" @spread.multicast(msg, @group, 1) if @spread rescue Exception => e @spread = nil end end def initialize_spread @spread ||= Spread.new @server, @my_name rescue Exception => e puts 'couldnt initialize spread' puts e.inspect end def initialize(server, group) @level = Logger::DEBUG @my_name = `hostname`.chomp @group = group @server = server initialize_spread end def add(severity, message = nil, progname = nil, &block) severity ||= Logger::DEBUG @progname = progname || "tanga-mongrel" return true if severity < @level return true end def silence(temporary_level = Logger::ERROR) old_logger_level = @level @level = temporary_level yield ensure @level = old_logger_level end private def clean(message) message = message.to_s.dup message.strip! message.gsub!(/%/, '%%') message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes return message end end |
Sorry, comments are closed for this article.