cabin 0.4.4 → 0.5.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/bin/rubygems-cabin-test +0 -0
- data/lib/cabin/metrics.rb +7 -2
- data/lib/cabin/outputs/em/stdlib-logger.rb +2 -5
- data/lib/cabin/outputs/io.rb +5 -6
- data/lib/cabin/outputs/stdlib-logger.rb +3 -3
- data/lib/cabin/outputs/zeromq.rb +96 -0
- data/test/all.rb +4 -0
- data/test/test_zeromq.rb +99 -0
- metadata +82 -64
data/bin/rubygems-cabin-test
CHANGED
File without changes
|
data/lib/cabin/metrics.rb
CHANGED
@@ -51,6 +51,7 @@ class Cabin::Metrics
|
|
51
51
|
# Get us a new metrics container.
|
52
52
|
public
|
53
53
|
def initialize
|
54
|
+
@metrics_lock = Mutex.new
|
54
55
|
@metrics = {}
|
55
56
|
end # def initialize
|
56
57
|
|
@@ -76,7 +77,9 @@ class Cabin::Metrics
|
|
76
77
|
if @channel
|
77
78
|
@channel.debug("Created metric", :instance => instance, :type => metric_object.class)
|
78
79
|
end
|
79
|
-
return @
|
80
|
+
return @metrics_lock.synchronize do
|
81
|
+
@metrics[metric_name] = metric_object
|
82
|
+
end
|
80
83
|
end # def create
|
81
84
|
|
82
85
|
# Create a new Counter metric
|
@@ -114,6 +117,8 @@ class Cabin::Metrics
|
|
114
117
|
# iterate over each metric. yields identifer, metric
|
115
118
|
def each(&block)
|
116
119
|
# delegate to the @metrics hash until we need something fancier
|
117
|
-
@
|
120
|
+
@metrics_lock.synchronize do
|
121
|
+
@metrics.each(&block)
|
122
|
+
end
|
118
123
|
end # def each
|
119
124
|
end # class Cabin::Metrics
|
@@ -1,11 +1,8 @@
|
|
1
1
|
require "cabin"
|
2
|
-
require "json"
|
3
2
|
require "eventmachine"
|
4
3
|
|
5
4
|
# Wrap Ruby stdlib's logger and make it EventMachine friendly. This
|
6
|
-
# allows you to output to a normal ruby logger with Cabin.
|
7
|
-
# Ruby's Logger has a love for strings alone, this wrapper will
|
8
|
-
# convert the data/event to json before sending it to Logger.
|
5
|
+
# allows you to output to a normal ruby logger with Cabin.
|
9
6
|
class Cabin::Outputs::EM::StdlibLogger
|
10
7
|
public
|
11
8
|
def initialize(logger)
|
@@ -33,7 +30,7 @@ class Cabin::Outputs::EM::StdlibLogger
|
|
33
30
|
def <<(data)
|
34
31
|
line = Hash.new
|
35
32
|
line[:method] = data[:level] || "info"
|
36
|
-
line[:message] = "#{data[:message]} #{data.
|
33
|
+
line[:message] = "#{data[:message]} #{data.inspect}"
|
37
34
|
if EM::reactor_running?
|
38
35
|
# Push line onto queue for later sending
|
39
36
|
@logger_queue.push(line)
|
data/lib/cabin/outputs/io.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
require "cabin"
|
2
|
-
require "json"
|
3
2
|
require "thread"
|
4
3
|
|
5
4
|
# Wrap IO objects with a reasonable log output.
|
6
5
|
#
|
7
6
|
# If the IO is *not* attached to a tty (io#tty? returns false), then
|
8
|
-
# the event will be written in
|
7
|
+
# the event will be written in ruby inspect format terminated by a newline:
|
9
8
|
#
|
10
|
-
# { "timestamp"
|
9
|
+
# { "timestamp" => ..., "message" => message, ... }
|
11
10
|
#
|
12
11
|
# If the IO is attached to a TTY, there are # human-friendly in this format:
|
13
12
|
#
|
14
|
-
# message {
|
13
|
+
# message { event data }
|
15
14
|
#
|
16
15
|
# Additionally, colorized output may be available. If the event has :level,
|
17
16
|
# :color, or :bold. Any of the Cabin::Mixins::Logger methods (info, error, etc)
|
@@ -50,7 +49,7 @@ class Cabin::Outputs::IO
|
|
50
49
|
def <<(event)
|
51
50
|
@lock.synchronize do
|
52
51
|
if !@io.tty?
|
53
|
-
@io.puts(event.
|
52
|
+
@io.puts(event.inspect)
|
54
53
|
else
|
55
54
|
tty_write(event)
|
56
55
|
end
|
@@ -77,7 +76,7 @@ class Cabin::Outputs::IO
|
|
77
76
|
if data.empty?
|
78
77
|
message = [event[:message]]
|
79
78
|
else
|
80
|
-
message = ["#{event[:message]} #{data.
|
79
|
+
message = ["#{event[:message]} #{data.inspect}"]
|
81
80
|
end
|
82
81
|
message.unshift("\e[#{CODEMAP[color.to_sym]}m") if !color.nil?
|
83
82
|
message.unshift("\e[#{CODEMAP[bold]}m") if !bold.nil?
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require "cabin"
|
2
|
-
require "json"
|
3
2
|
|
4
3
|
# Wrap Ruby stdlib's logger. This allows you to output to a normal ruby logger
|
5
4
|
# with Cabin. Since Ruby's Logger has a love for strings alone, this
|
6
|
-
# wrapper will convert the data/event to
|
5
|
+
# wrapper will convert the data/event to ruby inspect format before sending it
|
6
|
+
# to Logger.
|
7
7
|
class Cabin::Outputs::StdlibLogger
|
8
8
|
public
|
9
9
|
def initialize(logger)
|
@@ -24,7 +24,7 @@ class Cabin::Outputs::StdlibLogger
|
|
24
24
|
# delete things from the 'data' portion that's not really data.
|
25
25
|
data.delete(:message)
|
26
26
|
data.delete(:timestamp)
|
27
|
-
message = "#{event[:message]} #{data.
|
27
|
+
message = "#{event[:message]} #{data.inspect}"
|
28
28
|
|
29
29
|
#p [@logger.level, logger.class::DEBUG]
|
30
30
|
# This will call @logger.info(data) or something similar.
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'cabin'
|
2
|
+
require 'ffi-rzmq'
|
3
|
+
|
4
|
+
# Output to a zeromq socket.
|
5
|
+
class Cabin::Outputs::ZeroMQ
|
6
|
+
DEFAULTS = {
|
7
|
+
:topology => "pushpull",
|
8
|
+
:hwm => 0, # zeromq default: no limit
|
9
|
+
:linger => -1, # zeromq default: wait until all messages are sent.
|
10
|
+
:topic => ""
|
11
|
+
}
|
12
|
+
|
13
|
+
CONTEXT = ZMQ::Context.new
|
14
|
+
|
15
|
+
attr_reader :socket, :topology, :topic
|
16
|
+
|
17
|
+
# Create a new ZeroMQ output.
|
18
|
+
#
|
19
|
+
# arguments:
|
20
|
+
# addresses A list of addresses to connect to. These are round-robined by zeromq.
|
21
|
+
#
|
22
|
+
# :topology Either 'pushpull' or 'pubsub'. Specifies which zeromq socket type to use. Default pushpull.
|
23
|
+
# :hwm Specifies the High Water Mark for the socket. Default 0, which means there is none.
|
24
|
+
# :linger Specifies the linger time in milliseconds for the socket. Default -1, meaning wait forever for the socket to close.
|
25
|
+
# :topic Specifies the topic for a pubsub topology. This can be a string or a proc with the event as the only argument.
|
26
|
+
def initialize(addresses, options={})
|
27
|
+
options = DEFAULTS.merge(options)
|
28
|
+
|
29
|
+
@topology = options[:topology].to_s
|
30
|
+
case @topology
|
31
|
+
when "pushpull"
|
32
|
+
socket_type = ZMQ::PUSH
|
33
|
+
when "pubsub"
|
34
|
+
socket_type = ZMQ::PUB
|
35
|
+
end
|
36
|
+
|
37
|
+
@topic = options[:topic]
|
38
|
+
@socket = CONTEXT.socket(socket_type)
|
39
|
+
|
40
|
+
Array(addresses).each do |address|
|
41
|
+
error_check @socket.connect(address), "connecting to #{address}"
|
42
|
+
end
|
43
|
+
|
44
|
+
error_check @socket.setsockopt(ZMQ::LINGER, options[:linger]), "while setting ZMQ::LINGER to #{options[:linger]}"
|
45
|
+
error_check @socket.setsockopt(ZMQ::HWM, options[:hwm]), "while setting ZMQ::HWM to #{options[:hwm]}"
|
46
|
+
|
47
|
+
#TODO use cabin's teardown when it exists
|
48
|
+
at_exit do
|
49
|
+
teardown
|
50
|
+
end
|
51
|
+
|
52
|
+
#define_finalizer
|
53
|
+
end
|
54
|
+
|
55
|
+
def linger
|
56
|
+
array = []
|
57
|
+
error_check @socket.getsockopt(ZMQ::LINGER, array), "while getting ZMQ::LINGER"
|
58
|
+
array.first
|
59
|
+
end
|
60
|
+
|
61
|
+
def hwm
|
62
|
+
array = []
|
63
|
+
error_check @socket.getsockopt(ZMQ::HWM, array), "while getting ZMQ::HWM"
|
64
|
+
array.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def <<(event)
|
68
|
+
if @socket.name == "PUB"
|
69
|
+
topic = @topic.is_a?(Proc) ? @topic.call(event) : @topic
|
70
|
+
error_check @socket.send_string(topic, ZMQ::SNDMORE), "in topic send_string"
|
71
|
+
end
|
72
|
+
error_check @socket.send_string(event.inspect), "in send_string"
|
73
|
+
end
|
74
|
+
|
75
|
+
def teardown
|
76
|
+
@socket.close if @socket
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def error_check(rc, doing)
|
81
|
+
unless ZMQ::Util.resultcode_ok?(rc)
|
82
|
+
raise "ZeroMQ Error while #{doing}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# This causes the following message on exit:
|
87
|
+
# File exists (epoll.cpp:69)
|
88
|
+
# [1] 26175 abort bundle exec irb
|
89
|
+
# def define_finalizer
|
90
|
+
# ObjectSpace.define_finalizer(self, self.class.finalize(@socket))
|
91
|
+
# end
|
92
|
+
|
93
|
+
# def self.finalize(socket)
|
94
|
+
# Proc.new { puts "finalizing"; socket.close unless socket.nil?; puts "done" }
|
95
|
+
# end
|
96
|
+
end
|
data/test/all.rb
CHANGED
@@ -9,5 +9,9 @@ SimpleCov.start
|
|
9
9
|
dir = File.dirname(File.expand_path(__FILE__))
|
10
10
|
Dir.glob(File.join(dir, "**", "test_*.rb")).each do |path|
|
11
11
|
puts "Loading tests from #{path}"
|
12
|
+
if path =~ /test_zeromq/
|
13
|
+
puts "Skipping zeromq tests because they force ruby to exit if libzmq is not found"
|
14
|
+
next
|
15
|
+
end
|
12
16
|
require path
|
13
17
|
end
|
data/test/test_zeromq.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "minitest-patch"
|
6
|
+
require "cabin/outputs/zeromq"
|
7
|
+
require "minitest/autorun" if __FILE__ == $0
|
8
|
+
|
9
|
+
describe Cabin::Outputs::ZeroMQ do
|
10
|
+
|
11
|
+
def error_check(rc, doing)
|
12
|
+
unless ZMQ::Util.resultcode_ok?(rc)
|
13
|
+
raise "ZeroMQ Error while #{doing}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
NonBlockingFlag = (ZMQ::LibZMQ.version2? ? ZMQ::NOBLOCK : ZMQ::DONTWAIT) unless defined?(NonBlockingFlag)
|
18
|
+
def receive(socket)
|
19
|
+
received = ""
|
20
|
+
error_check socket.recv_string(received, NonBlockingFlag), "receiving"
|
21
|
+
received
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
@logger = Cabin::Channel.new
|
26
|
+
@address = "inproc://zeromq-output"
|
27
|
+
@pull = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::PULL)
|
28
|
+
@sub = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::SUB)
|
29
|
+
end
|
30
|
+
|
31
|
+
after do
|
32
|
+
@pull.close
|
33
|
+
@sub.close
|
34
|
+
@output.teardown if @output
|
35
|
+
end
|
36
|
+
|
37
|
+
test 'push messages' do
|
38
|
+
@pull.bind(@address); sleep 0.1 # sleeps are necessary for inproc transport
|
39
|
+
@output = Cabin::Outputs::ZeroMQ.new(@address)
|
40
|
+
@logger.subscribe(@output)
|
41
|
+
@logger.info("hello")
|
42
|
+
@logger.info("hello2")
|
43
|
+
assert_equal "hello", JSON.parse(receive(@pull))['message']
|
44
|
+
assert_equal "hello2", JSON.parse(receive(@pull))['message']
|
45
|
+
end
|
46
|
+
|
47
|
+
test "pub messages" do
|
48
|
+
@sub.bind(@address); sleep 0.1
|
49
|
+
error_check @sub.setsockopt(ZMQ::SUBSCRIBE, ""), "subscribing"
|
50
|
+
@output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub")
|
51
|
+
@logger.subscribe(@output)
|
52
|
+
@logger.info("hi")
|
53
|
+
assert_equal "", receive(@sub)
|
54
|
+
assert_equal "hi", JSON.parse(receive(@sub))['message']
|
55
|
+
end
|
56
|
+
|
57
|
+
test "pub messages on a topic" do
|
58
|
+
@sub.bind(@address); sleep 0.1
|
59
|
+
error_check @sub.setsockopt(ZMQ::SUBSCRIBE, "topic"), "subscribing"
|
60
|
+
@output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub", :topic => "topic")
|
61
|
+
@logger.subscribe(@output)
|
62
|
+
@logger.info("hi")
|
63
|
+
assert_equal "topic", receive(@sub)
|
64
|
+
assert_equal "hi", JSON.parse(receive(@sub))['message']
|
65
|
+
end
|
66
|
+
|
67
|
+
test "topic proc" do
|
68
|
+
@sub.bind(@address); sleep 0.1
|
69
|
+
error_check @sub.setsockopt(ZMQ::SUBSCRIBE, "topic2"), "subscribing"
|
70
|
+
@output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub", :topic => Proc.new { |event| event[:message] })
|
71
|
+
@logger.subscribe(@output)
|
72
|
+
@logger.info("topic1")
|
73
|
+
@logger.info("topic2")
|
74
|
+
assert_equal "topic2", receive(@sub)
|
75
|
+
assert_equal "topic2", JSON.parse(receive(@sub))['message']
|
76
|
+
end
|
77
|
+
|
78
|
+
test "multiple addresses" do
|
79
|
+
@pull.bind(@address); sleep 0.1
|
80
|
+
@pull2 = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::PULL)
|
81
|
+
@pull2.bind(@address.succ); sleep 0.1
|
82
|
+
|
83
|
+
@output = Cabin::Outputs::ZeroMQ.new([@address, @address.succ])
|
84
|
+
@logger.subscribe(@output)
|
85
|
+
@logger.info("yo")
|
86
|
+
@logger.info("yo")
|
87
|
+
|
88
|
+
assert_equal "yo", JSON.parse(receive(@pull))['message']
|
89
|
+
assert_equal "yo", JSON.parse(receive(@pull2))['message']
|
90
|
+
end
|
91
|
+
|
92
|
+
test "options" do
|
93
|
+
@pull.bind(@address); sleep 0.1
|
94
|
+
@output = Cabin::Outputs::ZeroMQ.new(@address, :hwm => 10, :linger => 100)
|
95
|
+
|
96
|
+
assert_equal 10, @output.hwm
|
97
|
+
assert_equal 100, @output.linger
|
98
|
+
end
|
99
|
+
end
|
metadata
CHANGED
@@ -1,35 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cabin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jordan Sissel
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
name: json
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
description: This is an experiment to try and make logging more flexible and more
|
31
|
-
consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
|
32
|
-
Metrics, too!
|
12
|
+
date: 2012-11-09 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: This is an experiment to try and make logging more flexible and more consumable. Plain text logs are bullshit, let's emit structured and contextual logs. Metrics, too!
|
33
15
|
email:
|
34
16
|
- jls@semicomplete.com
|
35
17
|
executables:
|
@@ -37,65 +19,101 @@ executables:
|
|
37
19
|
extensions: []
|
38
20
|
extra_rdoc_files: []
|
39
21
|
files:
|
40
|
-
-
|
41
|
-
|
42
|
-
-
|
43
|
-
|
44
|
-
-
|
45
|
-
|
46
|
-
-
|
47
|
-
|
48
|
-
-
|
49
|
-
|
50
|
-
-
|
51
|
-
|
52
|
-
-
|
53
|
-
|
54
|
-
-
|
55
|
-
|
56
|
-
-
|
57
|
-
|
58
|
-
-
|
59
|
-
|
60
|
-
-
|
61
|
-
|
62
|
-
-
|
63
|
-
|
64
|
-
-
|
65
|
-
|
66
|
-
-
|
67
|
-
|
68
|
-
-
|
69
|
-
|
70
|
-
-
|
22
|
+
- !binary |-
|
23
|
+
bGliL2NhYmluLnJi
|
24
|
+
- !binary |-
|
25
|
+
bGliL2NhYmluL2NoYW5uZWwucmI=
|
26
|
+
- !binary |-
|
27
|
+
bGliL2NhYmluL2NvbnRleHQucmI=
|
28
|
+
- !binary |-
|
29
|
+
bGliL2NhYmluL2luc3BlY3RhYmxlLnJi
|
30
|
+
- !binary |-
|
31
|
+
bGliL2NhYmluL21ldHJpYy5yYg==
|
32
|
+
- !binary |-
|
33
|
+
bGliL2NhYmluL21ldHJpY3MucmI=
|
34
|
+
- !binary |-
|
35
|
+
bGliL2NhYmluL25hbWVzcGFjZS5yYg==
|
36
|
+
- !binary |-
|
37
|
+
bGliL2NhYmluL3B1Ymxpc2hlci5yYg==
|
38
|
+
- !binary |-
|
39
|
+
bGliL2NhYmluL3RpbWVyLnJi
|
40
|
+
- !binary |-
|
41
|
+
bGliL2NhYmluL21ldHJpY3MvY291bnRlci5yYg==
|
42
|
+
- !binary |-
|
43
|
+
bGliL2NhYmluL21ldHJpY3MvZ2F1Z2UucmI=
|
44
|
+
- !binary |-
|
45
|
+
bGliL2NhYmluL21ldHJpY3MvaGlzdG9ncmFtLnJi
|
46
|
+
- !binary |-
|
47
|
+
bGliL2NhYmluL21ldHJpY3MvbWV0ZXIucmI=
|
48
|
+
- !binary |-
|
49
|
+
bGliL2NhYmluL21ldHJpY3MvdGltZXIucmI=
|
50
|
+
- !binary |-
|
51
|
+
bGliL2NhYmluL21peGlucy9DQVBTTE9DSy5yYg==
|
52
|
+
- !binary |-
|
53
|
+
bGliL2NhYmluL21peGlucy9jb2xvcnMucmI=
|
54
|
+
- !binary |-
|
55
|
+
bGliL2NhYmluL21peGlucy9kcmFnb25zLnJi
|
56
|
+
- !binary |-
|
57
|
+
bGliL2NhYmluL21peGlucy9sb2dnZXIucmI=
|
58
|
+
- !binary |-
|
59
|
+
bGliL2NhYmluL21peGlucy90aW1lci5yYg==
|
60
|
+
- !binary |-
|
61
|
+
bGliL2NhYmluL21peGlucy90aW1lc3RhbXAucmI=
|
62
|
+
- !binary |-
|
63
|
+
bGliL2NhYmluL291dHB1dHMvaW8ucmI=
|
64
|
+
- !binary |-
|
65
|
+
bGliL2NhYmluL291dHB1dHMvc3RkbGliLWxvZ2dlci5yYg==
|
66
|
+
- !binary |-
|
67
|
+
bGliL2NhYmluL291dHB1dHMvemVyb21xLnJi
|
68
|
+
- !binary |-
|
69
|
+
bGliL2NhYmluL291dHB1dHMvZW0vc3RkbGliLWxvZ2dlci5yYg==
|
70
|
+
- !binary |-
|
71
|
+
ZXhhbXBsZXMvZmlib25hY2NpLXRpbWluZy5yYg==
|
72
|
+
- !binary |-
|
73
|
+
ZXhhbXBsZXMvbWV0cmljcy5yYg==
|
74
|
+
- !binary |-
|
75
|
+
ZXhhbXBsZXMvc2FtcGxlLnJi
|
76
|
+
- !binary |-
|
77
|
+
ZXhhbXBsZXMvc2luYXRyYS1sb2dnaW5nLnJi
|
78
|
+
- !binary |-
|
79
|
+
dGVzdC9hbGwucmI=
|
80
|
+
- !binary |-
|
81
|
+
dGVzdC9taW5pdGVzdC1wYXRjaC5yYg==
|
82
|
+
- !binary |-
|
83
|
+
dGVzdC90ZXN0X2xvZ2dpbmcucmI=
|
84
|
+
- !binary |-
|
85
|
+
dGVzdC90ZXN0X21ldHJpY3MucmI=
|
86
|
+
- !binary |-
|
87
|
+
dGVzdC90ZXN0X3plcm9tcS5yYg==
|
71
88
|
- LICENSE
|
72
89
|
- CHANGELIST
|
73
90
|
- bin/rubygems-cabin-test
|
74
91
|
homepage: https://github.com/jordansissel/ruby-cabin
|
75
92
|
licenses:
|
76
93
|
- Apache License (2.0)
|
77
|
-
post_install_message:
|
94
|
+
post_install_message:
|
78
95
|
rdoc_options: []
|
79
96
|
require_paths:
|
80
97
|
- lib
|
81
98
|
- lib
|
82
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
100
|
requirements:
|
85
101
|
- - ! '>='
|
86
102
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
88
|
-
|
103
|
+
version: !binary |-
|
104
|
+
MA==
|
89
105
|
none: false
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
107
|
requirements:
|
91
108
|
- - ! '>='
|
92
109
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
110
|
+
version: !binary |-
|
111
|
+
MA==
|
112
|
+
none: false
|
94
113
|
requirements: []
|
95
|
-
rubyforge_project:
|
96
|
-
rubygems_version: 1.8.
|
97
|
-
signing_key:
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
98
117
|
specification_version: 3
|
99
118
|
summary: Experiments in structured and contextual logging
|
100
119
|
test_files: []
|
101
|
-
has_rdoc:
|