logglier 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source :gemcutter
2
2
 
3
- gemspec 'logglier'
3
+ gemspec
4
+
5
+ gem "jruby-openssl", :platforms => :jruby
data/README.md CHANGED
@@ -3,7 +3,8 @@ Overview
3
3
 
4
4
  Send logged messages to Loggly using either the HTTP API or Syslog/UDP.
5
5
 
6
- Can be used in place of Ruby's Logger (<http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/>)
6
+ Can be used in place of Ruby's Logger
7
+ (<http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/>)
7
8
 
8
9
  In fact, it (currently) returns an instance of Logger.
9
10
 
@@ -32,21 +33,35 @@ Input URLs
32
33
  ### HTTP Inputs
33
34
  Logglier.new('https://logs.loggly.com/inputs/<id>')
34
35
 
35
- The id is provided by loggly, look at the input's details page
36
- To make sure the http client doesn't block too long read_timeout and
36
+ The id is provided by loggly, look at the input's details page To make
37
+ sure the http client doesn't block too long read_timeout and
37
38
  open_timeout are set to 2 seconds by default. This can be overridden
38
39
  like so:
39
40
 
40
- Logglier.new(:input_url => 'https://logs.loggle.com/inputs/<id>',
41
+ Logglier.new('https://logs.loggly.com/inputs/<id>',
41
42
  :read_timeout => <#>,
42
43
  :open_timeout => <#> )
43
44
 
45
+ #### Threaded Delivery
46
+
47
+ Creating a new Logglier instance, pointed at a http input, with the
48
+ `:threaded => true` option will tell Logglier to deliver log messages
49
+ for that logger in a seperate thread. Each new Logglier instance gets
50
+ it's own delivery thread and those threads are joined at exit to ensure
51
+ log message delivery.
52
+
53
+ Example:
54
+
55
+ Logglier.new('https://logs.loggly.com/inputs/<id>',
56
+ :threaded => true)
57
+
44
58
  ### Syslog TCP/UDP Inputs
45
59
 
46
60
  Logglier.new('[udp|tcp]://<hostname>:<port>/<facility>')
47
61
 
48
62
  The facility is optional and defaults to 16 (local0) if none is
49
- specified. Facilities are just integers from 0 to 23, see <http://www.faqs.org/rfcs/rfc3164.html>
63
+ specified. Facilities are just integers from 0 to 23, see
64
+ <http://www.faqs.org/rfcs/rfc3164.html>
50
65
 
51
66
 
52
67
  Logging
@@ -86,4 +101,4 @@ TODO
86
101
 
87
102
  * Alternative https implementations (Typheous, Excon, etc). May be
88
103
  faster?
89
- * Do logging in a seperate thread. Is this useful? Too complex?
104
+ * EM Integration?
@@ -0,0 +1,42 @@
1
+ module Logglier
2
+
3
+ module Client
4
+
5
+ module HTTP
6
+
7
+ class Sync
8
+ include Logglier::Client::InstanceMethods
9
+
10
+ attr_reader :input_uri, :http
11
+
12
+ def initialize(opts={})
13
+ setup_input_uri(opts)
14
+
15
+ @http = Net::HTTP.new(@input_uri.host, @input_uri.port)
16
+ if @input_uri.scheme == 'https'
17
+ @http.use_ssl = true
18
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
19
+ end
20
+
21
+ @http.read_timeout = opts[:read_timeout] || 2
22
+ @http.open_timeout = opts[:open_timeout] || 2
23
+ end
24
+
25
+ # Required by Logger::LogDevice
26
+ def write(message)
27
+ begin
28
+ @http.request_post(@input_uri.path, message)
29
+ rescue TimeoutError => e
30
+ $stderr.puts "WARNING: TimeoutError posting message: #{message}"
31
+ end
32
+ end
33
+
34
+ # Required by Logger::LogDevice
35
+ def close
36
+ nil
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,98 @@
1
+ require 'thread'
2
+
3
+ module Logglier
4
+
5
+ module Client
6
+
7
+ module HTTP
8
+
9
+ # Used by the Threaded client to hold a queue, deliver messsages from it
10
+ # and to ensure it's flushed on program exit.
11
+ #
12
+ # Not meant to be used directly.
13
+ class DeliveryThread < Thread
14
+
15
+ # @param [URI] input_uri The uri to deliver messags to
16
+ # @param [Integer] read_timeout Read timeout for the http session. defaults to 120
17
+ # @param [Integer] open_timeout Open timeout for the http session. defaults to 120
18
+ #
19
+ # @note registers an at_exit handler that signals exit intent and joins the thread.
20
+ def initialize(input_uri, read_timeout=120, open_timeout=120)
21
+
22
+ @input_uri = input_uri
23
+
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 = read_timeout
31
+ @http.open_timeout = open_timeout
32
+
33
+ @queue = Queue.new
34
+ @exiting = false
35
+
36
+ super do
37
+ until @exiting && @queue.empty?
38
+ deliver(@queue.pop)
39
+ end
40
+ end
41
+
42
+ at_exit {
43
+ exit!
44
+ join
45
+ }
46
+ end
47
+
48
+ # Signals the queue that we're exiting
49
+ def exit!
50
+ @exiting = true
51
+ @queue.push(:__delivery_thread_exit_signal__)
52
+ end
53
+
54
+ # Delivers individual messages via http
55
+ def deliver(message)
56
+ unless message == :__delivery_thread_exit_signal__
57
+ begin
58
+ @http.request_post(@input_uri.path, message)
59
+ rescue TimeoutError => e
60
+ $stderr.puts "WARNING: TimeoutError posting message: #{message}"
61
+ end
62
+ end
63
+ end
64
+
65
+ # Pushes a message onto the internal queue
66
+ def push(message)
67
+ @queue.push(message)
68
+ end
69
+ end
70
+
71
+ # Interface to the DeliveryThread
72
+ class Threaded
73
+ include Logglier::Client::InstanceMethods
74
+
75
+ attr_reader :input_uri, :delivery_thread
76
+
77
+ def initialize(opts={})
78
+ setup_input_uri(opts)
79
+ @delivery_thread = DeliveryThread.new(@input_uri, opts[:read_timeout] || 120, opts[:open_timeout] || 120)
80
+ end
81
+
82
+ # Required by Logger::LogDevice
83
+ # @param [String] message the message to deliver
84
+ #
85
+ # @note doesn't do actual deliver. Pushes the messages off to the delivery thread
86
+ def write(message)
87
+ @delivery_thread.push(message)
88
+ end
89
+
90
+ # Required by Logger::LogDevice
91
+ def close
92
+ nil
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+ end
@@ -2,48 +2,20 @@ require 'net/https'
2
2
  require 'uri'
3
3
 
4
4
  module Logglier
5
-
6
5
  module Client
6
+ module HTTP
7
7
 
8
- class HTTP
9
- include Logglier::Client::InstanceMethods
10
-
11
- attr_reader :input_uri, :http
12
-
13
- def initialize(opts={})
14
- setup_input_uri(opts)
15
-
16
- @http = Net::HTTP.new(@input_uri.host, @input_uri.port)
17
- if @input_uri.scheme == 'https'
18
- @http.use_ssl = true
19
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
20
- end
21
-
22
- @http.read_timeout = opts[:read_timeout] || 2
23
- @http.open_timeout = opts[:open_timeout] || 2
24
- end
25
-
26
- # Required by Logger::LogDevice
27
- def write(message)
28
- begin
29
- @http.request_post(@input_uri.path, message)
30
- rescue TimeoutError => e
31
- $stderr.puts "WARNING: TimeoutError posting message: #{message}"
32
- end
33
- end
34
-
35
- def formatter
36
- proc do |severity, datetime, progname, msg|
37
- message = "#{datetime} "
38
- message << massage_message(msg, severity)
8
+ def self.new(opts={})
9
+ if opts[:threaded]
10
+ Logglier::Client::HTTP::Threaded.new(opts)
11
+ else
12
+ Logglier::Client::HTTP::Sync.new(opts)
39
13
  end
40
14
  end
41
15
 
42
- # Required by Logger::LogDevice
43
- def close
44
- nil
45
- end
46
-
47
16
  end
48
17
  end
49
18
  end
19
+
20
+ require File.join(File.dirname(__FILE__), 'http', 'sync')
21
+ require File.join(File.dirname(__FILE__), 'http', 'threaded')
@@ -1,12 +1,21 @@
1
1
  module Logglier
2
2
  module Client
3
3
 
4
- def self.new(opts={})
5
- if opts.nil? or opts.empty?
6
- raise InputURLRequired.new
4
+ # Creates a new loggly client, based on a url scheme and options provided
5
+ # @param [Hash,String] opts the options hash or url string
6
+ # @option opts [String] :input_url The Loggly input_url
7
+ #
8
+ # If a url string is passed, it becomes {:input_url => <string>}
9
+ #
10
+ #
11
+ # @raise [Logglier::UnsupportedScheme] if the :input_url isn't recognized
12
+ # @return [Logglier::Client::HTTP, Logglier::Client::Syslog] returns an instance of the Logglier Client class
13
+ def self.new(input_url, opts={})
14
+ unless input_url
15
+ raise URLRequired.new
7
16
  end
8
17
 
9
- opts = { :input_url => opts } if opts.is_a?(String)
18
+ opts.merge!({ :input_url => input_url })
10
19
 
11
20
  begin
12
21
  input_uri = URI.parse(opts[:input_url])
@@ -46,6 +55,13 @@ module Logglier
46
55
  end.join(", ")
47
56
  end
48
57
 
58
+ def formatter
59
+ proc do |severity, datetime, progname, msg|
60
+ message = "#{datetime} "
61
+ message << massage_message(msg, severity)
62
+ end
63
+ end
64
+
49
65
  def massage_message(incoming_message, severity)
50
66
  outgoing_message = ""
51
67
  outgoing_message << "severity=#{severity}, "
@@ -66,7 +82,7 @@ module Logglier
66
82
  begin
67
83
  @input_uri = URI.parse(@input_uri)
68
84
  rescue URI::InvalidURIError => e
69
- raise InputURLRequired.new("Invalid Input URL: #{@input_uri}")
85
+ raise URLRequired.new("Invalid Input URL: #{@input_uri}")
70
86
  end
71
87
  end
72
88
 
@@ -1,3 +1,3 @@
1
1
  module Logglier
2
- VERSION = '0.0.5'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/logglier.rb CHANGED
@@ -8,8 +8,8 @@ module Logglier
8
8
  class UnsupportedScheme < ArgumentError; end
9
9
  class UnknownFacility < ArgumentError; end
10
10
 
11
- def self.new(opts={})
12
- client = Logglier::Client.new(opts)
11
+ def self.new(url, opts={})
12
+ client = Logglier::Client.new(url, opts)
13
13
  logger = Logger.new(client)
14
14
 
15
15
  if client.respond_to?(:formatter)
data/logglier.gemspec CHANGED
@@ -4,15 +4,15 @@ require File.expand_path(File.join(dir, 'lib', 'logglier', 'version'))
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "logglier"
6
6
  s.version = Logglier::VERSION
7
- s.date = '2011-02-25'
7
+ s.date = Time.now
8
8
  s.summary = "Loggly 'plugin' for Logger"
9
9
  s.description =<<EOD
10
10
  Logger => Loggly
11
11
  EOD
12
12
 
13
13
  s.authors = ["Edward Muller (aka freeformz)"]
14
- s.email =
15
- s.homepage = "http://loggly.com"
14
+ s.email = "edwardam@interlix.com"
15
+ s.homepage = "http://icanhazdowntime.org"
16
16
 
17
17
  s.files = %w{ README.md Gemfile LICENSE logglier.gemspec Rakefile } + Dir["#{dir}/lib/**/*.rb"]
18
18
  s.require_paths = ["lib"]
data/spec/client_spec.rb CHANGED
@@ -7,7 +7,7 @@ describe Logglier::Client do
7
7
  context "w/o any params" do
8
8
 
9
9
  it "should raise an error" do
10
- expect { Logglier::Client.new() }.to raise_error Logglier::InputURLRequired
10
+ expect { Logglier::Client.new() }.to raise_error ArgumentError
11
11
  end
12
12
 
13
13
  end
@@ -18,7 +18,7 @@ describe Logglier::Client do
18
18
 
19
19
  it "should return an instance of the proper client" do
20
20
  log = Logglier::Client.new('http://localhost')
21
- log.should be_an_instance_of Logglier::Client::HTTP
21
+ log.should be_an_instance_of Logglier::Client::HTTP::Sync
22
22
  end
23
23
 
24
24
  end
@@ -1,12 +1,24 @@
1
1
  describe Logglier do
2
2
 
3
3
  context "HTTPS" do
4
- subject { new_logglier('https://localhost') }
5
4
 
6
- it { should be_an_instance_of Logger }
7
- its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP }
5
+ context "w/o any options" do
6
+ subject { new_logglier('https://localhost') }
8
7
 
9
- it_should_behave_like "a logglier enhanced Logger instance"
8
+ it { should be_an_instance_of Logger }
9
+ its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP::Sync }
10
+
11
+ it_should_behave_like "a logglier enhanced Logger instance"
12
+ end
13
+
14
+ context "w/threaded option" do
15
+ subject { new_logglier('https://localhost', :threaded => true) }
16
+
17
+ it { should be_an_instance_of Logger }
18
+ its('logdev.dev') { should be_an_instance_of Logglier::Client::HTTP::Threaded }
19
+
20
+ it_should_behave_like "a logglier enhanced Logger instance"
21
+ end
10
22
 
