cabin 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cabin/channel.rb +43 -16
- data/lib/cabin/mixins/logger.rb +1 -1
- data/lib/cabin/outputs/io.rb +8 -4
- data/test/test_logging.rb +2 -3
- metadata +10 -5
data/lib/cabin/channel.rb
CHANGED
@@ -6,7 +6,8 @@ require "cabin/context"
|
|
6
6
|
require "cabin/outputs/stdlib-logger"
|
7
7
|
require "cabin/outputs/io"
|
8
8
|
require "cabin/metrics"
|
9
|
-
require "logger"
|
9
|
+
require "logger" # stdlib
|
10
|
+
require "thread"
|
10
11
|
|
11
12
|
# A wonderful channel for logging.
|
12
13
|
#
|
@@ -46,6 +47,9 @@ require "logger"
|
|
46
47
|
# I, [2011-10-11T01:00:57.993575 #1209] INFO -- : {:timestamp=>"2011-10-11T01:00:57.993517-0700", :message=>"Done in foo", :level=>:info}
|
47
48
|
#
|
48
49
|
class Cabin::Channel
|
50
|
+
@channel_lock = Mutex.new
|
51
|
+
@channels = Hash.new { |h,k| h[k] = Cabin::Channel.new }
|
52
|
+
|
49
53
|
class << self
|
50
54
|
# Get a channel for a given identifier. If this identifier has never been
|
51
55
|
# used, a new channel is created for it.
|
@@ -54,10 +58,21 @@ class Cabin::Channel
|
|
54
58
|
# This is useful for using the same Cabin::Channel across your
|
55
59
|
# entire application.
|
56
60
|
def get(identifier=$0)
|
57
|
-
@
|
58
|
-
return @channels[identifier]
|
61
|
+
return @channel_lock.synchronize { @channels[identifier] }
|
59
62
|
end # def Cabin::Channel.get
|
60
63
|
|
64
|
+
def set(identifier, channel)
|
65
|
+
return @channel_lock.synchronize { @channels[identifier] = channel }
|
66
|
+
end # def Cabin::Channel.set
|
67
|
+
|
68
|
+
def each(&block)
|
69
|
+
@channel_lock.synchronize do
|
70
|
+
@channels.each do |identifier, channel|
|
71
|
+
yield identifier, channel
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end # def Cabin::Channel.each
|
75
|
+
|
61
76
|
# Get a list of filters included in this class.
|
62
77
|
def filters
|
63
78
|
@filters ||= []
|
@@ -71,8 +86,8 @@ class Cabin::Channel
|
|
71
86
|
end
|
72
87
|
end # class << self
|
73
88
|
|
74
|
-
include Cabin::Mixins::Logger
|
75
89
|
include Cabin::Mixins::Timestamp
|
90
|
+
include Cabin::Mixins::Logger
|
76
91
|
include Cabin::Mixins::Timer
|
77
92
|
|
78
93
|
# All channels come with a metrics provider.
|
@@ -83,16 +98,19 @@ class Cabin::Channel
|
|
83
98
|
# Create a new logging channel.
|
84
99
|
# The default log level is 'info'
|
85
100
|
def initialize
|
86
|
-
@
|
101
|
+
@subscribers = {}
|
87
102
|
@data = {}
|
88
103
|
@level = :info
|
89
104
|
@metrics = Cabin::Metrics.new
|
90
105
|
@metrics.channel = self
|
106
|
+
@subscriber_lock = Mutex.new
|
91
107
|
end # def initialize
|
92
108
|
|
93
109
|
# Subscribe a new input
|
94
110
|
# New events will be sent to the subscriber using the '<<' method
|
95
111
|
# foo << event
|
112
|
+
#
|
113
|
+
# Returns a subscription id you can use later to unsubscribe
|
96
114
|
def subscribe(output)
|
97
115
|
# Wrap ruby stdlib Logger if given.
|
98
116
|
if output.is_a?(::Logger)
|
@@ -100,10 +118,18 @@ class Cabin::Channel
|
|
100
118
|
elsif output.is_a?(::IO)
|
101
119
|
output = Cabin::Outputs::IO.new(output)
|
102
120
|
end
|
103
|
-
@
|
104
|
-
|
105
|
-
|
121
|
+
@subscriber_lock.synchronize do
|
122
|
+
@subscribers[output.object_id] = output
|
123
|
+
end
|
124
|
+
return output.object_id
|
106
125
|
end # def subscribe
|
126
|
+
|
127
|
+
# Unsubscribe. Takes a 'subscription id' as returned by the subscribe method
|
128
|
+
def unsubscribe(id)
|
129
|
+
@subscriber_lock.synchronize do
|
130
|
+
@subscribers.delete(id)
|
131
|
+
end
|
132
|
+
end # def unsubscribe
|
107
133
|
|
108
134
|
# Set some contextual map value
|
109
135
|
def []=(key, value)
|
@@ -129,20 +155,21 @@ class Cabin::Channel
|
|
129
155
|
# is a string ISO8601 timestamp with microsecond precision.
|
130
156
|
def publish(data)
|
131
157
|
event = {}
|
132
|
-
|
158
|
+
self.class.filters.each do |filter|
|
159
|
+
filter.call(event)
|
160
|
+
end
|
133
161
|
|
134
162
|
if data.is_a?(String)
|
135
163
|
event[:message] = data
|
136
164
|
else
|
137
165
|
event.merge!(data)
|
138
166
|
end
|
167
|
+
event.merge!(@data) # Merge any logger context
|
139
168
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
@outputs.each do |out|
|
145
|
-
out << event
|
169
|
+
@subscriber_lock.synchronize do
|
170
|
+
@subscribers.each do |id, output|
|
171
|
+
output << event
|
172
|
+
end
|
146
173
|
end
|
147
174
|
end # def publish
|
148
175
|
|
@@ -158,5 +185,5 @@ class Cabin::Channel
|
|
158
185
|
return data
|
159
186
|
end # def dataify
|
160
187
|
|
161
|
-
public(:initialize, :context, :subscribe, :[]=, :[], :remove, :publish, :time, :context)
|
188
|
+
public(:initialize, :context, :subscribe, :unsubscribe, :[]=, :[], :remove, :publish, :time, :context)
|
162
189
|
end # class Cabin::Channel
|
data/lib/cabin/mixins/logger.rb
CHANGED
@@ -93,7 +93,7 @@ module Cabin::Mixins::Logger
|
|
93
93
|
data[:exception] = message.class
|
94
94
|
data[:backtrace] = message.backtrace
|
95
95
|
else
|
96
|
-
data
|
96
|
+
data = { :message => message }.merge(data)
|
97
97
|
end
|
98
98
|
|
99
99
|
# Add extra debugging bits (file, line, method) if level is debug.
|
data/lib/cabin/outputs/io.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "cabin"
|
2
2
|
require "json"
|
3
|
+
require "thread"
|
3
4
|
|
4
5
|
# Wrap IO objects with a reasonable log output.
|
5
6
|
#
|
@@ -42,14 +43,17 @@ class Cabin::Outputs::IO
|
|
42
43
|
|
43
44
|
def initialize(io)
|
44
45
|
@io = io
|
46
|
+
@lock = Mutex.new
|
45
47
|
end # def initialize
|
46
48
|
|
47
49
|
# Receive an event
|
48
50
|
def <<(event)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
@lock.synchronize do
|
52
|
+
if !@io.tty?
|
53
|
+
@io.puts(event.to_json)
|
54
|
+
else
|
55
|
+
tty_write(event)
|
56
|
+
end
|
53
57
|
end
|
54
58
|
end # def <<
|
55
59
|
|
data/test/test_logging.rb
CHANGED
@@ -56,12 +56,11 @@ describe Cabin::Channel do
|
|
56
56
|
assert(event[:duration].is_a?(Numeric))
|
57
57
|
end
|
58
58
|
|
59
|
-
test "double subscription" do
|
59
|
+
test "double subscription should still only subscribe once" do
|
60
60
|
@logger.subscribe(@target)
|
61
61
|
@logger.publish("Hello world")
|
62
|
-
assert_equal(
|
62
|
+
assert_equal(1, @target.data.length)
|
63
63
|
assert_equal("Hello world", @target.data[0][:message])
|
64
|
-
assert_equal("Hello world", @target.data[1][:message])
|
65
64
|
end
|
66
65
|
|
67
66
|
test "context values" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cabin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,12 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
description: This is an experiment to try and make logging more flexible and more
|
26
31
|
consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
|
27
32
|
Metrics, too!
|
@@ -88,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
93
|
version: '0'
|
89
94
|
requirements: []
|
90
95
|
rubyforge_project:
|
91
|
-
rubygems_version: 1.8.
|
96
|
+
rubygems_version: 1.8.18
|
92
97
|
signing_key:
|
93
98
|
specification_version: 3
|
94
99
|
summary: Experiments in structured and contextual logging
|