logglier 0.0.5 → 0.1.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/Gemfile +3 -1
- data/README.md +21 -6
- data/lib/logglier/client/http/sync.rb +42 -0
- data/lib/logglier/client/http/threaded.rb +98 -0
- data/lib/logglier/client/http.rb +9 -37
- data/lib/logglier/client.rb +21 -5
- data/lib/logglier/version.rb +1 -1
- data/lib/logglier.rb +2 -2
- data/logglier.gemspec +3 -3
- data/spec/client_spec.rb +2 -2
- data/spec/logglier_spec.rb +16 -4
- data/spec/spec_helper.rb +8 -4
- data/spec/threaded_spec.rb +19 -0
- metadata +13 -28
data/Gemfile
CHANGED
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
|
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
|
-
|
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(
|
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
|
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
|
-
*
|
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
|
data/lib/logglier/client/http.rb
CHANGED
@@ -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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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')
|
data/lib/logglier/client.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
module Logglier
|
2
2
|
module Client
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
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
|
85
|
+
raise URLRequired.new("Invalid Input URL: #{@input_uri}")
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
data/lib/logglier/version.rb
CHANGED
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 =
|
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://
|
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
|
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
|
data/spec/logglier_spec.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
5
|
+
context "w/o any options" do
|
6
|
+
subject { new_logglier('https://localhost') }
|
8
7
|
|
9
|
-
|
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 '
|
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
|
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
|
-
|
5
|
-
|
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-
|
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:
|
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://
|
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.
|
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
|