Mixboard
Mixboard is a framework for routing signals from sources to sinks. Signals can be metrics, log messages, errors, or any other similar piece of information one might want to route from a source to a sink. Conditional routing is available via filters, and transformation of signals is possible via signal processors. A specific end-to-end route is called a channel.
It's a common pattern to spend a non-trivial amount of time enabling and disabling logs, metrics, or other types of debugging information on a per-customer or a per-business-object basis. Instead, this framework provides the tooling to do this type of observability work ahead of time (i.e. inserting the proper signalling) and be able to enable/disable it selectively at runtime or via configuration.
Usage
Mixboard is designed as a Rails Engine, so usage is straightforward. A simple example is below.
Signals
A signal is a payload containing information that can be routed through the mixer.
First, create a signal by extending Mixboard::Signal
if you aren't using a generic
signal type, like so:
class DummySignal < Mixboard::Signal
attr_accessor :my_attribute
end
Signal Levels
Signals have levels associated with them, similar to log messages. Using levels,
it's easy to subcategorize or delineate the granularity of signals. The provided
levels are: verbose
, debug
, info
, warn
, and error
, similar to most
logging frameworks. These are often helpful when using filters.
Sources
A source is anything that emits signals of a certain type.
Create a source by extending Mixboard::Source
, making sure the methods you
plan on using on your source are calling the emit
method with a signal instance,
like so:
class DummySource < Mixboard::Source
def signal_class
DummySignal
end
def my_logging_method
signal = DummySignal.new
signal.my_attribute = 'FizzBuzz'
emit(signal)
end
end
Sinks
A sink is anything that accepts signals of a certain type.
Create a sink by extending Mixboard::Sink
if you aren't using a generic sink,
making sure to provide an accept
method, like so:
class DummySink < Mixboard::Sink
def signal_class
DummySignal
end
def accept(signal)
MyExternalLoggingService.post(signal.my_attribute)
end
end
Configuration
To statically configure channels, create a mixboard.rb
initializer and
configure a channel, like so:
Mixboard::Mixer.configure do |c|
channel = Mixboard::Channel.new
.add_source(DummySource)
.add_sink(DummySink.new(any_config_options))
c.add_channel(channel)
end
And you should be all set. You can instantiate your DummySource
anywhere, use
my_logging_method
, and Mixboard will connect the two components!
To dynamically configure channels... TODO!
Signal Processors
Signal processors accept signals of a certain type and emit signals of a certain type. The are used for transforming signals from one type to another.
Imagine wanting to send an error message to something like Sentry, but also wanting to track it as a metric in StatsD. You would set up two channels:
- Source: Logger; Sink: SentrySink
- Source: Logger; Sink: StatsDCountMetricSink
For the first channel, the signal types match (assuming they are both something simple,
such as MessageSignal
, so you don't need to associate a signal processor. For the
second channel, you have a source type of MessageSignal
, and a sink type of CountMetric
.
You would need to add a signal processor that accepts MessageSignal
(or a superclass),
and emits CountMetric
. Your channels would look like:
- Source: Logger; Sink: SentrySink
- Source: Logger; Sink: StatsDCountMetricSink, SignalProcessors: MessageSignalCounter
Your signal processor might look like:
class MessageSignalCounter < Mixboard::SignalProcessor
def initialize(metric_name:)
@metric_name = metric_name
end
def input_signal_class
MessageSignal
end
def output_signal_class
CountMetric
end
def transform(signal)
return CountMetric.new(@metric_name, 1)
end
end
Filters
Filters interrupt the flow of signals through the system. You can add filters before or after channels and signal processors. Here's a simple example:
class DummyFilter < Mixboard::Filter
def signal_class
DummySignal
end
def filter(signal)
signal
end
end
Filters' filter
method should only return the original signal to continue processing, or
return nil to indicate to stop processing the signal further.
Installation
Add this line to your application's Gemfile:
gem 'mixboard'
Or install it yourself as:
$ gem install mixboard
To take advantage of the dynamic "mixing panel" to configure channels... TODO
Contributing
Contribution directions go here. TODO
License
The gem is available as open source under the terms of the MIT License.