logglier 0.1.1 → 0.2.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.
@@ -1,32 +1,56 @@
1
1
  module Logglier
2
-
3
2
  module Client
3
+ class HTTP
4
4
 
5
- module HTTP
5
+ # Used to wrap and setup Net::HTTP as we need it
6
+ class NetHTTPProxy
6
7
 
7
- class Sync
8
- include Logglier::Client::InstanceMethods
9
- include Logglier::Client::HTTP::InstanceMethods
8
+ # @param [URI] input_uri URI to deliver messages to
9
+ # @param [Hash] opts Option hash
10
+ # @option [Integer] read_timeout Read timeout for the http session. defaults to 2
11
+ # @option [Integer] open_timeout Open timeout for the http session. defaults to 2
12
+ # @option [Integer] verify_mode OpenSSL::SSL::VERIFY_* constant
13
+ # @option [String] ca_file Path to the ca file
14
+ def initialize(input_uri, opts={})
15
+ @input_uri = input_uri
10
16
 
11
- attr_reader :input_uri, :http
17
+ @http = Net::HTTP.new(@input_uri.host, @input_uri.port)
12
18
 
13
- def initialize(opts={})
14
- setup_input_uri(opts)
19
+ if @input_uri.scheme == 'https'
20
+ @http.use_ssl = true
21
+ @http.verify_mode = opts[:verify_mode] || OpenSSL::SSL::VERIFY_PEER
22
+ @http.ca_file = opts[:ca_file] if opts[:ca_file]
23
+ end
15
24
 
16
- setup_http(opts)
17
- end
25
+ # We prefer persistent HTTP connections, so workaround http://redmine.ruby-lang.org/issues/4522
26
+ @http.start
18
27
 
19
- # Required by Logger::LogDevice
20
- def write(message)
21
- deliver(message)
28
+ @http.read_timeout = opts[:read_timeout] || 2
29
+ @http.open_timeout = opts[:open_timeout] || 2
22
30
  end
23
31
 
24
- # Required by Logger::LogDevice
25
- def close
26
- nil
32
+ # Delivers the message via HTTP, handling errors
33
+ #
34
+ # @param [String] message The message to deliver
35
+ def deliver(message)
36
+ retried = false
37
+ begin
38
+ @http.request_post(@input_uri.path, message)
39
+ # We're using persistent connections, so connection can be closed by the other side
40
+ # after a timeout. Don't consider it an error, just retry once.
41
+ rescue Errno::ECONNRESET
42
+ unless retried
43
+ retried = true
44
+ retry
45
+ else
46
+ $stderr.puts "WARNING: connection was reset while posting message: #{message}"
47
+ end
48
+ rescue TimeoutError, OpenSSL::SSL::SSLError, EOFError, Errno::ECONNRESET => e
49
+ $stderr.puts "WARNING: #{e.class} posting message: #{message}"
50
+ end
27
51
  end
28
-
29
52
  end
53
+
30
54
  end
31
55
  end
32
56
  end
@@ -1,28 +1,31 @@
1
1
  require 'thread'
2
2
 
3
3
  module Logglier
4
-
5
4
  module Client
6
-
7
- module HTTP
5
+ class HTTP
8
6
 
9
7
  # Used by the Threaded client to hold a queue, deliver messsages from it
10
8
  # and to ensure it's flushed on program exit.
11
9
  #
12
- # Not meant to be used directly.
10
+ # @note Uses NetHTTPProxy
11
+ #
13
12
  class DeliveryThread < Thread
14
- include Logglier::Client::HTTP::InstanceMethods
15
13
 
16
- # @param [URI] input_uri The uri to deliver messags to
17
- # @param [Integer] read_timeout Read timeout for the http session. defaults to 120
18
- # @param [Integer] open_timeout Open timeout for the http session. defaults to 120
14
+ # @param [URI] input_uri The uri to deliver messages to
15
+ # @param [Hash] opts Option hash
16
+ # @option [Integer] read_timeout Read timeout for the http session. defaults to 120
17
+ # @option [Integer] open_timeout Open timeout for the http session. defaults to 120
19
18
  #
19
+ # @note See NetHTTPProxy for further option processing of opts
20
20
  # @note registers an at_exit handler that signals exit intent and joins the thread.
21
- def initialize(input_uri, read_timeout=120, open_timeout=120)
21
+ def initialize(input_uri, opts={})
22
22
 
23
23
  @input_uri = input_uri
24
24
 
25
- setup_http( {:read_timeout => read_timeout, :open_timeout => open_timeout} )
25
+ opts[:read_timeout] = opts[:read_timeout] || 120
26
+ opts[:open_timeout] = opts[:open_timeout] || 120
27
+
28
+ @http = Logglier::Client::HTTP::NetHTTPProxy.new(@input_uri, opts)
26
29
 
