diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25e7585 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Gemfile.lock +.bundle diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..4b1d255 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source :rubygems + +gemspec + +gem 'syslog_proto', :path => '../syslog_proto' \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cb470d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2011 Eric Lindvall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8193bf --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Remote Syslog Logger + +This library providers an [ActiveSupport][] compatible logger that logs +directly to a remote syslogd via UDP. + +[ActiveSupport]: http://as.rubyonrails.org/ + + +# Installation + +The easiest way to install `remote_syslog_logger` is with RubyGems: + + $ [sudo] gem install remote_syslog_logger + + +# Usage + + + >> require 'remote_syslog_logger' + => true + >> + + +# Source + +Remote Syslog Logger is available on GitHub, which can be browsed at: + + + +and cloned with: + + $ git clone git://github.com/eric/remote_syslog_logger.git + + +# Contributing + +Once you've made your great commits: + + 1. [Fork][fk] `remote_syslog_logger` + 2. Create a topic branch - `git checkout -b my_branch` + 3. Push to your branch - `git push origin my_branch` + 4. Create a Pull Request or an [Issue][is] with a link to your branch + 5. That's it! + +You might want to checkout Resque's [Contributing][cb] wiki page for information +on coding standards, new features, etc. + + +# License + +Copyright (c) 2011 Eric Lindvall. See [LICENSE][] for details. + + +[cb]: https://wiki.github.com/defunkt/resque/contributing +[fk]: http://help.github.com/forking/ +[is]: https://github.com/eric/remote_syslog_logger/issues +[LICENSE]: https://github.com/eric/remote_syslog_logger/blob/master/LICENSE diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..d4f7da8 --- /dev/null +++ b/Rakefile @@ -0,0 +1,150 @@ +require 'rubygems' +require 'rake' +require 'date' + +############################################################################# +# +# Helper functions +# +############################################################################# + +def name + @name ||= Dir['*.gemspec'].first.split('.').first +end + +def version + line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/] + line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] +end + +def date + Date.today.to_s +end + +def rubyforge_project + name +end + +def gemspec_file + "#{name}.gemspec" +end + +def gem_file + "#{name}-#{version}.gem" +end + +def replace_header(head, header_name) + head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"} +end + +############################################################################# +# +# Standard tasks +# +############################################################################# + +task :default => :test + +require 'rake/testtask' +Rake::TestTask.new(:test) do |test| + test.libs << 'lib' << 'test' + test.pattern = 'test/**/test_*.rb' + test.verbose = true +end + +desc "Generate RCov test coverage and open in your browser" +task :coverage do + require 'rcov' + sh "rm -fr coverage" + sh "rcov test/test_*.rb" + sh "open coverage/index.html" +end + +require 'rake/rdoctask' +Rake::RDocTask.new do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = "#{name} #{version}" + rdoc.rdoc_files.include('README*') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +desc "Open an irb session preloaded with this library" +task :console do + sh "irb -rubygems -r ./lib/#{name}.rb" +end + +############################################################################# +# +# Custom tasks (add your own tasks here) +# +############################################################################# + + + +############################################################################# +# +# Packaging tasks +# +############################################################################# + +desc "Create tag v#{version} and build and push #{gem_file} to Rubygems" +task :release => :build do + unless `git branch` =~ /^\* master$/ + puts "You must be on the master branch to release!" + exit! + end + sh "git commit --allow-empty -a -m 'Release #{version}'" + sh "git tag v#{version}" + sh "git push origin master" + sh "git push origin v#{version}" + sh "gem push pkg/#{name}-#{version}.gem" +end + +desc "Build #{gem_file} into the pkg directory" +task :build => :gemspec do + sh "mkdir -p pkg" + sh "gem build #{gemspec_file}" + sh "mv #{gem_file} pkg" +end + +desc "Generate #{gemspec_file}" +task :gemspec => :validate do + # read spec file and split out manifest section + spec = File.read(gemspec_file) + head, manifest, tail = spec.split(" # = MANIFEST =\n") + + # replace name version and date + replace_header(head, :name) + replace_header(head, :version) + replace_header(head, :date) + #comment this out if your rubyforge_project has a different name + replace_header(head, :rubyforge_project) + + # determine file list from git ls-files + files = `git ls-files`. + split("\n"). + sort. + reject { |file| file =~ /^\./ }. + reject { |file| file =~ /^(rdoc|pkg)/ }. + map { |file| " #{file}" }. + join("\n") + + # piece file back together and write + manifest = " s.files = %w[\n#{files}\n ]\n" + spec = [head, manifest, tail].join(" # = MANIFEST =\n") + File.open(gemspec_file, 'w') { |io| io.write(spec) } + puts "Updated #{gemspec_file}" +end + +desc "Validate #{gemspec_file}" +task :validate do + libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"] + unless libfiles.empty? + puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir." + exit! + end + unless Dir['VERSION*'].empty? + puts "A `VERSION` file at root level violates Gem best practices." + exit! + end +end diff --git a/lib/remote_syslog_logger.rb b/lib/remote_syslog_logger.rb new file mode 100644 index 0000000..d5d9ac6 --- /dev/null +++ b/lib/remote_syslog_logger.rb @@ -0,0 +1,11 @@ + +require 'remote_syslog_logger/udp_sender' +require 'logger' + +module RemoteSyslogLogger + VERSION = '1.0.0' + + def self.new(remote_hostname, remote_port, options = {}) + Logger.new(RemoteSyslogLogger::UdpSender.new(remote_hostname, remote_port, options)) + end +end \ No newline at end of file diff --git a/lib/remote_syslog_logger/udp_sender.rb b/lib/remote_syslog_logger/udp_sender.rb new file mode 100644 index 0000000..f7d46dd --- /dev/null +++ b/lib/remote_syslog_logger/udp_sender.rb @@ -0,0 +1,35 @@ +require 'socket' +require 'syslog_proto' + +module RemoteSyslogLogger + class UdpSender + def initialize(remote_hostname, remote_port, options = {}) + @remote_hostname = remote_hostname + @remote_port = remote_port + + @socket = UDPSocket.new + @packet = SyslogProto::Packet.new + + local_hostname = options[:local_hostname] || (Socket.gethostname rescue `hostname`.chomp) + local_hostname = 'localhost' if local_hostname.nil? || local_hostname.empty? + @packet.hostname = local_hostname + + @packet.facility = options[:facility] || 'user' + @packet.severity = options[:severity] || 'notice' + @packet.tag = options[:program] || "#{File.basename($0)}[#{$$}]" + end + + def transmit(message) + packet = @packet.dup + packet.content = message + @socket.send(packet.assemble, 0, @remote_hostname, @remote_port) + end + + # Make this act a little bit like an `IO` object + alias_method :write, :transmit + + def close + @socket.close + end + end +end \ No newline at end of file diff --git a/remote_syslog_logger.gemspec b/remote_syslog_logger.gemspec new file mode 100644 index 0000000..251ac8d --- /dev/null +++ b/remote_syslog_logger.gemspec @@ -0,0 +1,67 @@ +## This is the rakegem gemspec template. Make sure you read and understand +## all of the comments. Some sections require modification, and others can +## be deleted if you don't need them. Once you understand the contents of +## this file, feel free to delete any comments that begin with two hash marks. +## You can find comprehensive Gem::Specification documentation, at +## http://docs.rubygems.org/read/chapter/20 +Gem::Specification.new do |s| + s.specification_version = 2 if s.respond_to? :specification_version= + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.rubygems_version = '1.3.5' + + ## Leave these as is they will be modified for you by the rake gemspec task. + ## If your rubyforge_project name is different, then edit it and comment out + ## the sub! line in the Rakefile + s.name = 'remote_syslog_logger' + s.version = '1.0.0' + s.date = '2011-03-27' + # s.rubyforge_project = 'NAME' + + ## Make sure your summary is short. The description may be as long + ## as you like. + s.summary = "Short description used in Gem listings." + s.description = "Long description. Maybe copied from the README." + + ## List the primary authors. If there are a bunch of authors, it's probably + ## better to set the email to an email list or something. If you don't have + ## a custom homepage, consider using your GitHub URL or the like. + s.authors = ["Eric Lindvall"] + s.email = 'eric@5stops.com' + s.homepage = 'http://github.com/eric/remote_syslog_logger' + + ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as + ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb' + s.require_paths = %w[lib] + + ## This sections is only necessary if you have C extensions. + # s.require_paths << 'ext' + # s.extensions = %w[ext/extconf.rb] + + ## If your gem includes any executables, list them here. + # s.executables = ["name"] + # s.default_executable = 'name' + + ## Specify any RDoc options here. You'll want to add your README and + ## LICENSE files to the extra_rdoc_files list. + s.rdoc_options = ["--charset=UTF-8"] + s.extra_rdoc_files = %w[README LICENSE] + + ## List your runtime dependencies here. Runtime dependencies are those + ## that are needed for an end user to actually USE your code. + s.add_dependency('syslog_proto') + + ## List your development dependencies here. Development dependencies are + ## those that are only needed during development + # s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"]) + + ## Leave this section as-is. It will be automatically generated from the + ## contents of your Git repository via the gemspec task. DO NOT REMOVE + ## THE MANIFEST COMMENTS, they are used as delimiters by the task. + # = MANIFEST = + s.files = %w[] + # = MANIFEST = + + ## Test files will be grabbed from the file list. Make sure the path glob + ## matches what you actually use. + s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ } +end diff --git a/test/helper.rb b/test/helper.rb new file mode 100644 index 0000000..1198bab --- /dev/null +++ b/test/helper.rb @@ -0,0 +1,12 @@ +$:.unshift File.expand_path('../../lib', __FILE__) + +unless ENV['BUNDLE_GEMFILE'] + require 'rubygems' + require 'bundler' + Bundler.setup + Bundler.require +end + +require 'remote_syslog_logger' + +require 'test/unit' \ No newline at end of file diff --git a/test/test_remote_syslog_logger.rb b/test/test_remote_syslog_logger.rb new file mode 100644 index 0000000..702d07a --- /dev/null +++ b/test/test_remote_syslog_logger.rb @@ -0,0 +1,18 @@ +require File.expand_path('../helper', __FILE__) + +class TestRemoteSyslogLogger < Test::Unit::TestCase + def setup + @server_port = rand(50000) + 1024 + @socket = UDPSocket.new + @socket.bind('127.0.0.1', @server_port) + end + + def test_logger + @logger = RemoteSyslogLogger.new('127.0.0.1', @server_port) + @logger.info "This is a test" + + message, addr = *@socket.recvfrom(1024) + puts message + assert_match /This is a test/, message + end +end \ No newline at end of file