logglier 0.1.1 → 0.2.0

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