Jonathan Gnagy's Blog

Rails for everyone!

Ruby Syslog Server

I uploaded a very rough draft of a syslog server written in Ruby. Here’s a link to it. I talked about it in a previous blog post, and I think now its ready to show the world. Here’s a quick breakdown of some of the key features:

  • Uses ActiveRecord for DB backend
  • Heavily Multithreaded
    • Makes extensive use of Queues and Mutexes
    • Works in Ruby 1.9, JRuby, and Mostly in MacRuby for Native threads. (Obviously) Uses green threads in Ruby 1.8
    • Utilizes Thread Pools to maintain a set number of Threads
  • Listens for UDP, TCP, or both.
  • Uses a YAML config file
  • Fully documented with RDoc
  • Performs exceptionally under heavy load. I tested 10,000+ simultaneous connections with the included test client (under /test/).
  • Written with MVC in mind. (I say “in mind” because there really is no “view” portion to this app)

That said, its still pretty new, and I’m still working on it. I welcome any comments about the code or any bugs encountered. Keep in mind that this SHOULD NOT BE USED IN PRODUCTION. I offer no warranty, implied or otherwise. I license this code under the GPLv3, even if I forgot to include a copy of the license in the tarball.

All-in-all, its actually a pretty simple app. Not that much actual code involved; just a little cleverness on how to process things one piece at a time. Here’s some of my favorite code, along with some parts that help it make more sense:

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
# ...
        # This methods tells the server to pull all logs from the LogQueue and 'process' them.
        # Essentially, this moves logs from the incoming LogQueue to a process ThreadPool for
        # controlled processing. This method prevents an insurge of messages from eating up all
        # the server's CPU by limiting the number of logs processed at a time. After the log is
        # parsed in the process pool, it is queued up for storage in the storage ThreadPool.
        def process_logs
                until @log_queue.empty?
                        @queue_flushed = false if @queue_flushed
                        log = pop_log
                        @process_pool.add_job {
                                puts "Processing Log: #{log.object_id.to_s(16)}"
                                parsed_log = @parser.parse log
                                @storage_pool.add_job {@storage.store_log parsed_log}
                        }
                end
                if !@queue_flushed
                        puts "Log Queue Flush Complete @ #{Time.now}" if !logs_queued?
                        @queue_flushed = true
                end
        end
        
        # Utilizes the LogMutex to synchronize initial storage of log packets in the LogQueue
        def enqueue_log(log_data)
                @mutex.synchronize { @log_queue.push log_data }
        end
        
        # Utilizes the LogMutex to synchronize removing log packets from the LogQueue
        def pop_log
                return @mutex.synchronize { @log_queue.pop }
        end
        
        # Utilizes the LogMutex to synchronize checking how many logs are currently queued in the
        # LogQueue.
        def logs_queued?
                return @mutex.synchronize { @log_queue.size > 0 }
        end
# ...

Hopefully, with more work, this could have some serious potential.

Comments:
Add a comment:
Please leave following field blank:
SubmitSubmit    HelpHelp