11
23
  end
12
24
 
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
2
 
3
- require 'lib/logglier'
3
+ require 'logglier'
4
4
 
5
5
  module LoggerHacks
6
6
  def logdev
@@ -21,8 +21,8 @@ RSpec.configure do |config|
21
21
  def send(*args); end
22
22
  end
23
23
 
24
- def new_logglier(url)
25
- log = Logglier.new(url)
24
+ def new_logglier(url,opts={})
25
+ log = Logglier.new(url,opts)
26
26
  log.extend(LoggerHacks)
27
27
  end
28
28
 
@@ -39,10 +39,14 @@ shared_examples_for "a logglier enhanced Logger instance" do
39
39
 
40
40
  context "with a hash" do
41
41
  it "should send a message via the logdev" do
42
- subject.logdev.dev.should_receive(:write).with(/severity=WARN, foo=bar, man=pants/)
42
+ subject.logdev.dev.should_receive(:write).with(/severity=WARN/)
43
+ subject.logdev.dev.should_receive(:write).with(/foo=bar/)
44
+ subject.logdev.dev.should_receive(:write).with(/man=pants/)
43
45
  # The following is equiv to:
44
46
  # subject.warn :foo => :bar, :man => :pants
45
47
  subject.add(Logger::WARN) { {:foo => :bar, :man => :pants} }
