gelf-1.4.0/0000755000004100000410000000000012152173365012466 5ustar www-datawww-datagelf-1.4.0/benchmarks/0000755000004100000410000000000012152173365014603 5ustar www-datawww-datagelf-1.4.0/benchmarks/notifier.rb0000755000004100000410000000226612152173365016760 0ustar www-datawww-data#! /usr/bin/env ruby puts "Loading..." require 'benchmark' require 'rubygems' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'gelf' puts "Generating random data..." srand(1) RANDOM_DATA = ('A'..'z').to_a k3_message = (1..3*1024).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join TARGET_HOST = 'localhost' TARGET_PORT = 12201 DEFAULT_OPTIONS = { '_host' => 'localhost' } TIMES = 5000 SHORT_HASH = { 'short_message' => 'message' } LONG_HASH = { 'short_message' => 'message', 'long_message' => k3_message } notifier_lan = GELF::Notifier.new(TARGET_HOST, TARGET_PORT, 'LAN', DEFAULT_OPTIONS) notifier_wan = GELF::Notifier.new(TARGET_HOST, TARGET_PORT, 'WAN', DEFAULT_OPTIONS) # to create mongo collections, etc. notifier_lan.notify!(LONG_HASH) sleep(5) puts "Sending #{TIMES} notifications...\n" tms = Benchmark.bm(25) do |b| b.report('lan, short data, 1 chunk ') { TIMES.times { notifier_lan.notify!(SHORT_HASH) } } sleep(5) b.report('lan, long data, 1 chunk ') { TIMES.times { notifier_lan.notify!(LONG_HASH) } } sleep(5) b.report('wan, long data, 2 chunks') { TIMES.times { notifier_wan.notify!(LONG_HASH) } } end gelf-1.4.0/.travis.yml0000644000004100000410000000015012152173365014573 0ustar www-datawww-datarvm: - 1.8.7 - ree - 1.9.2 - 1.9.3 - jruby notifications: irc: "irc.freenode.org#graylog2" gelf-1.4.0/test/0000755000004100000410000000000012152173365013445 5ustar www-datawww-datagelf-1.4.0/test/test_logger.rb0000644000004100000410000001642512152173365016320 0ustar www-datawww-datarequire 'helper' class TestLogger < Test::Unit::TestCase context "with logger with mocked sender" do setup do Socket.stubs(:gethostname).returns('stubbed_hostname') @logger = GELF::Logger.new @sender = mock @logger.instance_variable_set('@sender', @sender) end should "respond to #close" do assert @logger.respond_to?(:close) end context "#add" do # logger.add(Logger::INFO, 'Message') should "implement add method with level and message from parameters, facility from defaults" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Message' && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO, 'Message') end # logger.add(Logger::INFO, RuntimeError.new('Boom!')) should "implement add method with level and exception from parameters, facility from defaults" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'RuntimeError: Boom!' && hash['full_message'] =~ /^Backtrace/ && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO, RuntimeError.new('Boom!')) end # logger.add(Logger::INFO) { 'Message' } should "implement add method with level from parameter, message from block, facility from defaults" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Message' && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO) { 'Message' } end # logger.add(Logger::INFO) { RuntimeError.new('Boom!') } should "implement add method with level from parameter, exception from block, facility from defaults" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'RuntimeError: Boom!' && hash['full_message'] =~ /^Backtrace/ && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO) { RuntimeError.new('Boom!') } end # logger.add(Logger::INFO, 'Message', 'Facility') should "implement add method with level, message and facility from parameters" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Message' && hash['facility'] == 'Facility' end @logger.add(GELF::INFO, 'Message', 'Facility') end # logger.add(Logger::INFO, RuntimeError.new('Boom!'), 'Facility') should "implement add method with level, exception and facility from parameters" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'RuntimeError: Boom!' && hash['full_message'] =~ /^Backtrace/ && hash['facility'] == 'Facility' end @logger.add(GELF::INFO, RuntimeError.new('Boom!'), 'Facility') end # logger.add(Logger::INFO, 'Facility') { 'Message' } should "implement add method with level and facility from parameters, message from block" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Message' && hash['facility'] == 'Facility' end @logger.add(GELF::INFO, 'Facility') { 'Message' } end # logger.add(Logger::INFO, 'Facility') { RuntimeError.new('Boom!') } should "implement add method with level and facility from parameters, exception from block" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'RuntimeError: Boom!' && hash['full_message'] =~ /^Backtrace/ && hash['facility'] == 'Facility' end @logger.add(GELF::INFO, 'Facility') { RuntimeError.new('Boom!') } end ##################### # logger.add(Logger::INFO, { :short_message => "Some message" }) should "implement add method with level and message from hash, facility from defaults" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Some message' && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO, { :short_message => "Some message" }) end # logger.add(Logger::INFO, { :short_message => "Some message", :_foo => "bar", "_zomg" => "wat" }) should "implement add method with level and message from hash, facility from defaults and some additional fields" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Some message' && hash['facility'] == 'gelf-rb' && hash['_foo'] == 'bar' && hash['_zomg'] == 'wat' end @logger.add(GELF::INFO, { :short_message => "Some message", :_foo => "bar", "_zomg" => "wat"}) end # logger.add(Logger::INFO, "somefac", { :short_message => "Some message", :_foo => "bar", "_zomg" => "wat" }) should "implement add method with level and message from hash, facility from parameters and some additional fields" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Some message' && hash['facility'] == 'somefac' && hash['_foo'] == 'bar' && hash['_zomg'] == 'wat' end @logger.add(GELF::INFO, { :short_message => "Some message", :_foo => "bar", "_zomg" => "wat"}, "somefac") end end GELF::Levels.constants.each do |const| # logger.error "Argument #{ @foo } mismatch." should "call add with level #{const} from method name, message from parameter" do @logger.expects(:add).with(GELF.const_get(const), 'message') @logger.__send__(const.downcase, 'message') end # logger.fatal { "Argument 'foo' not given." } should "call add with level #{const} from method name, message from block" do @logger.expects(:add).with(GELF.const_get(const), 'message') @logger.__send__(const.downcase) { 'message' } end # logger.info('initialize') { "Initializing..." } should "call add with level #{const} from method name, facility from parameter, message from block" do @logger.expects(:add).with(GELF.const_get(const), 'message', 'facility') @logger.__send__(const.downcase, 'facility') { 'message' } end should "respond to #{const.downcase}?" do @logger.level = GELF.const_get(const) - 1 assert @logger.__send__(const.to_s.downcase + '?') @logger.level = GELF.const_get(const) assert @logger.__send__(const.to_s.downcase + '?') @logger.level = GELF.const_get(const) + 1 assert !@logger.__send__(const.to_s.downcase + '?') end end should "support Logger#<<" do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::UNKNOWN && hash['short_message'] == "Message" end @logger << "Message" end should "have formatter attribute" do @logger.formatter end end end gelf-1.4.0/test/test_ruby_sender.rb0000644000004100000410000000160112152173365017350 0ustar www-datawww-datarequire 'helper' class TestRubyUdpSender < Test::Unit::TestCase context "with ruby sender" do setup do @addresses = [['localhost', 12201], ['localhost', 12202]] @sender = GELF::RubyUdpSender.new(@addresses) @datagrams1 = %w(d1 d2 d3) @datagrams2 = %w(e1 e2 e3) end context "send_datagrams" do setup do @sender.send_datagrams(@datagrams1) @sender.send_datagrams(@datagrams2) end before_should "be called 3 times with 1st and 2nd address" do UDPSocket.any_instance.expects(:send).times(3).with do |datagram, _, host, port| datagram.start_with?('d') && host == 'localhost' && port == 12201 end UDPSocket.any_instance.expects(:send).times(3).with do |datagram, _, host, port| datagram.start_with?('e') && host == 'localhost' && port == 12202 end end end end end gelf-1.4.0/test/helper.rb0000644000004100000410000000034712152173365015255 0ustar www-datawww-datarequire 'rubygems' require 'test/unit' require 'shoulda' require 'mocha' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'gelf' class Test::Unit::TestCase end gelf-1.4.0/test/test_notifier.rb0000644000004100000410000002400512152173365016651 0ustar www-datawww-datarequire 'helper' RANDOM_DATA = ('A'..'Z').to_a class TestNotifier < Test::Unit::TestCase should "allow access to host, port, max_chunk_size and default_options" do Socket.expects(:gethostname).returns('default_hostname') n = GELF::Notifier.new assert_equal [[['localhost', 12201]], 1420], [n.addresses, n.max_chunk_size] assert_equal( { 'version' => '1.0', 'level' => GELF::UNKNOWN, 'host' => 'default_hostname', 'facility' => 'gelf-rb' }, n.default_options ) n.addresses, n.max_chunk_size, n.default_options = [['graylog2.org', 7777]], :lan, {:host => 'grayhost'} assert_equal [['graylog2.org', 7777]], n.addresses assert_equal 8154, n.max_chunk_size assert_equal({'host' => 'grayhost'}, n.default_options) n.max_chunk_size = 1337.1 assert_equal 1337, n.max_chunk_size end context "with notifier with mocked sender" do setup do Socket.stubs(:gethostname).returns('stubbed_hostname') @notifier = GELF::Notifier.new('host', 12345) @sender = mock @notifier.instance_variable_set('@sender', @sender) end context "extract_hash" do should "check arguments" do assert_raise(ArgumentError) { @notifier.__send__(:extract_hash) } assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, 1, 2, 3) } end should "work with hash" do hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' }) assert_equal '1.0', hash['version'] assert_equal 'message', hash['short_message'] end should "work with any object which responds to #to_hash" do o = Object.new o.expects(:to_hash).returns({ 'version' => '1.0', 'short_message' => 'message' }) hash = @notifier.__send__(:extract_hash, o) assert_equal '1.0', hash['version'] assert_equal 'message', hash['short_message'] end should "work with exception with backtrace" do e = RuntimeError.new('message') e.set_backtrace(caller) hash = @notifier.__send__(:extract_hash, e) assert_equal 'RuntimeError: message', hash['short_message'] assert_match /Backtrace/, hash['full_message'] assert_equal GELF::ERROR, hash['level'] end should "work with exception without backtrace" do e = RuntimeError.new('message') hash = @notifier.__send__(:extract_hash, e) assert_match /Backtrace is not available/, hash['full_message'] end should "work with exception and hash" do e, h = RuntimeError.new('message'), {'param' => 1, 'level' => GELF::FATAL, 'short_message' => 'will be hidden by exception'} hash = @notifier.__send__(:extract_hash, e, h) assert_equal 'RuntimeError: message', hash['short_message'] assert_equal GELF::FATAL, hash['level'] assert_equal 1, hash['param'] end should "work with plain text" do hash = @notifier.__send__(:extract_hash, 'message') assert_equal 'message', hash['short_message'] assert_equal GELF::INFO, hash['level'] end should "work with plain text and hash" do hash = @notifier.__send__(:extract_hash, 'message', 'level' => GELF::WARN) assert_equal 'message', hash['short_message'] assert_equal GELF::WARN, hash['level'] end should "covert hash keys to strings" do hash = @notifier.__send__(:extract_hash, :short_message => :message) assert hash.has_key?('short_message') assert !hash.has_key?(:short_message) end should "not overwrite keys on convert" do assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, :short_message => :message1, 'short_message' => 'message2') } end should "use default_options" do @notifier.default_options = {:foo => 'bar', 'short_message' => 'will be hidden by explicit argument', 'host' => 'some_host'} hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' }) assert_equal 'bar', hash['foo'] assert_equal 'message', hash['short_message'] end should "be compatible with HoptoadNotifier" do # https://github.com/thoughtbot/hoptoad_notifier/blob/master/README.rdoc, section Going beyond exceptions hash = @notifier.__send__(:extract_hash, :error_class => 'Class', :error_message => 'Message') assert_equal 'Class: Message', hash['short_message'] end should "set file and line" do line = __LINE__ hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' }) assert_match /test_notifier.rb/, hash['file'] assert_equal line + 1, hash['line'] end should "set timestamp to current time if not set" do hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' }) assert_instance_of Float, hash['timestamp'] now = Time.now.utc.to_f assert ((now - 1)..(now + 1)).include?(hash['timestamp']) end should "set timestamp to specified time" do timestamp = 1319799449.13765 hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message', 'timestamp' => timestamp }) assert_equal timestamp, hash['timestamp'] end end context "serialize_hash" do setup do @notifier.level_mapping = :direct @notifier.instance_variable_set('@hash', { 'level' => GELF::WARN, 'field' => 'value' }) @data = @notifier.__send__(:serialize_hash) assert @data.respond_to?(:each) # Enumerable::Enumerator in 1.8, ::Enumerator in 1.9, so... @deserialized_hash = JSON.parse(Zlib::Inflate.inflate(@data.to_a.pack('C*'))) assert_instance_of Hash, @deserialized_hash end should "map level using mapping" do assert_not_equal GELF::WARN, @deserialized_hash['level'] assert_not_equal GELF::LOGGER_MAPPING[GELF::WARN], @deserialized_hash['level'] assert_equal GELF::DIRECT_MAPPING[GELF::WARN], @deserialized_hash['level'] end end context "datagrams_from_hash" do should "not split short data" do @notifier.instance_variable_set('@hash', { 'version' => '1.0', 'short_message' => 'message' }) datagrams = @notifier.__send__(:datagrams_from_hash) assert_equal 1, datagrams.count assert_instance_of String, datagrams[0] assert_equal "\x78\x9c", datagrams[0][0..1] # zlib header end should "split long data" do srand(1) # for stable tests hash = { 'version' => '1.0', 'short_message' => 'message' } hash.merge!('something' => (0..3000).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join) # or it will be compressed too good @notifier.instance_variable_set('@hash', hash) datagrams = @notifier.__send__(:datagrams_from_hash) assert_equal 2, datagrams.count datagrams.each_index do |i| datagram = datagrams[i] assert_instance_of String, datagram assert datagram[0..1] == "\x1e\x0f" # chunked GELF magic number # datagram[2..9] is a message id assert_equal i, datagram[10].ord assert_equal datagrams.count, datagram[11].ord end end end context "level threshold" do setup do @notifier.level = GELF::WARN @hash = { 'version' => '1.0', 'short_message' => 'message' } end ['debug', 'DEBUG', :debug].each do |l| should "allow to set threshold as #{l.inspect}" do @notifier.level = l assert_equal GELF::DEBUG, @notifier.level end end should "not send notifications with level below threshold" do @sender.expects(:send_datagrams).never @notifier.notify!(@hash.merge('level' => GELF::DEBUG)) end should "not notifications with level equal or above threshold" do @sender.expects(:send_datagrams).once @notifier.notify!(@hash.merge('level' => GELF::WARN)) end end context "when disabled" do setup do @notifier.disable end should "not send datagrams" do @sender.expects(:send_datagrams).never @notifier.expects(:extract_hash).never @notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' }) end context "and enabled again" do setup do @notifier.enable end should "send datagrams" do @sender.expects(:send_datagrams) @notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' }) end end end should "pass valid data to sender" do @sender.expects(:send_datagrams).with do |datagrams| datagrams.is_a?(Array) && datagrams[0].is_a?(String) end @notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' }) end GELF::Levels.constants.each do |const| should "call notify with level #{const} from method name" do @notifier.expects(:notify_with_level).with(GELF.const_get(const), { 'version' => '1.0', 'short_message' => 'message' }) @notifier.__send__(const.downcase, { 'version' => '1.0', 'short_message' => 'message' }) end end should "not rescue from invalid invocation of #notify!" do assert_raise(ArgumentError) { @notifier.notify!(:no_short_message => 'too bad') } end should "rescue from invalid invocation of #notify" do @notifier.expects(:notify_with_level!).with(nil, instance_of(Hash)).raises(ArgumentError) @notifier.expects(:notify_with_level!).with(GELF::UNKNOWN, instance_of(ArgumentError)) assert_nothing_raised { @notifier.notify(:no_short_message => 'too bad') } end end context "with notifier with real sender" do setup do @notifier = GELF::Notifier.new('no_such_host_321') end should "raise exception" do assert_raise(SocketError) { @notifier.notify('Hello!') } end should "not raise exception if asked" do @notifier.rescue_network_errors = true assert_nothing_raised { @notifier.notify('Hello!') } end end end gelf-1.4.0/test/test_severity.rb0000644000004100000410000000040012152173365016675 0ustar www-datawww-datarequire 'helper' class TestSeverity < Test::Unit::TestCase should "map Ruby Logger levels to syslog levels as SyslogLogger" do GELF::LOGGER_MAPPING.each do |ruby_level, syslog_level| assert_not_equal syslog_level, ruby_level end end end gelf-1.4.0/LICENSE0000644000004100000410000000207612152173365013500 0ustar www-datawww-dataCopyright (c) 2010-2011 Lennart Koopmann, Alexey Palazhchenko 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. gelf-1.4.0/README.rdoc0000644000004100000410000000163312152173365014277 0ustar www-datawww-data= GELF This is the new GELF gem written by Alexey Palazhchenko. It is based on the old gem by Lennart Koopmann and allows you to send GELF messages to Graylog2 server instances. See http://www.graylog2.org/about/gelf for more information about GELF and http://rdoc.info/github/Graylog2/gelf-rb/master/frames for API documentation. Works with Ruby 1.8.7 and 1.9.x. 1.8.6 is not supported. == Note on Patches/Pull Requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. == Copyright Copyright (c) 2010-2011 Lennart Koopmann and Alexey Palazhchenko. See LICENSE for details. gelf-1.4.0/Rakefile0000644000004100000410000000323612152173365014137 0ustar www-datawww-datarequire 'rake' begin require 'ci/reporter/rake/test_unit' rescue LoadError # nothing end begin require 'jeweler' Jeweler::Tasks.new do |gem| gem.name = "gelf" gem.summary = 'Library to send GELF messages to Graylog2 logging server.' gem.description = 'Library to send GELF messages to Graylog2 logging server. Supports plain-text, GELF messages and exceptions.' gem.email = "alexey.palazhchenko@gmail.com" gem.homepage = "http://github.com/Graylog2/gelf-rb" gem.authors = ["Alexey Palazhchenko", "Lennart Koopmann"] gem.add_dependency "json" gem.add_development_dependency "shoulda" gem.add_development_dependency "mocha" # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings end rescue LoadError => e puts e abort "Jeweler (or a dependency) not available. Install it with: gem install jeweler" end require 'rake/testtask' Rake::TestTask.new(:test) do |test| test.libs << 'lib' << 'test' test.pattern = 'test/**/test_*.rb' test.verbose = true end task :test => :check_dependencies task :default => :test begin require 'rcov/rcovtask' Rcov::RcovTask.new do |test| test.libs << 'test' test.pattern = 'test/**/test_*.rb' test.rcov_opts << '--exclude gem' test.verbose = true end rescue LoadError => e task :rcov do puts e abort "rcov is not available. Run: gem install rcov" end end #require 'rake/rdoctask' #Rake::RDocTask.new do |rdoc| # version = File.exist?('VERSION') ? File.read('VERSION') : "" # # rdoc.rdoc_dir = 'rdoc' # rdoc.title = "gelf #{version}" # rdoc.rdoc_files.include('README*') # rdoc.rdoc_files.include('lib/**/*.rb') #end gelf-1.4.0/VERSION0000644000004100000410000000000612152173365013532 0ustar www-datawww-data1.4.0 gelf-1.4.0/.gemtest0000644000004100000410000000000012152173365014125 0ustar www-datawww-datagelf-1.4.0/metadata.yml0000644000004100000410000000511012152173365014766 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: gelf version: !ruby/object:Gem::Version version: 1.4.0 prerelease: platform: ruby authors: - Alexey Palazhchenko - Lennart Koopmann autorequire: bindir: bin cert_chain: [] date: 2013-01-26 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: json requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: shoulda requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: mocha requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: Library to send GELF messages to Graylog2 logging server. Supports plain-text, GELF messages and exceptions. email: alexey.palazhchenko@gmail.com executables: [] extensions: [] extra_rdoc_files: - LICENSE - README.rdoc files: - .gemtest - .travis.yml - CHANGELOG - LICENSE - README.rdoc - Rakefile - VERSION - benchmarks/notifier.rb - gelf.gemspec - lib/gelf.rb - lib/gelf/logger.rb - lib/gelf/notifier.rb - lib/gelf/ruby_sender.rb - lib/gelf/severity.rb - test/helper.rb - test/test_logger.rb - test/test_notifier.rb - test/test_ruby_sender.rb - test/test_severity.rb homepage: http://github.com/Graylog2/gelf-rb licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: Library to send GELF messages to Graylog2 logging server. test_files: [] gelf-1.4.0/CHANGELOG0000644000004100000410000000300312152173365013674 0ustar www-datawww-data1.3.2, 2011-12-02: * support for rubygems-test. * rescue from more network errors. 1.3.1, 2011-10-28: + allow to rescue from network errors. 1.3.0, 2011-07-27: + allow to set timestamp manually. 1.2.0.beta1, 2011-05-23: + compatibility with GELF specification 1.0: * requires modern graylog2-server and graylog2-web-interface; + Notifier#default_options, Notifier#default_options=; + severity (level) threshold; + automatically set 'file', 'line' and 'timestamp' fields; + wrappers for GELF::Notifier#notify with severity: + GELF::Notifier.debug + GELF::Notifier.info + GELF::Notifier.warn + GELF::Notifier.error + GELF::Notifier.fatal + GELF::Notifier.unknown + full compatibility with Ruby Logger and other loggers: + GELF::Logger#fatal { "Argument 'foo' not given." } + GELF::Logger#error "Argument #{ @foo } mismatch." + GELF::Logger#info('initialize') { "Initializing..." } + GELF::Logger#add(GELF::FATAL) { 'Fatal error!' } + GELF::Logger#close + GELF::Logger#level = GELF::INFO + allow to change severity mapping; + send messages to receivers in round-robin; * GELF::Notifier#host and #port are attr_readers now and deprecated (were attr_accessor); + allow to disable file and line collection (GELF::Notifier#collect_file_and_line = false); - deprecated Gelf class removed. 1.0.2, 2010-11-29: 1.0.1, 2010-11-29: - added more tests for chunking in attempt to locate not existing bug. 1.0.0, 2010-11-10: + initial stable version; * deprecated Gelf class is still there. gelf-1.4.0/gelf.gemspec0000644000004100000410000000355212152173365014755 0ustar www-datawww-data# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "gelf" s.version = "1.4.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Alexey Palazhchenko", "Lennart Koopmann"] s.date = "2013-01-26" s.description = "Library to send GELF messages to Graylog2 logging server. Supports plain-text, GELF messages and exceptions." s.email = "alexey.palazhchenko@gmail.com" s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] s.files = [ ".gemtest", ".travis.yml", "CHANGELOG", "LICENSE", "README.rdoc", "Rakefile", "VERSION", "benchmarks/notifier.rb", "gelf.gemspec", "lib/gelf.rb", "lib/gelf/logger.rb", "lib/gelf/notifier.rb", "lib/gelf/ruby_sender.rb", "lib/gelf/severity.rb", "test/helper.rb", "test/test_logger.rb", "test/test_notifier.rb", "test/test_ruby_sender.rb", "test/test_severity.rb" ] s.homepage = "http://github.com/Graylog2/gelf-rb" s.require_paths = ["lib"] s.rubygems_version = "1.8.24" s.summary = "Library to send GELF messages to Graylog2 logging server." if s.respond_to? :specification_version then s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end end gelf-1.4.0/lib/0000755000004100000410000000000012152173365013234 5ustar www-datawww-datagelf-1.4.0/lib/gelf/0000755000004100000410000000000012152173365014151 5ustar www-datawww-datagelf-1.4.0/lib/gelf/ruby_sender.rb0000644000004100000410000000064412152173365017023 0ustar www-datawww-datamodule GELF # Plain Ruby UDP sender. class RubyUdpSender attr_accessor :addresses def initialize(addresses) @addresses = addresses @i = 0 @socket = UDPSocket.open end def send_datagrams(datagrams) host, port = @addresses[@i] @i = (@i + 1) % @addresses.length datagrams.each do |datagram| @socket.send(datagram, 0, host, port) end end end end gelf-1.4.0/lib/gelf/notifier.rb0000644000004100000410000001737412152173365016331 0ustar www-datawww-datamodule GELF # Graylog2 notifier. class Notifier @last_chunk_id = 0 class << self attr_accessor :last_chunk_id end attr_accessor :enabled, :collect_file_and_line, :rescue_network_errors attr_reader :max_chunk_size, :level, :default_options, :level_mapping # +host+ and +port+ are host/ip and port of graylog2-server. # +max_size+ is passed to max_chunk_size=. # +default_options+ is used in notify! def initialize(host = 'localhost', port = 12201, max_size = 'WAN', default_options = {}) @enabled = true @collect_file_and_line = true self.level = GELF::DEBUG self.max_chunk_size = max_size self.rescue_network_errors = false self.default_options = default_options self.default_options['version'] = SPEC_VERSION self.default_options['host'] ||= Socket.gethostname self.default_options['level'] ||= GELF::UNKNOWN self.default_options['facility'] ||= 'gelf-rb' @sender = RubyUdpSender.new([[host, port]]) self.level_mapping = :logger end # Get a list of receivers. # notifier.addresses # => [['localhost', 12201], ['localhost', 12202]] def addresses @sender.addresses end # Set a list of receivers. # notifier.addresses = [['localhost', 12201], ['localhost', 12202]] def addresses=(addrs) @sender.addresses = addrs end def host warn "GELF::Notifier#host is deprecated. Use #addresses instead." self.addresses.first[0] end def port warn "GELF::Notifier#port is deprecated. Use #addresses instead." self.addresses.first[1] end # +size+ may be a number of bytes, 'WAN' (1420 bytes) or 'LAN' (8154). # Default (safe) value is 'WAN'. def max_chunk_size=(size) case size.to_s.downcase when 'wan' @max_chunk_size = 1420 when 'lan' @max_chunk_size = 8154 else @max_chunk_size = size.to_int end end def level=(new_level) @level = if new_level.is_a?(Fixnum) new_level else GELF.const_get(new_level.to_s.upcase) end end def default_options=(options) @default_options = self.class.stringify_keys(options) end # +mapping+ may be a hash, 'logger' (GELF::LOGGER_MAPPING) or 'direct' (GELF::DIRECT_MAPPING). # Default (compatible) value is 'logger'. def level_mapping=(mapping) case mapping.to_s.downcase when 'logger' @level_mapping = GELF::LOGGER_MAPPING when 'direct' @level_mapping = GELF::DIRECT_MAPPING else @level_mapping = mapping end end def disable @enabled = false end def enable @enabled = true end # Same as notify!, but rescues all exceptions (including +ArgumentError+) # and sends them instead. def notify(*args) notify_with_level(nil, *args) end # Sends message to Graylog2 server. # +args+ can be: # - hash-like object (any object which responds to +to_hash+, including +Hash+ instance): # notify!(:short_message => 'All your rebase are belong to us', :user => 'AlekSi') # - exception with optional hash-like object: # notify!(SecurityError.new('ALARM!'), :trespasser => 'AlekSi') # - string-like object (anything which responds to +to_s+) with optional hash-like object: # notify!('Plain olde text message', :scribe => 'AlekSi') # Resulted fields are merged with +default_options+, the latter will never overwrite the former. # This method will raise +ArgumentError+ if arguments are wrong. Consider using notify instead. def notify!(*args) notify_with_level!(nil, *args) end GELF::Levels.constants.each do |const| class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{const.downcase}(*args) # def debug(*args) notify_with_level(GELF::#{const}, *args) # notify_with_level(GELF::DEBUG, *args) end # end EOT end private def notify_with_level(message_level, *args) notify_with_level!(message_level, *args) rescue SocketError, SystemCallError raise unless self.rescue_network_errors rescue Exception => exception notify_with_level!(GELF::UNKNOWN, exception) end def notify_with_level!(message_level, *args) return unless @enabled extract_hash(*args) @hash['level'] = message_level unless message_level.nil? if @hash['level'] >= level @sender.send_datagrams(datagrams_from_hash) end end def extract_hash(object = nil, args = {}) primary_data = if object.respond_to?(:to_hash) object.to_hash elsif object.is_a?(Exception) args['level'] ||= GELF::ERROR self.class.extract_hash_from_exception(object) else args['level'] ||= GELF::INFO { 'short_message' => object.to_s } end @hash = default_options.merge(self.class.stringify_keys(args.merge(primary_data))) convert_hoptoad_keys_to_graylog2 set_file_and_line if @collect_file_and_line set_timestamp check_presence_of_mandatory_attributes @hash end def self.extract_hash_from_exception(exception) bt = exception.backtrace || ["Backtrace is not available."] { 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") } end # Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific. def convert_hoptoad_keys_to_graylog2 if @hash['short_message'].to_s.empty? if @hash.has_key?('error_class') && @hash.has_key?('error_message') @hash['short_message'] = @hash.delete('error_class') + ': ' + @hash.delete('error_message') end end end CALLER_REGEXP = /^(.*):(\d+).*/ LIB_GELF_PATTERN = File.join('lib', 'gelf') def set_file_and_line stack = caller begin frame = stack.shift end while frame.include?(LIB_GELF_PATTERN) match = CALLER_REGEXP.match(frame) @hash['file'] = match[1] @hash['line'] = match[2].to_i end def set_timestamp @hash['timestamp'] = Time.now.utc.to_f if @hash['timestamp'].nil? end def check_presence_of_mandatory_attributes %w(version short_message host).each do |attribute| if @hash[attribute].to_s.empty? raise ArgumentError.new("#{attribute} is missing. Options version, short_message and host must be set.") end end end def datagrams_from_hash data = serialize_hash datagrams = [] # Maximum total size is 8192 byte for UDP datagram. Split to chunks if bigger. (GELF v1.0 supports chunking) if data.count > @max_chunk_size id = self.class.last_chunk_id += 1 msg_id = Digest::MD5.digest("#{Time.now.to_f}-#{id}")[0, 8] num, count = 0, (data.count.to_f / @max_chunk_size).ceil data.each_slice(@max_chunk_size) do |slice| datagrams << "\x1e\x0f" + msg_id + [num, count, *slice].pack('C*') num += 1 end else datagrams << data.to_a.pack('C*') end datagrams end def serialize_hash raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty? @hash['level'] = @level_mapping[@hash['level']] Zlib::Deflate.deflate(@hash.to_json).bytes end def self.stringify_keys(hash) hash.keys.each do |key| value, key_s = hash.delete(key), key.to_s raise ArgumentError.new("Both #{key.inspect} and #{key_s} are present.") if hash.has_key?(key_s) hash[key_s] = value end hash end end end gelf-1.4.0/lib/gelf/logger.rb0000644000004100000410000000440612152173365015761 0ustar www-datawww-datamodule GELF # Methods for compatibility with Ruby Logger. module LoggerCompatibility attr_accessor :formatter # Does nothing. def close end # Use it like Logger#add... or better not to use at all. def add(level, *args) raise ArgumentError.new('Wrong arguments.') unless (0..2).include?(args.count) # Ruby Logger's author is a maniac. message, progname = if args.count == 2 [args[0], args[1]] elsif args.count == 0 [yield, default_options['facility']] elsif block_given? [yield, args[0]] else [args[0], default_options['facility']] end if message.is_a?(Hash) # Stringify keys. hash = {} message.each do |k,v| hash[k.to_s] = message[k] end hash['facility'] = progname else hash = {'short_message' => message, 'facility' => progname} end hash.merge!(self.class.extract_hash_from_exception(message)) if message.is_a?(Exception) notify_with_level(level, hash) end # Redefines methods in +Notifier+. GELF::Levels.constants.each do |const| class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{const.downcase}(*args) # def debug(*args) args.unshift(yield) if block_given? # args.unshift(yield) if block_given? add(GELF::#{const}, *args) # add(GELF::DEBUG, *args) end # end def #{const.downcase}? # def debug? GELF::#{const} >= level # GELF::DEBUG >= level end # end EOT end def <<(message) notify_with_level(GELF::UNKNOWN, 'short_message' => message) end end # Graylog2 notifier, compatible with Ruby Logger. # You can use it with Rails like this: # config.logger = GELF::Logger.new("localhost", 12201, "WAN", { :facility => "appname" }) # config.colorize_logging = false class Logger < Notifier include LoggerCompatibility @last_chunk_id = 0 end end gelf-1.4.0/lib/gelf/severity.rb0000644000004100000410000000252212152173365016351 0ustar www-datawww-datamodule GELF # There are two things you should know about log levels/severity: # - syslog defines levels from 0 (Emergency) to 7 (Debug). # 0 (Emergency) and 1 (Alert) levels are reserved for OS kernel. # - Ruby default Logger defines levels from 0 (DEBUG) to 4 (FATAL) and 5 (UNKNOWN). # Note that order is inverted. # For compatibility we define our constants as Ruby Logger, and convert values before # generating GELF message, using defined mapping. module Levels DEBUG = 0 INFO = 1 WARN = 2 ERROR = 3 FATAL = 4 UNKNOWN = 5 end include Levels # Maps Ruby Logger levels to syslog levels as SyslogLogger and syslogger gems. This one is default. LOGGER_MAPPING = {DEBUG => 7, # Debug INFO => 6, # Info WARN => 5, # Notice ERROR => 4, # Warning FATAL => 3, # Error UNKNOWN => 1} # Alert – shouldn't be used # Maps Ruby Logger levels to syslog levels as is. DIRECT_MAPPING = {DEBUG => 7, # Debug INFO => 6, # Info # skip 5 Notice WARN => 4, # Warning ERROR => 3, # Error FATAL => 2, # Critical UNKNOWN => 1} # Alert – shouldn't be used end gelf-1.4.0/lib/gelf.rb0000644000004100000410000000031612152173365014476 0ustar www-datawww-datarequire 'json' require 'socket' require 'zlib' require 'digest/md5' module GELF SPEC_VERSION = '1.0' end require 'gelf/severity' require 'gelf/ruby_sender' require 'gelf/notifier' require 'gelf/logger'