27
30
  @queue = Queue.new
28
31
  @exiting = false
@@ -31,7 +34,7 @@ module Logglier
31
34
  loop do
32
35
  msg = @queue.pop
33
36
  break if msg == :__delivery_thread_exit_signal__
34
- deliver(msg)
37
+ @http.deliver(msg)
35
38
  end
36
39
  end
37
40
 
@@ -48,36 +51,11 @@ module Logglier
48
51
  end
49
52
 
50
53
  # Pushes a message onto the internal queue
51
- def push(message)
54
+ def deliver(message)
52
55
  @queue.push(message)
53
56
  end
54
57
  end
55
58
 
56
- # Interface to the DeliveryThread
57
- class Threaded
58
- include Logglier::Client::InstanceMethods
59
-
60
- attr_reader :input_uri, :delivery_thread
61
-
62
- def initialize(opts={})
63
- setup_input_uri(opts)
64
- @delivery_thread = DeliveryThread.new(@input_uri, opts[:read_timeout] || 120, opts[:open_timeout] || 120)
65
- end
66
-
67
- # Required by Logger::LogDevice
68
- # @param [String] message the message to deliver
69
- #
70
- # @note doesn't do actual deliver. Pushes the messages off to the delivery thread
71
- def write(message)
72
- @delivery_thread.push(message)
73
- end
74
-
75
- # Required by Logger::LogDevice
76
- def close
77
- nil
78
- end
79
-
80
- end
81
59
  end
82
60
  end
83
61
  end
@@ -1,52 +1,33 @@
1
1
  require 'net/https'
2
2
  require 'uri'
3
3
 
4
+ require File.join(File.dirname(__FILE__), 'http', 'sync')
5
+ require File.join(File.dirname(__FILE__), 'http', 'threaded')
6
+
4
7
  module Logglier
5
8
  module Client
6
- module HTTP
9
+ class HTTP
10
+ include Logglier::Client::InstanceMethods
11
+
12
+ attr_reader :input_uri, :deliverer
7
13
 
8
- def self.new(opts={})
9
- if opts[:threaded]
10
- Logglier::Client::HTTP::Threaded.new(opts)
14
+ def initialize(opts={})
15
+ setup_input_uri(opts)
16
+ @deliverer = if opts[:threaded]
17
+ Logglier::Client::HTTP::DeliveryThread.new(@input_uri, opts)
11
18
  else
12
- Logglier::Client::HTTP::Sync.new(opts)
19
+ Logglier::Client::HTTP::NetHTTPProxy.new(@input_uri, opts)
13
20
  end
14
21
  end
15
22
 
16
- module InstanceMethods
17
-
18
- # Sets up @http
19
- #
20
- # @param [Hash] opts The options Hash
21
- # @option opts [String] :read_timeout The read timeout for the http connection
22
- # @option opts [String] :open_timeout The open timeout for the http connection
23
- def setup_http(opts={})
24
- @http = Net::HTTP.new(@input_uri.host, @input_uri.port)
25
- if @input_uri.scheme == 'https'
26
- @http.use_ssl = true
27
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
28
- end
29
-
30
- @http.read_timeout = opts[:read_timeout] || 2
31
- @http.open_timeout = opts[:open_timeout] || 2
32
- end
33
-
34
- # Delivers the message via HTTP, handling errors
35
- #
36
- # @param [String] message The message to deliver
37
- def deliver(message)
38
- begin
39
- @http.request_post(@input_uri.path, message)
40
- rescue TimeoutError, OpenSSL::SSL::SSLError, EOFError, Errno::ECONNRESET => e
41
- $stderr.puts "WARNING: #{e.class} posting message: #{message}"
42
- end
43
- end
44
-
23
+ def write(message)
24
+ @deliverer.deliver(message)
45
25
  end
46
26
 
27
+ def close
28
+ nil
29
+ end
47
30
  end
48
31
  end
49
32
  end
50
33
 
51
- require File.join(File.dirname(__FILE__), 'http', 'sync')
52
- require File.join(File.dirname(__FILE__), 'http', 'threaded')
@@ -1,3 +1,3 @@
1
1
  module Logglier
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
data/spec/client_spec.rb CHANGED
@@ -16,9 +16,13 @@ describe Logglier::Client do
16
16
 
17
17
  context "that is a valid http uri" do
18
18
 
19
+ before do
20
+ Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { MockNetHTTPProxy.new }
21
+ end
22
+
19
23
  it "should return an instance of the proper client" do
20
24
  log = Logglier::Client.new('http://localhost')
