logstash-logger 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 289d8d944755e6c3be2857d075b37d8e5c560a45
4
+ data.tar.gz: 8fdff518b64fdb1c71dc60ba6f9f75183a092831
5
+ SHA512:
6
+ metadata.gz: dc0992ffd0c6e3478d8018590bcb16fbd436713672c4fc9a6a64595d563002cea11e8f4dae61eb8c99b239f29215213c5ac1dd4dcfccf6ac766a5b241807848c
7
+ data.tar.gz: ad911e6164b8fc9d53d7f0afb54b68ce0d3f8eef091f4513920cb5fab8645cb5b3366b81c7e7763d491b342140f906bad55a101b341d2857a760252f59789e4c
data/README.md CHANGED
@@ -6,7 +6,7 @@ writing to a file or syslog since logstash can receive the structured data direc
6
6
 
7
7
  ## Features
8
8
 
9
- * Writes directly to logstash over a TCP connection.
9
+ * Writes directly to logstash over a UDP or TCP connection.
10
10
  * Always writes in logstash JSON format.
11
11
  * Logger can take a string message, a hash, a LogStash::Event, or a logstash-formatted json string as input.
12
12
  * Events are automatically populated with message, timestamp, host, and severity.
@@ -25,14 +25,23 @@ Or install it yourself as:
25
25
 
26
26
  $ gem install logstash-logger
27
27
 
28
- ## Usage
28
+ ## Basic Usage
29
29
 
30
- First set up a logstash agent to receive input over a TCP port.
30
+ First set up a logstash agent to receive input over a UDP or TCP port.
31
+ Then in ruby, create a LogStashLogger that writes to that port.
31
32
 
32
33
  ```ruby
34
+ require 'logstash-logger'
35
+
36
+ # Defaults to UDP
33
37
  logger = LogStashLogger.new('localhost', 5228)
34
38
  logger.info 'test'
35
- # Logs {"@source":"server-host-name","@tags":[],"@fields":{"severity":"INFO"},"@message":"test","@timestamp":"2012-12-15T00:48:29+00:00"}
39
+ # Writes the following to UDP port 5228:
40
+ # {"@source":"server-host-name","@tags":[],"@fields":{"severity":"INFO"},"@message":"test","@timestamp":"2013-04-08T18:56:23.767273+00:00"}
41
+
42
+ # Specify UDP or TCP explicitly
43
+ udp_logger = LogStashLogger.new('localhost', 5228, :udp)
44
+ tcp_logger = LogStashLogger.new('localhost', 5229, :tcp)
36
45
  ```
37
46
 
38
47
  ## Rails integration
