|
=begin |
|
Usage: |
|
|
|
require 'audit_logger' |
|
|
|
class MyClass |
|
include AuditLogger::Helpers |
|
audit_logger_name 'my-class.log' |
|
|
|
# It works in instance methods |
|
def foo |
|
# Standard logging |
|
audit # => "#{self.class}[id:#{id}]##{method}:#{line}" |
|
|
|
# Log with a message |
|
audit 'this message' # => "#{self.class}[id:#{id}]##{method}:#{line}\nthis message" |
|
|
|
# If you want to log to a different logfile than the current class default: |
|
audit log:'mylogger.log' |
|
|
|
# There are several options you can pass in |
|
audit log:'mylogger.log', body:'this message', level:'error' |
|
end |
|
|
|
# It works in class or static methods |
|
def self.foo |
|
audit # => "#{self}::#{method}:#{line}" |
|
end |
|
|
|
end |
|
|
|
=end |
|
|
|
end |
|
|
|
class AuditLogger < Logger |
|
cattr_accessor :audit_loggers |
|
self.audit_loggers = {} |
|
|
|
at_exit { AuditLogger.close_all } |
|
|
|
# AuditLogger.[] is the preferred creation method |
|
def initialize(name, *args) |
|
# make sure to track any creations |
|
super |
|
self.class.audit_loggers[name] = self |
|
end |
|
|
|
def audit(*args) |
|
|
|
end |
|
|
|
def puts(*args); info args.join "\n\t"; end |
|
|
|
def tabs(*args); info args.join "\t"; end |
|
|
|
def self.file_name(name) |
|
Rails.root.join('log', "#{name.to_s.downcase}.log").to_s |
|
end |
|
|
|
def self.[](name) |
|
name = file_name(name) |
|
audit_loggers[name] || begin |
|
# log rotation: keep 25 old log files with 20MB |
|
AuditLogger.new(name, 25, (2097152*10)) |
|
end |
|
end |
|
|
|
def self.close_all |
|
audit_loggers.values.each do |name, logger| |
|
if logger |
|
logger.close rescue nil # Ignore |
|
end |
|
end |
|
|
|
audit_loggers.clear |
|
end |
|
|
|
def format_message(severity, timestamp, progname, msg) |
|
"#{timestamp.to_formatted_s(:db)} #{severity} #{msg}\n" |
|
end |
|
|
|
module Helpers |
|
@audit_logger_name = 'default_audit' |
|
|
|
private |
|
|
|
def audit(*args) |
|
proc = |
|
if self.respond_to?(:id) |
|
lambda{|line, method| "#{self.class}[id:#{id}]##{method}:#{line}" } |
|
else |
|
lambda{|line, method| "#{self.class}##{method}:#{line}" } |
|
end |
|
self.class._audit proc, args |
|
end |
|
|
|
def self.included(base) |
|
base.extend ClassMethods |
|
end |
|
|
|
module ClassMethods |
|
def audit(*args) |
|
_audit lambda{|line, method| "#{self}::#{method}:#{line}"}, args |
|
end |
|
|
|
def audit_logger_name name |
|
@audit_logger_name = name |
|
end |
|
|
|
def _audit header_proc, args |
|
# build header line |
|
line, method = _stack_data 2 |
|
header = header_proc.call(line,method) |
|
# set options |
|
options = args.first.is_a?(Hash) ? args.first : {body:args} |
|
log = options[:log] ? AuditLogger[options[:log]] : AuditLogger[@audit_logger_name] |
|
# build logger output |
|
output = |
|
if options[:body] |
|
body = Array(options[:body]).join options.fetch(:delim, " ") |
|
[header,body].join options.fetch(:header_delim, "\n\t") |
|
else |
|
header |
|
end |
|
# write to log, using appropriate log level |
|
options[:level] ||= :info |
|
log.send options[:level], output |
|
end |
|
|
|
# Returns method and line number from backtrace |
|
def _stack_data caller_steps=0 |
|
match_data = caller[caller_steps].match /:(\d+):in `(.+)'/ |
|
return [match_data[1], match_data[2]] |
|
end |
|
end |
|
|
|
end |
|
|
|
end |