21
- log.should be_an_instance_of Logglier::Client::HTTP::Sync
25
+ log.should be_an_instance_of Logglier::Client::HTTP
22
26
  end
23
27
 
24
28
  end
@@ -62,51 +66,77 @@ describe Logglier::Client do
62
66
 
63
67
  end
64
68
 
65
- context "HTTPS" do
66
- context "#write" do
67
- context "with a simple text message" do
68
- it "should post a message" do
69
- log = Logglier::Client.new('https://localhost')
70
- log.http.stub(:request_post)
71
- log.http.should_receive(:request_post).with('','msg')
72
- log.write('msg')
73
- end
74
- end
69
+ context "message formatting methods" do
70
+
71
+ before do
72
+ Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { MockNetHTTPProxy.new }
75
73
  end
76
74
 
77
- context "message formatting methods" do
78
- subject { Logglier::Client.new('https://localhost') }
75
+ subject { Logglier::Client.new('https://localhost') }
79
76
 
80
- it "should mash out hashes" do
81
- message = subject.massage_message({:foo => :bar},"WARN")
82
- message.should =~ /^severity=WARN,/
83
- message.should =~ /foo=bar/
84
- end
77
+ it "should mash out hashes" do
78
+ message = subject.massage_message({:foo => :bar},"WARN")
79
+ message.should =~ /^severity=WARN,/
80
+ message.should =~ /foo=bar/
81
+ end
85
82
 
86
- it "should mash out nested hashes" do
87
- message = subject.massage_message({:foo => :bar, :bazzle => { :bom => :bastic } }, "WARN")
88
- message.should =~ /^severity=WARN,/
89
- message.should =~ /foo=bar/
90
- message.should =~ /bazzle\.bom=bastic/
91
- end
83
+ it "should mash out nested hashes" do
84
+ message = subject.massage_message({:foo => :bar, :bazzle => { :bom => :bastic } }, "WARN")
85
+ message.should =~ /^severity=WARN,/
86
+ message.should =~ /foo=bar/
87
+ message.should =~ /bazzle\.bom=bastic/
88
+ end
89
+
90
+ it "should mash out deeply nested hashes" do
91
+ message = subject.massage_message({:foo => :bar, :bazzle => { :bom => :bastic, :totally => { :freaking => :funny } } }, "WARN")
92
+ message.should =~ /^severity=WARN,/
93
+ message.should =~ /foo=bar/
94
+ message.should =~ /bazzle\.bom=bastic/
95
+ message.should =~ /bazzle\.totally\.freaking=funny/
96
+ end
92
97
 
93
- it "should mash out deeply nested hashes" do
94
- message = subject.massage_message({:foo => :bar, :bazzle => { :bom => :bastic, :totally => { :freaking => :funny } } }, "WARN")
95
- message.should =~ /^severity=WARN,/
96
- message.should =~ /foo=bar/
97
- message.should =~ /bazzle\.bom=bastic/
98
- message.should =~ /bazzle\.totally\.freaking=funny/
98
+ it "should mash out deeply nested hashes, with an array" do
99
+ message = subject.massage_message({:foo => :bar, :taste => ["this","sauce"], :bazzle => { :bom => :bastic, :totally => { :freaking => :funny } } }, "WARN")
100
+ message.should =~ /^severity=WARN,/
101
+ message.should =~ /foo=bar/
102
+ message.should =~ /taste=\["this", "sauce"\]/
103
+ message.should =~ /bazzle\.bom=bastic/
104
+ message.should =~ /bazzle\.totally\.freaking=funny/
105
+ end
106
+ end
107
+
108
+ context "HTTPS" do
109
+
110
+ before do
111
+ @mock_http = MockNetHTTPProxy.new
112
+ Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { @mock_http }
113
+ end
114
+
115
+ context "Sync" do
116
+ context "#write" do
117
+ context "with a simple text message" do
118
+ it "should deliver a message" do
119
+ log = Logglier::Client.new('https://localhost')
120
+ @mock_http.should_receive(:deliver).with('msg')
121
+ log.write('msg')
122
+ end
123
+ end
99
124
  end
125
+ end
100
126
 
101
- it "should mash out deeply nested hashes, with an array" do
102
- message = subject.massage_message({:foo => :bar, :taste => ["this","sauce"], :bazzle => { :bom => :bastic, :totally => { :freaking => :funny } } }, "WARN")
103
- message.should =~ /^severity=WARN,/
104
- message.should =~ /foo=bar/
105
- message.should =~ /taste=\["this", "sauce"\]/
106
- message.should =~ /bazzle\.bom=bastic/
107
- message.should =~ /bazzle\.totally\.freaking=funny/
127
+ context "Threaded" do
128
+ context "#write" do
129
+ context "with a simple text message" do
130
+ it "should deliver a message" do
131
+ log = Logglier::Client.new('https://localhost', :threaded => true)
132
+ @mock_http.should_receive(:deliver).with('msg')
133
+ log.write('msg')
134
+ sleep 5
135
+ end
136
+ end
108
137
  end
