gelf-3.1.0/0000755000004100000410000000000013367446125012473 5ustar www-datawww-datagelf-3.1.0/Gemfile.lock0000644000004100000410000000274313367446125014723 0ustar www-datawww-dataGEM remote: https://rubygems.org/ specs: addressable (2.4.0) builder (3.2.2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) faraday (0.9.2) multipart-post (>= 1.2, < 3) git (1.3.0) github_api (0.14.5) addressable (~> 2.4.0) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) hashie (>= 3.4) oauth2 (~> 1.0) hashie (3.4.4) highline (1.7.8) jeweler (2.1.1) builder bundler (>= 1.0) git (>= 1.2.5) github_api highline (>= 1.6.15) nokogiri (>= 1.5.10) rake rdoc semver json (1.8.6) json (1.8.6-java) jwt (1.5.4) metaclass (0.0.4) mini_portile2 (2.1.0) mocha (1.1.0) metaclass (~> 0.0.1) multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) nokogiri (1.6.8-java) oauth2 (1.2.0) faraday (>= 0.8, < 0.10) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) pkg-config (1.1.7) power_assert (0.3.0) rack (1.6.4) rake (11.2.2) rdoc (4.2.2) json (~> 1.4) semver (1.0.1) shoulda (2.11.3) test-unit (3.2.1) power_assert thread_safe (0.3.5) thread_safe (0.3.5-java) PLATFORMS java ruby DEPENDENCIES jeweler (~> 2.1.1) mocha (~> 1.1.0) rack (< 2.0) shoulda (~> 2.11.3) test-unit (~> 3.2.0) BUNDLED WITH 1.14.6 gelf-3.1.0/.travis.yml0000644000004100000410000000057513367446125014613 0ustar www-datawww-datarvm: - 1.9.3 - 2.0.0 - 2.1.0 - 2.2.0 - 2.3.0 - 2.4.0 - jruby-19mode deploy: provider: rubygems api_key: secure: Pm1yAqo2ldet/Fd2jK5dKZkDNmyx5KkYtViL+eOR4Rxs82dwqOcCSyrBL6BOzuRfJtpjgXUX6FDapKgLDYW/2EDW8mmiciOL0WLzxYQsmJXcerJeUl2tTeFKpyEf+8aaDQpAlsK0m1dPmQjCJF9MWsa7m3oshm3mJ1VITY90chM= gem: gelf gemspec: gelf.gemspec on: tags: true repo: graylog-labs/gelf-rb gelf-3.1.0/test/0000755000004100000410000000000013367446125013452 5ustar www-datawww-datagelf-3.1.0/test/helper.rb0000644000004100000410000000035513367446125015261 0ustar www-datawww-datarequire 'rubygems' require 'test/unit' require 'shoulda' require 'mocha/setup' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'gelf' class Test::Unit::TestCase end gelf-3.1.0/test/test_ruby_sender.rb0000644000004100000410000000160213367446125017356 0ustar www-datawww-datarequire 'helper' class TestRubyUdpSender < Test::Unit::TestCase context "with ruby sender" do setup do @addresses = [['localhost', 12201], ['localhost', 12202]] @sender = GELF::Transport::UDP.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-3.1.0/test/test_notifier.rb0000644000004100000410000003011013367446125016650 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, 'protocol' => 0, '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 hash = { 'level' => GELF::WARN, 'field' => 'value' } @data = @notifier.__send__(:serialize_hash, 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 hash = { 'version' => '1.0', 'short_message' => 'message' } datagrams = @notifier.__send__(:datagrams_from_hash, hash) assert_equal 1, datagrams.count assert_instance_of String, datagrams[0] asserted = "\x78\x9c" if RUBY_VERSION[0,1].to_i >= 2 puts "I'm a Ruby > 2.0.0. Enforcing ASCII-8BIT. (#{RUBY_VERSION}/#{RUBY_VERSION[0,1].to_i})" # lol well yeah, Rubby. # http://stackoverflow.com/questions/15843684/binary-string-literals-in-ruby-2-0 asserted = asserted.b end assert_equal asserted, 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 datagrams = @notifier.__send__(:datagrams_from_hash, 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 should "split long data when subclassed" do class MyNotifier < GELF::Notifier; end @notifier = MyNotifier.new('host', 1234) @sender = mock @notifier.instance_variable_set('@sender', @sender) 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 datagrams = @notifier.__send__(:datagrams_from_hash, 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 should "throw an error if more than MAX_CHUNKS will be created" 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.max_chunk_size = 10 @notifier.instance_variable_set('@hash', hash) assert_raise(ArgumentError) do @notifier.__send__(:datagrams_from_hash) 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 "close" do should "close sender" do @sender.expects(:close).once @notifier.close 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 should "not mutate arguments" do data = { 'version' => '1.0', 'short_message' => 'message', foo: { bar: "BAZ" } } original_hash = data.hash @sender.expects(:send_datagrams) @notifier.notify!(data) assert_equal(data.hash, original_hash) 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-3.1.0/test/test_logger.rb0000644000004100000410000002351313367446125016321 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, nil) should 'implement add method with level, message and facility from defaults' do @logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'gelf-rb' && hash['facility'] == 'gelf-rb' end @logger.add(GELF::INFO, nil) end # 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, nil, '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, nil, 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, nil, nil) { '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, nil, nil) { 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, 'Message', nil) should "use facility from initialization if facility is nil" do logger = GELF::Logger.new('localhost', 12202, 'WAN', :facility => 'foo-bar') logger.expects(:notify_with_level!).with do |level, hash| level == GELF::INFO && hash['short_message'] == 'Message' && hash['facility'] == 'foo-bar' end logger.add(GELF::INFO, 'Message', nil) end # logger.add(Logger::INFO, 'Message', nil) should "use default facility if facility is nil" 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', nil) 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, nil, '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, nil, 'Facility') { 'Message' } end # logger.add(Logger::INFO, nil, '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, nil, '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, { :short_message => "Some message", :_foo => "bar", "_zomg" => "wat" }, 'somefac') 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 should 'implement add method with level and ignore zero-length message strings' do @logger.expects(:notify_with_level!).never @logger.add(GELF::INFO, '') end should 'implement add method with level and ignore hash without short_message key' do @logger.expects(:notify_with_level!).never @logger.add(GELF::INFO, { :message => 'Some message' }) end should 'implement add method with level and ignore hash with zero-length short_message entry' do @logger.expects(:notify_with_level!).never @logger.add(GELF::INFO, { :short_message => '' }) end should 'implement add method with level and ignore hash with nil short_message entry' do @logger.expects(:notify_with_level!).never @logger.add(GELF::INFO, { :short_message => nil }) 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(:notify_with_level!).with do |level, hash| level == GELF.const_get(const) && hash['short_message'] == 'message' && hash['facility'] == 'gelf-rb' end @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(:notify_with_level!).with do |level, hash| level == GELF.const_get(const) && hash['short_message'] == 'message' && hash['facility'] == 'gelf-rb' end @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(:notify_with_level!).with do |level, hash| level == GELF.const_get(const) && hash['short_message'] == 'message' && hash['facility'] == 'facility' end @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 context "close" do should "close socket" do @sender.expects(:close).once @logger.close end end end end gelf-3.1.0/test/test_severity.rb0000644000004100000410000000040013367446125016702 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-3.1.0/README.md0000644000004100000410000000536713367446125013765 0ustar www-datawww-data## GELF Ruby library 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 Graylog or Logstash instances. See the [GELF specification](http://docs.graylog.org/en/stable/pages/gelf.html) for more information about GELF and [RDoc](http://rdoc.info/github/graylog-labs/gelf-rb/master/frames) for API documentation. Tested with Ruby 1.9, 2.0, 2.1, 2.2, 2.3 and 2.4. [![Build Status](https://travis-ci.org/graylog-labs/gelf-rb.svg?branch=master)](https://travis-ci.org/graylog-labs/gelf-rb) [![Code Climate](https://codeclimate.com/github/graylog-labs/gelf-rb/badges/gpa.svg)](https://codeclimate.com/github/graylog-labs/gelf-rb) ## Usage ### Gelf::Notifier This allows you to sent arbitary messages via UDP to Graylog. n = GELF::Notifier.new("localhost", 12201) # Send with custom attributes and an additional parameter "foo" n.notify!(:short_message => "foo", :full_message => "something here\n\nbacktrace?!", :_foo => "bar") # Pass any object that responds to .to_hash n.notify!(Exception.new) The recommended default is to send via UDP but you can choose to send via TCP like this: n = GELF::Notifier.new("127.0.0.1", 12201, "LAN", { :protocol => GELF::Protocol::TCP }) Note that the `LAN` or `WAN` option is ignored for TCP because no chunking happens. (Read below for more information.) ### Gelf::Logger The Gelf::Logger is compatible with the standard Ruby Logger interface and can be used interchangeably. Under the hood it uses Gelf::Notifier to send log messages via UDP to Graylog. logger = GELF::Logger.new("localhost", 12201, "WAN", { :facility => "appname" }) logger.debug "foobar" logger.info "foobar" logger.warn "foobar" logger.error "foobar" logger.fatal "foobar" logger << "foobar" Then `WAN` or `LAN` option influences the UDP chunk size depending on if you send in your own network (LAN) or on a longer route (i.e. through the internet) and should be set accordingly. Since it's compatible with the Logger interface, you can also use it in your Rails application: # config/environments/production.rb config.logger = GELF::Logger.new("localhost", 12201, "WAN", { :facility => "appname" }) ### 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-2016 Lennart Koopmann and Alexey Palazhchenko. See LICENSE for details. gelf-3.1.0/gelf.gemspec0000644000004100000410000000506613367446125014764 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 -*- # stub: gelf 3.1.0 ruby lib Gem::Specification.new do |s| s.name = "gelf" s.version = "3.1.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett", "Marcus Ilgner"] s.date = "2018-09-05" s.description = "Library to send GELF messages to Graylog logging server. Supports plain-text, GELF messages and exceptions via UDP and TCP." s.email = "mail@marcusilgner.com" s.extra_rdoc_files = [ "LICENSE", "README.md" ] s.files = [ ".gemtest", ".travis.yml", "CHANGELOG", "CONTRIBUTING.md", "Gemfile", "Gemfile.lock", "LICENSE", "README.md", "Rakefile", "VERSION", "benchmarks/notifier.rb", "gelf.gemspec", "lib/gelf.rb", "lib/gelf/logger.rb", "lib/gelf/notifier.rb", "lib/gelf/severity.rb", "lib/gelf/transport/tcp.rb", "lib/gelf/transport/tcp_tls.rb", "lib/gelf/transport/udp.rb", "test/helper.rb", "test/test_logger.rb", "test/test_notifier.rb", "test/test_ruby_sender.rb", "test/test_severity.rb" ] s.homepage = "https://github.com/graylog-labs/gelf-rb" s.licenses = ["MIT"] s.rubygems_version = "2.5.1" s.summary = "Library to send GELF messages to Graylog logging server." if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, ["~> 2.11.3"]) s.add_development_dependency(%q, ["~> 2.1.1"]) s.add_development_dependency(%q, ["< 2.0"]) s.add_development_dependency(%q, ["~> 1.1.0"]) s.add_development_dependency(%q, ["~> 3.2.0"]) s.add_runtime_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 2.11.3"]) s.add_dependency(%q, ["~> 2.1.1"]) s.add_dependency(%q, ["< 2.0"]) s.add_dependency(%q, ["~> 1.1.0"]) s.add_dependency(%q, ["~> 3.2.0"]) s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, ["~> 2.11.3"]) s.add_dependency(%q, ["~> 2.1.1"]) s.add_dependency(%q, ["< 2.0"]) s.add_dependency(%q, ["~> 1.1.0"]) s.add_dependency(%q, ["~> 3.2.0"]) s.add_dependency(%q, [">= 0"]) end end gelf-3.1.0/LICENSE0000644000004100000410000000207613367446125013505 0ustar www-datawww-dataCopyright (c) 2010-2016 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-3.1.0/.gemtest0000644000004100000410000000000013367446125014132 0ustar www-datawww-datagelf-3.1.0/Rakefile0000644000004100000410000000314713367446125014145 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 Graylog logging server.' gem.description = 'Library to send GELF messages to Graylog logging server. Supports plain-text, GELF messages and exceptions via UDP and TCP.' gem.email = "mail@marcusilgner.com" gem.homepage = "http://github.com/Graylog2/gelf-rb" gem.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett", "Marcus Ilgner"] gem.add_dependency "json" gem.licenses = ["MIT"] # 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 :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-3.1.0/lib/0000755000004100000410000000000013367446125013241 5ustar www-datawww-datagelf-3.1.0/lib/gelf.rb0000644000004100000410000000034313367446125014503 0ustar www-datawww-datarequire 'json' require 'socket' require 'zlib' require 'digest/md5' module GELF SPEC_VERSION = '1.0' module Protocol UDP = 0 TCP = 1 end end require 'gelf/severity' require 'gelf/notifier' require 'gelf/logger' gelf-3.1.0/lib/gelf/0000755000004100000410000000000013367446125014156 5ustar www-datawww-datagelf-3.1.0/lib/gelf/notifier.rb0000644000004100000410000002121313367446125016321 0ustar www-datawww-datarequire 'gelf/transport/udp' require 'gelf/transport/tcp' require 'gelf/transport/tcp_tls' # replace JSON and #to_json with Yajl if available begin require 'yajl/json_gem' rescue LoadError end module GELF # Graylog2 notifier. class Notifier # Maximum number of GELF chunks as per GELF spec MAX_CHUNKS = 128 MAX_CHUNK_SIZE_WAN = 1420 MAX_CHUNK_SIZE_LAN = 8154 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 @random = Random.new self.level = GELF::DEBUG self.max_chunk_size = max_size self.rescue_network_errors = false self.default_options = default_options.dup self.default_options['version'] = SPEC_VERSION self.default_options['host'] ||= Socket.gethostname self.default_options['level'] ||= GELF::UNKNOWN self.default_options['facility'] ||= 'gelf-rb' self.default_options['protocol'] ||= GELF::Protocol::UDP self.level_mapping = :logger @sender = create_sender(host, port) 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 # +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 = MAX_CHUNK_SIZE_WAN when 'lan' @max_chunk_size = MAX_CHUNK_SIZE_LAN else @max_chunk_size = size.to_int end end def level=(new_level) @level = if new_level.is_a?(Integer) 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 # Closes sender def close @sender.close 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| define_method(const.downcase) do |*args| level = GELF.const_get(const) notify_with_level(level, *args) end end private def create_sender(host, port) addresses = [[host, port]] if default_options['protocol'] == GELF::Protocol::TCP if default_options.key?('tls') tls_options = default_options.delete('tls') GELF::Transport::TCPTLS.new(addresses, tls_options) else GELF::Transport::TCP.new(addresses) end else GELF::Transport::UDP.new(addresses) end end def notify_with_level(message_level, *args) notify_with_level!(message_level, *args) rescue SocketError, SystemCallError raise unless rescue_network_errors rescue Exception => exception notify_with_level!(GELF::UNKNOWN, exception) end def notify_with_level!(message_level, *args) return unless @enabled hash = extract_hash(*args) hash['level'] = message_level unless message_level.nil? if hash['level'] >= level if default_options['protocol'] == GELF::Protocol::TCP validate_hash(hash) @sender.send(hash.to_json + "\0") else @sender.send_datagrams(datagrams_from_hash(hash)) end 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(hash) set_file_and_line(hash) if @collect_file_and_line set_timestamp(hash) check_presence_of_mandatory_attributes(hash) 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(hash) 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(hash) stack = caller frame = stack.find { |f| !f.include?(LIB_GELF_PATTERN) } match = CALLER_REGEXP.match(frame) hash['file'] = match[1] hash['line'] = match[2].to_i end def set_timestamp(hash) hash['timestamp'] = Time.now.utc.to_f if hash['timestamp'].nil? end def check_presence_of_mandatory_attributes(hash) %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(hash) data = serialize_hash(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 = @random.bytes(8) msg_id = Digest::MD5.digest("#{Time.now.to_f}-#{id}")[0, 8] num, count = 0, (data.count.to_f / @max_chunk_size).ceil if count > MAX_CHUNKS raise ArgumentError, "Data too big (#{data.count} bytes), would create more than #{MAX_CHUNKS} chunks!" end 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 validate_hash(hash) raise ArgumentError.new("Hash is empty.") if hash.nil? || hash.empty? hash['level'] = @level_mapping[hash['level']] end def serialize_hash(hash) validate_hash(hash) Zlib::Deflate.deflate(hash.to_json).bytes end def self.stringify_keys(data) return data unless data.is_a? Hash data.each_with_object({}) do |(key, value), obj| key_s = key.to_s if (key != key_s) && data.key?(key_s) raise ArgumentError, "Both #{key.inspect} and #{key_s} are present." end obj[key_s] = value end end end end gelf-3.1.0/lib/gelf/transport/0000755000004100000410000000000013367446125016212 5ustar www-datawww-datagelf-3.1.0/lib/gelf/transport/udp.rb0000644000004100000410000000152213367446125017327 0ustar www-datawww-datamodule GELF module Transport class UDP attr_accessor :addresses def initialize(addresses) @addresses = addresses end def send_datagrams(datagrams) socket = get_socket idx = get_address_index host, port = @addresses[idx] set_address_index((idx + 1) % @addresses.length) datagrams.each do |datagram| socket.send(datagram, 0, host, port) end end def close socket = get_socket socket.close if socket end private def get_socket Thread.current[:gelf_udp_socket] ||= UDPSocket.open end def get_address_index Thread.current[:gelf_udp_address_idx] ||= 0 end def set_address_index(value) Thread.current[:gelf_udp_address_idx] = value end end end end gelf-3.1.0/lib/gelf/transport/tcp_tls.rb0000644000004100000410000001053213367446125020210 0ustar www-datawww-datarequire 'openssl' module GELF module Transport # Provides encryption capabilities for TCP connections class TCPTLS < TCP # Supported tls_options: # 'no_default_ca' [Boolean] prevents OpenSSL from using the systems CA store. # 'version' [Symbol] any of :TLSv1, :TLSv1_1, :TLSv1_2 (default) # 'ca' [String] the path to a custom CA store # 'cert' [String, IO] the client certificate file # 'key' [String, IO] the key for the client certificate # 'all_ciphers' [Boolean] allows any ciphers to be used, may be insecure # 'rescue_ssl_errors' [Boolean] similar to rescue_network_errors in notifier.rb, allows SSL exceptions to be raised # 'no_verify' [Boolean] disable peer verification attr_accessor :rescue_ssl_errors def initialize(addresses, tls_options={}) @tls_options = tls_options @rescue_ssl_errors = @tls_options['rescue_ssl_errors'] @rescue_ssl_errors if @rescue_ssl_errors.nil? super(addresses) end protected def write_socket(socket, message) super(socket, message) rescue OpenSSL::SSL::SSLError socket.close unless socket.closed? raise unless rescue_ssl_errors false end def connect(host, port) plain_socket = super(host, port) start_tls(plain_socket) rescue OpenSSL::SSL::SSLError plain_socket.close unless plain_socket.closed? raise unless rescue_ssl_errors nil end # Initiates TLS communication on the socket def start_tls(plain_socket) ssl_socket_class.new(plain_socket, ssl_context).tap do |ssl_socket| ssl_socket.sync_close = true ssl_socket.connect end end def ssl_socket_class if defined?(Celluloid::IO::SSLSocket) Celluloid::IO::SSLSocket else OpenSSL::SSL::SSLSocket end end def ssl_context @ssl_context ||= OpenSSL::SSL::SSLContext.new.tap do |ctx| ctx.cert_store = ssl_cert_store ctx.ssl_version = tls_version ctx.verify_mode = verify_mode set_certificate_and_key(ctx) restrict_ciphers(ctx) unless @tls_options['all_ciphers'] end end def set_certificate_and_key(context) return unless @tls_options['cert'] && @tls_options['key'] context.cert = OpenSSL::X509::Certificate.new(resource(@tls_options['cert'])) context.key = OpenSSL::PKey::RSA.new(resource(@tls_options['key'])) end # checks whether {resource} is a filename and tries to read it # otherwise treats it as if it already contains certificate/key data def resource(data) if data.is_a?(String) && File.exist?(data) File.read(data) else data end end # Ciphers have to come from the CipherString class, specifically the _TXT_ constants here - https://github.com/jruby/jruby-openssl/blob/master/src/main/java/org/jruby/ext/openssl/CipherStrings.java#L47-L178 def restrict_ciphers(ctx) # This CipherString is will allow a variety of 'currently' cryptographically secure ciphers, # while also retaining a broad level of compatibility ctx.ciphers = "TLSv1_2:TLSv1_1:TLSv1:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:!ADH:!IDEA:!3DES" end def verify_mode @tls_options['no_verify'] ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER end # SSL v2&3 are insecure, forces at least TLS v1.0 and defaults to v1.2 def tls_version if @tls_options.key?('version') && OpenSSL::SSL::SSLContext::METHODS.include?(@tls_options['version']) && @tls_options['version'] =~ /\ATLSv/ @tls_options['version'] else :TLSv1_2 end end def ssl_cert_store OpenSSL::X509::Store.new.tap do |store| unless @tls_options['no_default_ca'] store.set_default_paths end if @tls_options.key?('ca') ca = @tls_options['ca'] if File.directory?(ca) store.add_path(@tls_options['ca']) elsif File.file?(ca) store.add_file(ca) else $stderr.puts "No directory or file: #{ca}" end end end end end end end gelf-3.1.0/lib/gelf/transport/tcp.rb0000644000004100000410000000374413367446125017335 0ustar www-datawww-datamodule GELF module Transport class TCP attr_reader :addresses # `addresses` Array of [host, port] pairs def initialize(addresses) @sockets = [] self.addresses = addresses end def addresses=(addresses) @addresses = addresses.dup.freeze.tap do |addrs| @sockets.each(&:close) @sockets = addrs.map { |peer| connect(*peer) } end end def send(message) return if @addresses.empty? loop do connected = @sockets.reject(&:closed?) reconnect_all if connected.empty? break if write_any(connected, message) end end private def connect(host, port) socket_class.new(host, port) end def reconnect_all @sockets = @sockets.each_with_index.map do |old_socket, index| old_socket.closed? ? connect(*@addresses[index]) : old_socket end end def socket_class if defined?(Celluloid::IO::TCPSocket) Celluloid::IO::TCPSocket else ::TCPSocket end end def write_any(sockets, message) sockets.shuffle.each do |socket| return true if write_socket(socket, message) end false end def write_socket(socket, message) unsafe_write_socket(socket, message) rescue IOError, SystemCallError socket.close unless socket.closed? false end def unsafe_write_socket(socket, message) r,w = IO.select([socket], [socket]) # Read everything first while r.any? do # don't expect any reads, but a readable socket might # mean the remote end closed, so read it and throw it away. # we'll get an EOFError if it happens. socket.sysread(16384) r = IO.select([socket]) end # Now send the payload return false unless w.any? return socket.syswrite(message) > 0 end end end end gelf-3.1.0/lib/gelf/severity.rb0000644000004100000410000000415313367446125016360 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 # Additional native syslog severities. These will work in direct mapping mode # only, for compatibility with syslog sources unrelated to Logger. EMERGENCY = 10 ALERT = 11 CRITICAL = 12 WARNING = 14 NOTICE = 15 INFORMATIONAL = 16 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, # Informational WARN => 5, # Notice ERROR => 4, # Warning FATAL => 3, # Error UNKNOWN => 1} # Alert – shouldn't be used # Maps Syslog or Ruby Logger levels directly to standard syslog numerical severities. DIRECT_MAPPING = {DEBUG => 7, # Debug INFORMATIONAL => 6, # Informational (syslog source) INFO => 6, # Informational (Logger source) NOTICE => 5, # Notice WARNING => 4, # Warning (syslog source) WARN => 4, # Warning (Logger source) ERROR => 3, # Error CRITICAL => 2, # Critical (syslog source) FATAL => 2, # Critical (Logger source) ALERT => 1, # Alert (syslog source) UNKNOWN => 1, # Alert - shouldn't be used (Logger source) EMERGENCY => 0} # Emergency (syslog source) end gelf-3.1.0/lib/gelf/logger.rb0000644000004100000410000000336013367446125015764 0ustar www-datawww-datamodule GELF # Methods for compatibility with Ruby Logger. module LoggerCompatibility attr_accessor :formatter # Use it like Logger#add... or better not to use at all. def add(level, message = nil, progname = nil, &block) progname ||= default_options['facility'] message ||= block.call unless block.nil? if message.nil? message = progname progname = default_options['facility'] end message_hash = { 'facility' => progname } if message.is_a?(Hash) message.each do |key, value| message_hash[key.to_s] = value.to_s end else message_hash['short_message'] = message.to_s end if message.is_a?(Exception) message_hash.merge!(self.class.extract_hash_from_exception(message)) end if message_hash.key?('short_message') && !message_hash['short_message'].empty? notify_with_level(level, message_hash) end end # Redefines methods in +Notifier+. GELF::Levels.constants.each do |const| method_name = const.downcase define_method(method_name) do |progname=nil, &block| const_level = GELF.const_get(const) add(const_level, nil, progname, &block) end define_method("#{method_name}?") do const_level = GELF.const_get(const) const_level >= level end 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 end end gelf-3.1.0/CONTRIBUTING.md0000644000004100000410000000014313367446125014722 0ustar www-datawww-dataPlease follow [the instructions on graylog.org](https://www.graylog.org/contributing-to-graylog/). gelf-3.1.0/Gemfile0000644000004100000410000000050013367446125013761 0ustar www-datawww-datasource "https://rubygems.org" group :development do gem "shoulda", "~> 2.11.3" gem "jeweler", "~> 2.1.1" # Because of a dependency chain jeweler->github_api->oauth2->rack, # pin the version: Rack 2.0.x doesn't work on < Ruby 2.2 gem 'rack', '< 2.0' gem "mocha", "~> 1.1.0" gem "test-unit", "~> 3.2.0" end gelf-3.1.0/benchmarks/0000755000004100000410000000000013367446125014610 5ustar www-datawww-datagelf-3.1.0/benchmarks/notifier.rb0000755000004100000410000000226613367446125016765 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-3.1.0/VERSION0000644000004100000410000000000513367446125013536 0ustar www-datawww-data3.1.0gelf-3.1.0/CHANGELOG0000644000004100000410000000354513367446125013714 0ustar www-datawww-data3.0.0, 2016-08-21 + Overhaul TCP support + Include automatic support for Celluloid::IO if available + Add TLS support to TCP transport - Remove support for ancient Rubygems versions - Remove support for Ruby 1.9.2 (1.9.3 works!) - Remove already-deprecated `host` and `port` methods on GELF::Notifier 2.0.0, 2016-02-02 + Added GELF TCP support. 1.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.