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.
- data/lib/logglier/client/http/sync.rb +41 -17
- data/lib/logglier/client/http/threaded.rb +15 -37
- data/lib/logglier/client/http.rb +17 -36
- data/lib/logglier/version.rb +1 -1
- data/spec/client_spec.rb +67 -37
- data/spec/http_spec.rb +27 -0
- data/spec/logglier_spec.rb +7 -2
- data/spec/spec_helper.rb +5 -0
- data/spec/threaded_spec.rb +9 -7
- metadata +7 -5
@@ -1,32 +1,56 @@
|
|
1
1
|
module Logglier
|
2
|
-
|
3
2
|
module Client
|
3
|
+
class HTTP
|
4
4
|
|
5
|
-
|
5
|
+
# Used to wrap and setup Net::HTTP as we need it
|
6
|
+
class NetHTTPProxy
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
17
|
+
@http = Net::HTTP.new(@input_uri.host, @input_uri.port)
|
12
18
|
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
25
|
+
# We prefer persistent HTTP connections, so workaround http://redmine.ruby-lang.org/issues/4522
|
26
|
+
@http.start
|
18
27
|
|
19
|
-
|
20
|
-
|
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
|
-
#
|
25
|
-
|
26
|
-
|
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
|
-
#
|
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
|
17
|
-
# @param [
|
18
|
-
# @
|
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,
|
21
|
+
def initialize(input_uri, opts={})
|
22
22
|
|
23
23
|
@input_uri = input_uri
|
24
24
|
|
25
|
-
|
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
|
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
|
data/lib/logglier/client/http.rb
CHANGED
@@ -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
|
-
|
9
|
+
class HTTP
|
10
|
+
include Logglier::Client::InstanceMethods
|
11
|
+
|
12
|
+
attr_reader :input_uri, :deliverer
|
7
13
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
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::
|
19
|
+
Logglier::Client::HTTP::NetHTTPProxy.new(@input_uri, opts)
|
13
20
|
end
|
14
21
|
end
|
15
22
|
|
16
|
-
|
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')
|
data/lib/logglier/version.rb
CHANGED
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
|
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 "
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
78
|
-
subject { Logglier::Client.new('https://localhost') }
|
75
|
+
subject { Logglier::Client.new('https://localhost') }
|
79
76
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
102
|
-
|
103
|
-
message
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
data/spec/logglier_spec.rb
CHANGED
@@ -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
|
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
|
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
data/spec/threaded_spec.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
|
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
|
-
|
5
|
+
@mock_http = MockNetHTTPProxy.new
|
6
|
+
Logglier::Client::HTTP::NetHTTPProxy.stub(:new) { @mock_http }
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
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:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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-
|
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
|