109
138
  end
139
+
110
140
  end
111
141
 
112
142
  context "Syslog" do
data/spec/http_spec.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'HTTP' do
4
+ before do
5
+ @http = mock('Net::HTTP')
6
+ @http.stub!(:start)
7
+ @http.stub!(:read_timeout=)
8
+ @http.stub!(:open_timeout=)
9
+ Net::HTTP.stub!(:new).and_return(@http)
10
+ @proxy = Logglier::Client::HTTP::NetHTTPProxy.new(URI.parse('http://localhost:9292'))
11
+ end
12
+
13
+ it "retries after connection is reset" do
14
+ @http.should_receive(:request_post).and_raise Errno::ECONNRESET
15
+ @http.should_receive(:request_post)
16
+ @proxy.deliver('message')
17
+ end
18
+
19
+ it "retries only once connection is reset" do
20
+ @http.should_receive(:request_post).exactly(2).times.and_raise Errno::ECONNRESET
21
+ @proxy.deliver('message')
22
+ end
23
+ it "fails for other errors" do
24
+ @http.should_receive(:request_post).once.and_raise EOFError
25
+ @proxy.deliver('message')
26
+ end
27
+ end
@@ -1,4 +1,9 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Logglier do
4
+ before do
5
+ Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { MockNetHTTPProxy.new }
6
+ end
2
7
 
3
8
  context "HTTPS" do
4
9
 
@@ -6,7 +11,7 @@ describe Logglier do
6
11
  subject { new_logglier('https://localhost') }
7
12
 
8
13
  it { should be_an_instance_of Logger }
9
- its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP::Sync }
14
+ its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP }
10
15
 
11
16
  it_should_behave_like "a logglier enhanced Logger instance"
12
17
  end
@@ -15,7 +20,7 @@ describe Logglier do
15
20
  subject { new_logglier('https://localhost', :threaded => true) }
16
21
 
17
22
  it { should be_an_instance_of Logger }
18
- its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP::Threaded }
23
+ its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP }
19
24
 
20
25
  it_should_behave_like "a logglier enhanced Logger instance"
21
26
  end
data/spec/spec_helper.rb CHANGED
@@ -21,6 +21,11 @@ RSpec.configure do |config|
21
21
  def send(*args); end
22
22
  end
23
23
 
24
+ class MockNetHTTPProxy
25
+ def initialize(*args); end
26
+ def deliver(*args); end
27
+ end
28
+
24
29
  def new_logglier(url,opts={})
25
30
  log = Logglier.new(url,opts)
26
31
  log.extend(LoggerHacks)
@@ -1,14 +1,16 @@
1
- describe Logglier::Client::HTTP::DeliveryThread do
2
-
3
- subject { described_class.new(URI.parse('http://localhost')) }
1
+ require 'spec_helper'
4
2
 
3
+ describe Logglier::Client::HTTP::DeliveryThread do
5
4
  before do
6
- subject.stub(:deliver)
5
+ @mock_http = MockNetHTTPProxy.new
6
+ Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { @mock_http }
7
7
  end
8
8
 
9
- it "should" do
10
- subject.should_receive(:deliver).with("test")
11
- subject.push('test')
9
+ subject { described_class.new(URI.parse('http://localhost')) }
10
+
11
+ it "should deliver the message" do
12
+ @mock_http.should_receive(:deliver).with("test")
13
+ subject.deliver('test')
12
14
 
13
15
  #Signal the thread it's going to exit
14
16
  subject.exit!
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logglier
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Edward Muller (aka freeformz)
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-11 00:00:00 -07:00
18
+ date: 2011-06-16 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -72,6 +72,7 @@ files:
72
72
  - ./lib/logglier/version.rb
73
73
  - ./lib/logglier.rb
74
74
  - ./spec/client_spec.rb
75
+ - ./spec/http_spec.rb
75
76
  - ./spec/logglier_spec.rb
76
77
  - ./spec/spec_helper.rb
77
78
  - ./spec/threaded_spec.rb
@@ -111,6 +112,7 @@ specification_version: 3
111
112
  summary: Loggly 'plugin' for Logger
112
113
  test_files:
113
114
  - ./spec/client_spec.rb
115
+ - ./spec/http_spec.rb
114
116
  - ./spec/logglier_spec.rb
115
117
  - ./spec/spec_helper.rb
116
118
  - ./spec/threaded_spec.rb