Class: Cabin::Channel

Inherits:
Object
  • Object
show all
Includes:
Mixins::Logger, Mixins::Pipe, Mixins::Terminal, Mixins::Timer, Mixins::Timestamp
Defined in:
lib/cabin/channel.rb

Overview

A wonderful channel for logging.

You can log normal messages through here, but you should be really shipping structured data. A message is just part of your data. “An error occurred” - in what? when? why? how?

Logging channels support the usual ‘info’ ‘warn’ and other logger methods provided by Ruby’s stdlib Logger class

It additionally allows you to store arbitrary pieces of data in it like a hash, so your call stack can do be this:

@logger = Cabin::Channel.new
rubylog = Logger.new(STDOUT) # ruby's stlib logger
@logger.subscribe(rubylog)

def foo(val)
  context = @logger.context()
  context[:foo] = val
  context[:example] = 100
  bar()

  # Clear any context we just wanted bar() to know about
  context.clear()

  @logger.info("Done in foo")
end

def bar
  @logger.info("Fizzle")
end

The result:

I, [2011-10-11T01:00:57.993200 #1209]  INFO -- : {:timestamp=>"2011-10-11T01:00:57.992353-0700", :foo=>"Hello", :example=>100, :message=>"Fizzle", :level=>:info}
I, [2011-10-11T01:00:57.993575 #1209]  INFO -- : {:timestamp=>"2011-10-11T01:00:57.993517-0700", :message=>"Done in foo", :level=>:info}

Constant Summary

Constants included from Mixins::Logger

Mixins::Logger::BACKTRACE_RE, Mixins::Logger::LEVELS

Instance Attribute Summary collapse

Attributes included from Mixins::Logger

#level

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixins::Terminal

#terminal

Methods included from Mixins::Timer

#time

Methods included from Mixins::Pipe

#pipe

Methods included from Mixins::Logger

included, #log

Methods included from Mixins::Timestamp

extended, included

Constructor Details

#initializeChannel

Create a new logging channel. The default log level is ‘info’



122
123
124
125
126
127
128
129
# File 'lib/cabin/channel.rb', line 122

def initialize
  @subscribers = {}
  @data = {}
  @level = :info
  @metrics = Cabin::Metrics.new
  @metrics.channel = self
  @subscriber_lock = Mutex.new
end

Instance Attribute Details

#metricsObject

All channels come with a metrics provider.



116
117
118
# File 'lib/cabin/channel.rb', line 116

def metrics
  @metrics
end

Class Method Details

.action(&block) ⇒ Object Also known as: filter

Register a new action. The block is passed the event. It is expected to modify that event or otherwise do nothing.



87
88
89
# File 'lib/cabin/channel.rb', line 87

def action(&block)
  actions << block
end

.actionsObject Also known as: filters

Get a list of actions included in this class.



80
81
82
# File 'lib/cabin/channel.rb', line 80

def actions
  @actions ||= []
end

.allow_event?(event, subscription) ⇒ Boolean

Decide to publish the event based on conditions and subscription options

Returns:

  • (Boolean)


104
105
106
# File 'lib/cabin/channel.rb', line 104

def allow_event?(event, subscription)
  conditions.all? { |condition| condition.call(event, subscription) }
end

.condition(&block) ⇒ Object

Register a new condition. The block must expect an event and a subscription. It is expected to either return true (allow the event) or false (reject it).



99
100
101
# File 'lib/cabin/channel.rb', line 99

def condition(&block)
  conditions << block
end

.conditionsObject

Get a list of conditions included in this class.



93
94
95
# File 'lib/cabin/channel.rb', line 93

def conditions
  @conditions ||= []
end

.each(&block) ⇒ Object

def Cabin::Channel.set



71
72
73
74
75
76
77
# File 'lib/cabin/channel.rb', line 71

def each(&block)
  @channel_lock.synchronize do
    @channels.each do |identifier, channel|
      yield identifier, channel
    end
  end
end

.get(identifier = $0) ⇒ Object

Get a channel for a given identifier. If this identifier has never been used, a new channel is created for it. The default identifier is the application executable name.

This is useful for using the same Cabin::Channel across your entire application.



63
64
65
# File 'lib/cabin/channel.rb', line 63

def get(identifier=$0)
  return @channel_lock.synchronize { @channels[identifier] }
end

.set(identifier, channel) ⇒ Object

def Cabin::Channel.get



67
68
69
# File 'lib/cabin/channel.rb', line 67

def set(identifier, channel)
  return @channel_lock.synchronize { @channels[identifier] = channel }
end

Instance Method Details

#contextObject

def publish



202
203
204
205
# File 'lib/cabin/channel.rb', line 202

def context
  ctx = Cabin::Context.new(self)
  return ctx
end

#publish(data, &block) ⇒ Object

Publish data to all outputs. The data is expected to be a hash or a string.

A new hash is generated based on the data given. If data is a string, then it will be added to the new event hash with key :message.

A special key :timestamp is set at the time of this method call. The value is a string ISO8601 timestamp with microsecond precision.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/cabin/channel.rb', line 178

def publish(data, &block)
  event = {}

  self.class.actions.each do |action|
    action.call(event)
  end

  if data.is_a?(String)
    event[:message] = data
  else
    event.merge!(data)
  end
  event.merge!(@data) # Merge any logger context

  @subscriber_lock.synchronize do
    @subscribers.each do |_, subscriber|
      append = block_given? ? block.call(subscriber, event) : true
      if append && self.class.allow_event?(event, subscriber)
        subscriber << event
      end
    end
  end
end

#remove(key) ⇒ Object

Remove a context value by name.



167
168
169
# File 'lib/cabin/channel.rb', line 167

def remove(key)
  @data.delete(key)
end

#subscribe(output, options = {}) ⇒ Object

Subscribe a new input New events will be sent to the subscriber using the ‘<<’ method

foo << event

Returns a subscription id you can use later to unsubscribe



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/cabin/channel.rb', line 136

def subscribe(output, options = {})
  # Wrap ruby stdlib Logger if given.
  if output.is_a?(::Logger)
    output = Cabin::Outputs::StdlibLogger.new(output)
  elsif output.is_a?(::IO)
    output = Cabin::Outputs::IO.new(output)
  end
  @subscriber_lock.synchronize do
    @subscribers[output.object_id] = Cabin::Subscriber.new(output, options)
  end
  return output.object_id
end

#unsubscribe(id) ⇒ Object

Unsubscribe. Takes a ‘subscription id’ as returned by the subscribe method



150
151
152
153
154
# File 'lib/cabin/channel.rb', line 150

def unsubscribe(id)
  @subscriber_lock.synchronize do
    @subscribers.delete(id)
  end
end