logstash-logger 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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