Posts Tagged ‘logging’

Kleine Ruby Logger Klasse

by apoc · juni 13 2009 · leave a comment

Normalerweise sind die Artikel hier ja ziemlich umfangreich, was ja auch gut ist schließlich geht das Sixserv Blog ja schon sehr ins technische Detail. Doch diesmal wird es eher kurz, ich möchte eine kleine Logger Klasse vorstellen die ich in Ruby geschrieben habe und seither häufiger benutze.

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
class Logger
  def self.init
    @logfile = LOG_FILE
    @buffer = [] # logging lines for buffering
  end
 
  def self.<< msg
    msg = format msg
 
    # append message to buffer
    @buffer << msg
 
    # flush the buffer if possible
    self.flush
  end
 
  def self.flush
    return if @buffer.empty?
 
    # try to lock the file for writing
    log = File.new(@logfile, 'a+')
 
    if log.flock(File::LOCK_EX | File::LOCK_NB) == false
      # Logfile is locked
      puts "Logfile is locked!"
      log.close
      return false
    end
 
    # ==> not locked, write buffer to logfile
    while @buffer.length > 0
      log.puts @buffer.shift
    end
 
    log.flock(File::LOCK_UN)
    log.close
  end
 
  private
  def self.format msg
    time = Time.now.strftime '%H:%M:%S - %d.%m.%Y'
    "[#{time}] #{msg}"
  end
end

Nichts großes also, es gibt z.B. keine Log-Level usw. Außerdem kann man sich, wenn man etwas mehr Features braucht, auch eine Logging Bibliothek aus dem Gems bedienen. Die Anwendung ist ganz einfach:

1
2
3
4
5
6
7
8
require '/pfad/zu/logger.rb'
Logger.init
# das kann man dann überall im Code verteilen:
Logger << "Irgendeine Nachricht!"
# hat man eine große Anwendung mit vielen Prozessen/Threads
# kann man auch an strategischen Stellen ein
Logger.flush
# einbauen :)

Hier noch eine kleine Erweiterung die einen Log-Server implementiert. Er öffnet einen TCP Port, jeder der sich verbindet(z.B. per Telnet/Netcat) kann daraufhin die Log-Nachrichten mitlesen:

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
class Logger
  def self.init
    ...
    @port = LOG_PORT
    @query = []
    @query_mutex = nil
    @running = false
    @server = nil
  end
 
  def self.start_server
    @query_mutex = Mutex.new
    puts "Start logging console @ #{LOG_HOST}:#{LOG_PORT}"
 
    @server = TCPServer.new(LOG_HOST, LOG_PORT)
    clients = []
    @running = true
    Thread.start do
      client = []
      while @running do
        begin
          client = @server.accept_nonblock
          clients << client if client != nil
        rescue
        end
 
        begin
          message = nil
          @query_mutex.synchronize { message = @query.pop }
          if message != nil
            clients.each do |client|
                begin
                    client.puts message
                rescue
                    clients.delete client
                end 
            end
          end
        end while not @query.empty?
        sleep LOG_SERVER_INTERVAL
      end
      clients.each { |client| client.close }
      @server.close
      @server = nil
    end
  end
 
  def self.destroy
    @running = false
    @fs.close
 
    # wait until server closed
    true while @server != nil
  end
 
  def self.<< msg
    if @query_mutex != nil
      @query_mutex.synchronize do
        @query << format(msg)
      end
    end
    puts ">>> #{msg}"
  end  
 
  def self.flush
    # just wait until everything is sent/written
    if @query.length > 0
      true while @query.length != 0
    end
  end
 
  ...
end

So das wars schon, ich hoffe das ich das mit dem Mutex richtig gemacht habe und das hier überhaupt notwendig war, ich habe auf dem Gebiet nicht viel Erfahrung und würde mich über einen entsprechenden Kommentar freuen.
// I’m not sure I used the Mutex in this example correctly, I would appreciate a comment about this.

Tagged: , , ,