1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
require 'monitor'
module Yell #:nodoc:
module Adapters #:nodoc:
# This class provides the basic interface for all allowed operations on any
# adapter implementation. Other adapters should inherit from it for the methods
# used by the {Yell::Logger}.
#
# Writing your own adapter is really simple. Inherit from the base class and use
# the `setup`, `write` and `close` methods. Yell requires the `write` method to be
# specified (`setup` and `close` are optional).
#
#
# The following example shows how to define a basic Adapter to format and print
# log events to STDOUT:
#
# class PutsAdapter < Yell::Adapters::Base
# include Yell::Formatter::Helpers
#
# setup do |options|
# self.format = options[:format]
# end
#
# write do |event|
# message = format.call(event)
#
# STDOUT.puts message
# end
# end
#
#
# After the Adapter has been written, we need to register it to Yell:
#
# Yell::Adapters.register :puts, PutsAdapter
#
# Now, we can use it like so:
#
# logger = Yell.new :puts
# logger.info "Hello World!"
class Base < Monitor
include Yell::Helpers::Base
include Yell::Helpers::Level
class << self
# Setup your adapter with this helper method.
#
# @example
# setup do |options|
# @file_handle = File.new( '/dev/null', 'w' )
# end
def setup( &block )
compile!(:setup!, &block)
end
# Define your write method with this helper.
#
# @example Printing messages to file
# write do |event|
# @file_handle.puts event.message
# end
def write( &block )
compile!(:write!, &block)
end
# Define your open method with this helper.
#
# @example Open a file handle
# open do
# @stream = ::File.open( 'test.log', ::File::WRONLY|::File::APPEND|::File::CREAT )
# end
def open( &block )
compile!(:open!, &block)
end
# Define your close method with this helper.
#
# @example Closing a file handle
# close do
# @stream.close
# end
def close( &block )
compile!(:close!, &block)
end
private
# Pretty funky code block, I know but here is what it basically does:
#
# @example
# compile! :write! do |event|
# puts event.message
# end
#
# # Is actually defining the `:write!` instance method with a call to super:
#
# def write!( event )
# puts event.method
# super
# end
def compile!( name, &block )
# Get the already defined method
m = instance_method( name )
# Create a new method with leading underscore
define_method("_#{name}", &block)
_m = instance_method("_#{name}")
remove_method("_#{name}")
# Define instance method
define!(name, _m, m, &block)
end
# Define instance method by given name and call the unbound
# methods in order with provided block.
def define!( name, _m, m, &block )
if block.arity == 0
define_method(name) do
_m.bind(self).call
m.bind(self).call
end
else
define_method(name) do |*args|
_m.bind(self).call(*args)
m.bind(self).call(*args)
end
end
end
end
# Initializes a new Adapter.
#
# You should not overload the constructor, use #setup instead.
def initialize( options = {}, &block )
super() # init the monitor superclass
reset!
setup!(options)
# eval the given block
block.arity > 0 ? block.call(self) : instance_eval(&block) if block_given?
end
# The main method for calling the adapter.
#
# The method receives the log `event` and determines whether to
# actually write or not.
def write( event )
synchronize { write!(event) } if write?(event)
rescue Exception => e
# make sure the adapter is closed and re-raise the exception
synchronize { close }
raise(e)
end
# Close the adapter (stream, connection, etc).
#
# Adapter classes should provide their own implementation
# of this method.
def close
close!
end
# Get a pretty string representation of the adapter, including
def inspect
inspection = inspectables.map { |m| "#{m}: #{send(m).inspect}" }
"#<#{self.class.name} #{inspection * ', '}>"
end
private
# Setup the adapter instance.
#
# Adapter classes should provide their own implementation
# of this method (if applicable).
def setup!( options )
self.level = Yell.__fetch__(options, :level)
end
# Perform the actual write.
#
# Adapter classes must provide their own implementation
# of this method.
def write!( event )
# Not implemented
end
# Perform the actual open.
#
# Adapter classes should provide their own implementation
# of this method.
def open!
# Not implemented
end
# Perform the actual close.
#
# Adapter classes should provide their own implementation
# of this method.
def close!
# Not implemented
end
# Determine whether to write at the given severity.
#
# @example
# write? Yell::Event.new( 'INFO', 'Hello Wold!' )
#
# @param [Yell::Event] event The log event
#
# @return [Boolean] true or false
def write?( event )
level.nil? || level.at?(event.level)
end
# Get an array of inspected attributes for the adapter.
def inspectables
[:level]
end
end
end
end
|