48
+ subject.add(Logger::WARN) { {:foo => :bar, :man => :pants} }
49
+ subject.add(Logger::WARN) { {:foo => :bar, :man => :pants} }
46
50
  end
47
51
  end
48
52
  end
@@ -0,0 +1,19 @@
1
+ describe Logglier::Client::HTTP::DeliveryThread do
2
+
3
+ subject { described_class.new(URI.parse('http://localhost')) }
4
+
5
+ before do
6
+ subject.stub(:deliver)
7
+ end
8
+
9
+ it "should" do
10
+ subject.should_receive(:deliver).with("test")
11
+ subject.push('test')
12
+
13
+ #Signal the thread it's going to exit
14
+ subject.exit!
15
+
16
+ #Wait for it to exit
17
+ subject.join
18
+ end
19
+ end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logglier
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 0
9
- - 5
10
- version: 0.0.5
4
+ prerelease:
5
+ version: 0.1.0
11
6
  platform: ruby
12
7
  authors:
13
8
  - Edward Muller (aka freeformz)
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-02-25 00:00:00 -08:00
13
+ date: 2011-05-07 00:00:00 -07:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,9 +21,6 @@ dependencies:
26
21
  requirements:
27
22
  - - ">="
28
23
  - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
24
  version: "0"
33
25
  type: :development
