cabin 0.6.1 → 0.7.1
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.
- checksums.yaml +7 -0
- data/lib/cabin/channel.rb +5 -2
- data/lib/cabin/mixins/logger.rb +1 -1
- data/lib/cabin/mixins/pipe.rb +2 -1
- data/lib/cabin/mixins/terminal.rb +11 -0
- data/lib/cabin/outputs/io.rb +4 -1
- data/lib/cabin/outputs/stdlib-logger.rb +6 -2
- data/test/test_logging.rb +19 -0
- data/test/test_pipe.rb +84 -0
- metadata +54 -72
- data/examples/pipe-spoon.rb +0 -25
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 23a9c344930f27313647006b4a9a445a8d49f852
|
4
|
+
data.tar.gz: 42ac4012db8a1f4acfd9a5a49ffd0f4800b91ac1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ee17db0377d6fcf16088b3896f81f65edf0b3cfbad2b229a2d0331ba9f6069ef2d385e7cb5ead49ec3f1d24c39ee06b60d542d1d2a0261a88f4290ea477ea66
|
7
|
+
data.tar.gz: 55f18ad6421077a8ee9b76aad0f17857156e55925ea5112b6ff948aa94b6bb83063c824c19a1c02d329efa45e3828a2bf456b9fed2fa31e4b86d835da9992370
|
data/lib/cabin/channel.rb
CHANGED
@@ -2,6 +2,7 @@ require "cabin/mixins/logger"
|
|
2
2
|
require "cabin/mixins/pipe"
|
3
3
|
require "cabin/mixins/timestamp"
|
4
4
|
require "cabin/mixins/timer"
|
5
|
+
require "cabin/mixins/terminal"
|
5
6
|
require "cabin/namespace"
|
6
7
|
require "cabin/context"
|
7
8
|
require "cabin/outputs/stdlib-logger"
|
@@ -91,6 +92,7 @@ class Cabin::Channel
|
|
91
92
|
include Cabin::Mixins::Logger
|
92
93
|
include Cabin::Mixins::Pipe
|
93
94
|
include Cabin::Mixins::Timer
|
95
|
+
include Cabin::Mixins::Terminal
|
94
96
|
|
95
97
|
# All channels come with a metrics provider.
|
96
98
|
attr_accessor :metrics
|
@@ -155,7 +157,7 @@ class Cabin::Channel
|
|
155
157
|
#
|
156
158
|
# A special key :timestamp is set at the time of this method call. The value
|
157
159
|
# is a string ISO8601 timestamp with microsecond precision.
|
158
|
-
def publish(data)
|
160
|
+
def publish(data, &block)
|
159
161
|
event = {}
|
160
162
|
self.class.filters.each do |filter|
|
161
163
|
filter.call(event)
|
@@ -170,7 +172,8 @@ class Cabin::Channel
|
|
170
172
|
|
171
173
|
@subscriber_lock.synchronize do
|
172
174
|
@subscribers.each do |id, output|
|
173
|
-
output
|
175
|
+
append = block_given? ? block.call(output, event) : true
|
176
|
+
output << event if append
|
174
177
|
end
|
175
178
|
end
|
176
179
|
end # def publish
|
data/lib/cabin/mixins/logger.rb
CHANGED
data/lib/cabin/mixins/pipe.rb
CHANGED
@@ -28,7 +28,7 @@ module Cabin::Mixins::Pipe
|
|
28
28
|
# write(1, "Fri Jan 11 22:49:42 PST 2013\n", 29) = 29 {"level":"error"}
|
29
29
|
# Fri Jan 11 22:49:42 PST 2013 {"level":"info"}
|
30
30
|
# +++ exited with 0 +++ {"level":"error"}
|
31
|
-
def pipe(io_to_method_map)
|
31
|
+
def pipe(io_to_method_map, &block)
|
32
32
|
fds = io_to_method_map.keys
|
33
33
|
|
34
34
|
while !fds.empty?
|
@@ -43,6 +43,7 @@ module Cabin::Mixins::Pipe
|
|
43
43
|
end
|
44
44
|
|
45
45
|
method_name = io_to_method_map[fd]
|
46
|
+
block.call(line, method_name) if block_given?
|
46
47
|
send(method_name, line)
|
47
48
|
end # readers.each
|
48
49
|
end # while !fds.empty?
|
data/lib/cabin/outputs/io.rb
CHANGED
@@ -48,7 +48,7 @@ class Cabin::Outputs::IO
|
|
48
48
|
# Receive an event
|
49
49
|
def <<(event)
|
50
50
|
@lock.synchronize do
|
51
|
-
if
|
51
|
+
if !tty?
|
52
52
|
@io.puts(event.inspect)
|
53
53
|
else
|
54
54
|
tty_write(event)
|
@@ -56,6 +56,9 @@ class Cabin::Outputs::IO
|
|
56
56
|
end
|
57
57
|
end # def <<
|
58
58
|
|
59
|
+
def tty?
|
60
|
+
@io.tty?
|
61
|
+
end
|
59
62
|
private
|
60
63
|
def tty_write(event)
|
61
64
|
# The io is attached to a tty, so make pretty colors.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require "cabin"
|
2
2
|
|
3
3
|
# Wrap Ruby stdlib's logger. This allows you to output to a normal ruby logger
|
4
|
-
# with Cabin. Since Ruby's Logger has a love for strings alone, this
|
4
|
+
# with Cabin. Since Ruby's Logger has a love for strings alone, this
|
5
5
|
# wrapper will convert the data/event to ruby inspect format before sending it
|
6
6
|
# to Logger.
|
7
7
|
class Cabin::Outputs::StdlibLogger
|
@@ -17,7 +17,11 @@ class Cabin::Outputs::StdlibLogger
|
|
17
17
|
if !event.include?(:level)
|
18
18
|
event[:level] = :info
|
19
19
|
end
|
20
|
-
|
20
|
+
if event[:level].is_a?(Symbol)
|
21
|
+
method = event[:level]
|
22
|
+
else
|
23
|
+
method = event[:level].downcase.to_sym || :info
|
24
|
+
end
|
21
25
|
event.delete(:level)
|
22
26
|
|
23
27
|
data = event.clone
|
data/test/test_logging.rb
CHANGED
@@ -27,6 +27,12 @@ describe Cabin::Channel do
|
|
27
27
|
end
|
28
28
|
end # class Receiver
|
29
29
|
|
30
|
+
class TtyReceiver < Receiver
|
31
|
+
def tty?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
30
36
|
before do
|
31
37
|
@logger = Cabin::Channel.new
|
32
38
|
@target = Receiver.new
|
@@ -105,6 +111,19 @@ describe Cabin::Channel do
|
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
114
|
+
test "standard use case, 'terminal' logging when there is no tty" do
|
115
|
+
@logger.terminal('Hello world')
|
116
|
+
assert_equal(true, @target.data.empty?)
|
117
|
+
end
|
118
|
+
|
119
|
+
test "standard use case, 'terminal' logging when there is a tty" do
|
120
|
+
tty_target = TtyReceiver.new
|
121
|
+
id = @logger.subscribe(tty_target)
|
122
|
+
@logger.terminal('Hello world')
|
123
|
+
assert_equal('Hello world', tty_target.data[0][:message])
|
124
|
+
@logger.unsubscribe(id)
|
125
|
+
end
|
126
|
+
|
108
127
|
test "extra debugging data" do
|
109
128
|
@logger.level = :debug
|
110
129
|
@logger.info("Hello world")
|
data/test/test_pipe.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'minitest-patch'
|
6
|
+
require 'cabin'
|
7
|
+
require 'stringio'
|
8
|
+
require 'minitest/autorun' if __FILE__ == $0
|
9
|
+
|
10
|
+
describe Cabin::Channel do
|
11
|
+
class Receiver
|
12
|
+
attr_accessor :data
|
13
|
+
|
14
|
+
public
|
15
|
+
def initialize
|
16
|
+
@data = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def <<(data)
|
20
|
+
@data << data
|
21
|
+
end
|
22
|
+
end # class Receiver
|
23
|
+
|
24
|
+
before do
|
25
|
+
@logger = Cabin::Channel.new
|
26
|
+
@target = Receiver.new
|
27
|
+
@logger.subscribe(@target)
|
28
|
+
|
29
|
+
@info_reader, @info_writer = IO.pipe
|
30
|
+
@error_reader, @error_writer = IO.pipe
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
@logger.unsubscribe(@target.object_id)
|
35
|
+
[ @info_reader, @info_writer,
|
36
|
+
@error_reader, @error_writer ].each do |io|
|
37
|
+
io.close unless io.closed?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'Piping one IO' do
|
42
|
+
@info_writer.puts 'Hello world'
|
43
|
+
@info_writer.close
|
44
|
+
|
45
|
+
@logger.pipe(@info_reader => :info)
|
46
|
+
assert_equal(1, @target.data.length)
|
47
|
+
assert_equal('Hello world', @target.data[0][:message])
|
48
|
+
end
|
49
|
+
|
50
|
+
test 'Piping multiple IOs' do
|
51
|
+
@info_writer.puts 'Hello world'
|
52
|
+
@info_writer.close
|
53
|
+
|
54
|
+
@error_writer.puts 'Goodbye world'
|
55
|
+
@error_writer.close
|
56
|
+
|
57
|
+
@logger.pipe(@info_reader => :info, @error_reader => :error)
|
58
|
+
assert_equal(2, @target.data.length)
|
59
|
+
assert_equal('Hello world', @target.data[0][:message])
|
60
|
+
assert_equal(:info, @target.data[0][:level])
|
61
|
+
assert_equal('Goodbye world', @target.data[1][:message])
|
62
|
+
assert_equal(:error, @target.data[1][:level])
|
63
|
+
end
|
64
|
+
|
65
|
+
test 'Piping with a block' do
|
66
|
+
@info_writer.puts 'Hello world'
|
67
|
+
@info_writer.close
|
68
|
+
|
69
|
+
@error_writer.puts 'Goodbye world'
|
70
|
+
@error_writer.close
|
71
|
+
|
72
|
+
info = StringIO.new
|
73
|
+
error = StringIO.new
|
74
|
+
|
75
|
+
@logger.pipe(@info_reader => :info, @error_reader => :error) do |message, level|
|
76
|
+
info << message if level == :info
|
77
|
+
error << message if level == :error
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_equal('Hello world', info.string)
|
81
|
+
assert_equal('Goodbye world', error.string)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
metadata
CHANGED
@@ -1,106 +1,88 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cabin
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 1
|
10
|
-
version: 0.6.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.1
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Jordan Sissel
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2013-06-18 00:00:00 Z
|
11
|
+
date: 2015-01-26 00:00:00.000000000 Z
|
19
12
|
dependencies: []
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
description: This is an experiment to try and make logging more flexible and more
|
14
|
+
consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
|
15
|
+
Metrics, too!
|
16
|
+
email:
|
23
17
|
- jls@semicomplete.com
|
24
|
-
executables:
|
18
|
+
executables:
|
25
19
|
- rubygems-cabin-test
|
26
20
|
extensions: []
|
27
|
-
|
28
21
|
extra_rdoc_files: []
|
29
|
-
|
30
|
-
|
31
|
-
-
|
32
|
-
-
|
33
|
-
-
|
34
|
-
-
|
35
|
-
-
|
36
|
-
-
|
37
|
-
-
|
38
|
-
- lib/cabin
|
39
|
-
- lib/cabin/
|
40
|
-
- lib/cabin/metric.rb
|
22
|
+
files:
|
23
|
+
- CHANGELIST
|
24
|
+
- LICENSE
|
25
|
+
- bin/rubygems-cabin-test
|
26
|
+
- examples/fibonacci-timing.rb
|
27
|
+
- examples/metrics.rb
|
28
|
+
- examples/pipe.rb
|
29
|
+
- examples/sample.rb
|
30
|
+
- examples/sinatra-logging.rb
|
31
|
+
- lib/cabin.rb
|
32
|
+
- lib/cabin/channel.rb
|
41
33
|
- lib/cabin/context.rb
|
42
|
-
- lib/cabin/timer.rb
|
43
34
|
- lib/cabin/inspectable.rb
|
44
|
-
- lib/cabin/
|
45
|
-
- lib/cabin/
|
46
|
-
- lib/cabin/
|
35
|
+
- lib/cabin/metric.rb
|
36
|
+
- lib/cabin/metrics.rb
|
37
|
+
- lib/cabin/metrics/counter.rb
|
38
|
+
- lib/cabin/metrics/gauge.rb
|
39
|
+
- lib/cabin/metrics/histogram.rb
|
40
|
+
- lib/cabin/metrics/meter.rb
|
41
|
+
- lib/cabin/metrics/timer.rb
|
42
|
+
- lib/cabin/mixins/CAPSLOCK.rb
|
47
43
|
- lib/cabin/mixins/colors.rb
|
48
44
|
- lib/cabin/mixins/dragons.rb
|
49
|
-
- lib/cabin/mixins/
|
50
|
-
- lib/cabin/mixins/CAPSLOCK.rb
|
45
|
+
- lib/cabin/mixins/logger.rb
|
51
46
|
- lib/cabin/mixins/pipe.rb
|
47
|
+
- lib/cabin/mixins/terminal.rb
|
48
|
+
- lib/cabin/mixins/timer.rb
|
49
|
+
- lib/cabin/mixins/timestamp.rb
|
52
50
|
- lib/cabin/namespace.rb
|
53
|
-
- lib/cabin/
|
54
|
-
- lib/cabin/
|
55
|
-
- lib/cabin.rb
|
56
|
-
-
|
57
|
-
-
|
58
|
-
-
|
59
|
-
- examples/pipe-spoon.rb
|
60
|
-
- examples/metrics.rb
|
61
|
-
- examples/pipe.rb
|
51
|
+
- lib/cabin/outputs/em/stdlib-logger.rb
|
52
|
+
- lib/cabin/outputs/io.rb
|
53
|
+
- lib/cabin/outputs/stdlib-logger.rb
|
54
|
+
- lib/cabin/outputs/zeromq.rb
|
55
|
+
- lib/cabin/publisher.rb
|
56
|
+
- lib/cabin/timer.rb
|
62
57
|
- test/all.rb
|
63
58
|
- test/minitest-patch.rb
|
59
|
+
- test/test_logging.rb
|
64
60
|
- test/test_metrics.rb
|
61
|
+
- test/test_pipe.rb
|
65
62
|
- test/test_zeromq.rb
|
66
|
-
- test/test_logging.rb
|
67
|
-
- LICENSE
|
68
|
-
- CHANGELIST
|
69
|
-
- bin/rubygems-cabin-test
|
70
63
|
homepage: https://github.com/jordansissel/ruby-cabin
|
71
|
-
licenses:
|
64
|
+
licenses:
|
72
65
|
- Apache License (2.0)
|
66
|
+
metadata: {}
|
73
67
|
post_install_message:
|
74
68
|
rdoc_options: []
|
75
|
-
|
76
|
-
require_paths:
|
69
|
+
require_paths:
|
77
70
|
- lib
|
78
71
|
- lib
|
79
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
-
|
81
|
-
requirements:
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
82
74
|
- - ">="
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
version: "0"
|
88
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
91
79
|
- - ">="
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
|
94
|
-
segments:
|
95
|
-
- 0
|
96
|
-
version: "0"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
97
82
|
requirements: []
|
98
|
-
|
99
83
|
rubyforge_project:
|
100
|
-
rubygems_version:
|
84
|
+
rubygems_version: 2.2.2
|
101
85
|
signing_key:
|
102
|
-
specification_version:
|
86
|
+
specification_version: 4
|
103
87
|
summary: Experiments in structured and contextual logging
|
104
88
|
test_files: []
|
105
|
-
|
106
|
-
has_rdoc:
|
data/examples/pipe-spoon.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require "cabin"
|
2
|
-
require "spoon"
|
3
|
-
|
4
|
-
|
5
|
-
logger = Cabin::Channel.get
|
6
|
-
logger.subscribe(STDOUT)
|
7
|
-
logger.level = :info
|
8
|
-
|
9
|
-
fileactions = Spoon::FileActions.new
|
10
|
-
fileactions.close(0)
|
11
|
-
cor, cow = IO.pipe # child stdout
|
12
|
-
cer, cew = IO.pipe # child stderr
|
13
|
-
fileactions.dup2(cow.fileno, 1)
|
14
|
-
fileactions.dup2(cew.fileno, 2)
|
15
|
-
|
16
|
-
spawn_attr = Spoon::SpawnAttributes.new
|
17
|
-
pid = Spoon.posix_spawn("/bin/sh", fileactions, spawn_attr, ["sh", "-c"] + ARGV)
|
18
|
-
puts pid
|
19
|
-
cow.close
|
20
|
-
cew.close
|
21
|
-
|
22
|
-
logger.pipe(cor => :info, cer => :error)
|
23
|
-
pid, status = Process.waitpid2(pid)
|
24
|
-
|
25
|
-
puts status.inspect
|