@@ -49,7 +58,15 @@ To get Rails to nicely output its logs in structured logstash format, try one of
49
58
  * [yarder](https://github.com/rurounijones/yarder)
50
59
 
51
60
  Currently these gems output a JSON string, which LogStashLogger then parses.
52
- Future versions of these gems could potentially have deeper integration with LogStashLogger.
61
+ Future versions of these gems could potentially have deeper integration with LogStashLogger (i.e. by writing LogStash::Event objects).
62
+
63
+ ## UDP vs TCP
64
+ Should you write to a UDP or TCP listener? It depends on your specific needs, but most applications should use the default (UDP).
65
+
66
+ * UDP is faster because it's asynchronous (fire-and-forget). However, this means that log messages could get dropped. This is okay for most applications.
67
+ * TCP verifies that every message has been received via two-way communication . This could slow your app down to a crawl if the TCP listener is under heavy load.
68
+
69
+ For a more detailed discussion of UDP vs TCP, I recommend reading this article: [UDP vs. TCP](http://gafferongames.com/networking-for-game-programmers/udp-vs-tcp/)
53
70
 
54
71
  ## Ruby compatibility
55
72
 
@@ -61,7 +78,18 @@ Verified to work with:
61
78
 
62
79
  Ruby 1.8.7 is not supported because LogStash::Event is not compatible with Ruby 1.8.7. This might change in the future.
63
80
 
64
- Rubinius might work, but I haven't been able to test it.
81
+ The specs don't pass in Rubinius yet, but the logger does work.
82
+
83
+ ## Breaking change in version 0.3+
84
+ Earlier versions of this gem (<= 0.2.1) only implemented a TCP connection. Newer versions (>= 0.3) also implement UDP, and use that as the new default.
85
+ Please be aware if you are using the default constructor and still require TCP, you should add an additional argument:
86
+
87
+ ```ruby
88
+ # Now defaults to UDP instead of TCP
89
+ logger = LogStashLogger.new('localhost', 5228)
90
+ # Explicitly specify TCP instead of UDP
91
+ logger = LogStashLogger.new('localhost', 5228, :tcp)
92
+ ```
65
93
 
66
94
  ## Contributing
67
95
 
data/Rakefile CHANGED
@@ -2,10 +2,24 @@
2
2
  require "bundler/gem_tasks"
3
3
  require 'rspec/core/rake_task'
4
4
 
5
- desc "Run all specs"
5
+ desc "Run all specs with default options"
6
6
  RSpec::Core::RakeTask.new(:spec) do |t|
7
7
  t.rspec_opts = %w[--color]
8
8
  t.verbose = false
9
9
  end
10
10
 
11
- task :default => :spec
11
+ desc "Run specs with TCP socket"
12
+ RSpec::Core::RakeTask.new("spec:tcp") do |t|
13
+ ENV['SOCKET_TYPE'] = 'tcp'
14
+ t.rspec_opts = %w[--color]
15
+ t.verbose = false
16
+ end
17
+
18
+ desc "Run specs with UDP socket"
19
+ RSpec::Core::RakeTask.new("spec:udp") do |t|
20
+ ENV['SOCKET_TYPE'] = 'udp'
21
+ t.rspec_opts = %w[--color]
22
+ t.verbose = false
23
+ end
24
+
25
+ task :default => ["spec:tcp", "spec:udp"]
@@ -1,10 +1,9 @@
1
1
  require 'logstash-logger/version'
2
2
 
3
- require 'thread'
4
3
  require 'socket'
5
4
  require 'logstash-event'
6
5
 
7
- require 'logstash-logger/tcp_client'
6
+ require 'logstash-logger/socket'
8
7
  require 'logstash-logger/logger'
9
8
  require 'logstash-logger/version'
10
9
 
@@ -3,10 +3,10 @@ class LogStashLogger < ::Logger
3
3
  attr_reader :client
4
4
 
5
5
  LOGSTASH_EVENT_FIELDS = %w(@timestamp @tags @type @source @fields @message).freeze
6
- HOST = Socket.gethostname
6
+ HOST = ::Socket.gethostname
7
7
 
8
- def initialize(host, port)
9
- super(::LogStashLogger::TCPClient.new(host, port))
8
+ def initialize(host, port, socket_type=:udp)
9
+ super(::LogStashLogger::Socket.new(host, port, socket_type))
10
10
  end
11
11
 
12
12
  def add(severity, message = nil, progname = nil, &block)
@@ -0,0 +1,34 @@
1
+ class LogStashLogger::Socket
2
+ def initialize(host, port, socket_type = :udp)
3
+ @host = host
4
+ @port = port
5
+ @type = socket_type
6
+ @socket = nil
7
+ end
8
+
9
+ def write(event)
10
+ begin
11
+ connect unless @socket
12
+
13
+ @socket.write("#{event.to_hash.to_json}\n")
14
+ rescue => e
15
+ warn "#{self.class} - #{e.class} - #{e.message}"
16
+ close
17
+ @socket = nil
18
+ end
19
+ end
20
+
21
+ def close
22
+ @socket && @socket.close
23
+ rescue => e
24
+ warn "#{self.class} - #{e.class} - #{e.message}"
25
+ end
26
+
27
+ private
28
+ def connect
29
+ @socket = case @type
30
+ when :udp then UDPSocket.new.tap {|s| s.connect(@host, @port)}
31
+ when :tcp then TCPSocket.new(@host, @port)
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  require 'logger'
2
2
 
3
3
  class LogStashLogger < ::Logger
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,80 +1,130 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe LogStashLogger do
4
- subject { LogStashLogger.new('localhost', port)}
4
+ # The type of socket we're testing
5
+ def socket_type
6
+ @socket_type ||= (ENV['SOCKET_TYPE'] || 'UDP').to_s.downcase.to_sym
7
+ end
8
+
9
+ before(:all) { puts "Testing with #{socket_type.to_s.upcase} socket type" }
10
+
11
+ let(:host) { '0.0.0.0' }
12
+ let(:hostname) { Socket.gethostname }
5
13
  let(:port) { 5228 }
6
- let(:server) { TCPServer.new(port) }
7
- let(:host) { Socket.gethostname }
8
14
 
9
- before(:all) do
10
- server # Initialize TCP server
15
+ # The logstash logger
16
+ let(:logger) { LogStashLogger.new(host, port, socket_type) }
17
+ # The log device that the logger writes to
18
+ let(:logdev) { logger.instance_variable_get(:@logdev) }
19
+
20
+ let! :listener do
21
+ case socket_type
22
+ when :tcp
23
+ TCPServer.new(port)
24
+ when :udp
25
+ UDPSocket.new.tap {|socket| socket.bind(host, port)}
26
+ end
11
27
  end
12
28
 
29
+ # The TCP socket written to by the TCP logstash listener server
30
+ let(:tcp_client) { listener.accept }
31
+
32
+ # The logstash event to log
13
33
  let(:logstash_event) do
14
- event = LogStash::Event.new
15
- event.message = 'test'
16
- event['severity'] = 'INFO'
17
- event
34
+ LogStash::Event.new.tap do |event|
35
+ event.message = 'test'
36
+ event['severity'] = 'INFO'
37
+ end
18
38
  end
19
39
 
20
- def logdev
21
- subject.instance_variable_get(:@logdev)
40
+ # The raw input received by the logstash listener
41
+ let :listener_input do
42
+ case socket_type
43
+ when :tcp then tcp_client.readline
44
+ when :udp then listener.recvfrom(8192)[0]
45
+ end
22
46
  end
23
47
 
24
- it 'should use TCPClient as the log device' do
48
+ # The logstash event received by the listener
49
+ let(:listener_event) { LogStash::Event.new(JSON.parse listener_input) }
50
+
51
+ #before(:each) do
52
+ # Sync socket writes so we can receive them in the listener immediately
53
+ #@socket = logdev.instance_variable_get(:@dev).send(:connect)
54
+ #@socket.sync = true
55
+ #end
56
+
57
+ after(:each) do
58
+ listener.close
59
+ end
60
+
61
+ # The socket that the logger is writing to
62
+ #let(:socket) { @socket }
63
+
64
+ it 'uses a LogStashLogger::Socket as the log device' do
25
65
  logdev.should be_a Logger::LogDevice
26
- logdev.instance_variable_get(:@dev).should be_a LogStashLogger::TCPClient
66
+ logdev.instance_variable_get(:@dev).should be_a LogStashLogger::Socket
27
67
  end
28
68
 
29
- it 'should take a string message and write a logstash event' do
69
+ it 'takes a string message as input and writes a logstash event' do
30
70
  message = 'test'
31
-
32
- logdev.should_receive(:write) do |event|
71
+
72
+ logdev.should_receive(:write).and_call_original do |event|
33
73
  event.should be_a LogStash::Event
34
- event.source.should eql(host)
74
+ event.source.should eql(hostname)
35
75
  event.message.should eql(message)
36
76
  event['severity'].should eql('INFO')
37
77
  end
38
78
 
39
- subject.info(message)
79
+ logger.info(message)
80
+
81
+ listener_event.message.should == message
40
82
  end
41
83
 
42
- it 'should take a logstash-formatted json string and write out a logstash event' do
43
- logdev.should_receive(:write) do |event|
84
+ it 'takes a logstash-formatted json string as input and writes out a logstash event' do
85
+ logdev.should_receive(:write).and_call_original do |event|
44
86
  event.should be_a LogStash::Event
45
87
  event.message.should eql(logstash_event.message)
46
- event.source.should eql(host)
88
+ event.source.should eql(hostname)
47
89
  end
48
90
 
49
- subject.info(logstash_event.to_json)
91
+ logger.info(logstash_event.to_json)
92
+
93
+ listener_event.message.should == logstash_event.message
50
94
  end
51
95
 
52
- it 'should take a LogStash::Event and write it out' do
53
- logdev.should_receive(:write) do |event|
96
+ it 'takes a LogStash::Event as input and writes it out intact' do
97
+ logdev.should_receive(:write).and_call_original do |event|
54
98
  event.should be_a LogStash::Event
55
99
  event.message.should eql(logstash_event.message)
56
100
  event['severity'].should eql(logstash_event['severity'])
57
101
  event.timestamp.should eql(logstash_event.timestamp)
58
- event.source.should eql(host)
102
+ event.source.should eql(hostname)
59
103
  end
60
104
 
61
- subject.warn(logstash_event)
105
+ logger.warn(logstash_event)
106
+
107
+ listener_event.message.should == logstash_event.message
108
+ listener_event['severity'].should == logstash_event['severity']
62
109
  end
63
110
 
64
- it 'should take a hash and write out a logstash event' do
111
+ it 'takes a data hash as input and writes out a logstash event' do
65
112
  data = {
66
113
  "@message" => 'test',
67
114
  'severity' => 'INFO'
68
115
  }
69
116
 
70
- logdev.should_receive(:write) do |event|
117
+ logdev.should_receive(:write).and_call_original do |event|
71
118
  event.should be_a LogStash::Event
72
119
  event.message.should eql('test')
73
120
  event['severity'].should eql('INFO')
74
- event.source.should eql(host)
121
+ event.source.should eql(hostname)
75
122
  end
76
123
 
77
- subject.info(data)
124
+ logger.info(data.dup)
125
+
126
+ listener_event.message.should == data["@message"]
127
+ listener_event['severity'].should == data['severity']
78
128
  end
79
129
 
80
130
  end
metadata CHANGED
@@ -1,62 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - David Butler
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-12 00:00:00.000000000 Z
11
+ date: 2013-04-08 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: logstash-event
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rspec
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  description: Ruby logger that writes directly to LogStash
@@ -74,40 +67,33 @@ files:
74
67
  - Rakefile
75
68
  - lib/logstash-logger.rb
76
69
  - lib/logstash-logger/logger.rb
77
- - lib/logstash-logger/tcp_client.rb
70
+ - lib/logstash-logger/socket.rb
78
71
  - lib/logstash-logger/version.rb
79
72
  - logstash-logger.gemspec
80
73
  - spec/logger_spec.rb
81
74
  - spec/spec_helper.rb
82
75
  homepage: http://github.com/dwbutler/logstash-logger
83
76
  licenses: []
77
+ metadata: {}
84
78
  post_install_message:
85
79
  rdoc_options: []
86
80
  require_paths:
87
81
  - lib
88
82
  required_ruby_version: !ruby/object:Gem::Requirement
89
- none: false
90
83
  requirements:
91
- - - ! '>='
84
+ - - '>='
92
85
  - !ruby/object:Gem::Version
93
86
  version: '0'
94
- segments:
95
- - 0
96
- hash: -3685342851697372128
97
87
  required_rubygems_version: !ruby/object:Gem::Requirement
98
- none: false
99
88
  requirements:
100
- - - ! '>='
89
+ - - '>='
101
90
  - !ruby/object:Gem::Version
102
91
  version: '0'
103
- segments:
104
- - 0
105
- hash: -3685342851697372128
106
92
  requirements: []
107
93
  rubyforge_project:
108
- rubygems_version: 1.8.25
94
+ rubygems_version: 2.0.0
109
95
  signing_key:
110
- specification_version: 3
96
+ specification_version: 4
111
97
  summary: LogStash Logger for ruby
112
98
  test_files:
113
99
  - spec/logger_spec.rb
@@ -1,30 +0,0 @@
1
- class LogStashLogger::TCPClient
2
- def initialize(host, port)
3
- @host = host
4
- @port = port
5
- @socket = nil
6
- end
7
-
8
- def write(event)
9
- begin
10
- connect unless @socket
11
-
12
- @socket.write("#{event.to_hash.to_json}\n")
13
- rescue => e
14
- warn "LogStashLogger::TCPClient - #{e.class} - #{e.message}"
15
- close
16
- @socket = nil
17
- end
18
- end
19
-
20
- def close
21
- @socket && @socket.close
22
- rescue => e
23
- warn "LogStashLogger::TCPClient - #{e.class} - #{e.message}"
24
- end
25
-
26
- private
27
- def connect
28
- @socket = TCPSocket.new(@host, @port)
29
- end
30
- end