34
26
  version_requirements: *id001
@@ -40,18 +32,13 @@ dependencies:
40
32
  requirements:
41
33
  - - ~>
42
34
  - !ruby/object:Gem::Version
43
- hash: 27
44
- segments:
45
- - 2
46
- - 5
47
- - 0
48
35
  version: 2.5.0
49
36
  type: :development
50
37
  version_requirements: *id002
51
38
  description: |
52
39
  Logger => Loggly
53
40
 
54
- email: http://loggly.com
41
+ email: edwardam@interlix.com
55
42
  executables: []
56
43
 
57
44
  extensions: []
@@ -64,16 +51,19 @@ files:
64
51
  - LICENSE
65
52
  - logglier.gemspec
66
53
  - Rakefile
67
- - ./lib/logglier.rb
68
- - ./lib/logglier/client.rb
69
- - ./lib/logglier/version.rb
54
+ - ./lib/logglier/client/http/sync.rb
55
+ - ./lib/logglier/client/http/threaded.rb
70
56
  - ./lib/logglier/client/http.rb
71
57
  - ./lib/logglier/client/syslog.rb
58
+ - ./lib/logglier/client.rb
59
+ - ./lib/logglier/version.rb
60
+ - ./lib/logglier.rb
72
61
  - ./spec/client_spec.rb
73
62
  - ./spec/logglier_spec.rb
74
63
  - ./spec/spec_helper.rb
64
+ - ./spec/threaded_spec.rb
75
65
  has_rdoc: true
76
- homepage: http://loggly.com
66
+ homepage: http://icanhazdowntime.org
77
67
  licenses: []
78
68
 
79
69
  post_install_message:
@@ -86,23 +76,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
76
  requirements:
87
77
  - - ">="
88
78
  - !ruby/object:Gem::Version
89
- hash: 3
90
- segments:
91
- - 0
92
79
  version: "0"
93
80
  required_rubygems_version: !ruby/object:Gem::Requirement
94
81
  none: false
95
82
  requirements:
96
83
  - - ">="
97
84
  - !ruby/object:Gem::Version
98
- hash: 3
99
- segments:
100
- - 0
101
85
  version: "0"
102
86
  requirements: []
103
87
 
104
88
  rubyforge_project: logglier
105
- rubygems_version: 1.3.7
89
+ rubygems_version: 1.6.2
106
90
  signing_key:
107
91
  specification_version: 3
108
92
  summary: Loggly 'plugin' for Logger
@@ -110,3 +94,4 @@ test_files:
110
94
  - ./spec/client_spec.rb
111
95
  - ./spec/logglier_spec.rb
112
96
  - ./spec/spec_helper.rb
97
+ - ./spec/threaded_spec.rb