Compare commits

..

No commits in common. "master" and "v0.0.1" have entirely different histories.

16 changed files with 175 additions and 313 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
t*.gem *.gemspec
*.gem
*.rbc *.rbc
.bundle .bundle
.config .config

View file

@ -1,4 +1,4 @@
Semlogger Slogger
======= =======
(Semi-)Structured Logger for Ruby (and Rails) (Semi-)Structured Logger for Ruby (and Rails)

View file

@ -4,11 +4,11 @@ require 'rake'
begin begin
require 'jeweler' require 'jeweler'
Jeweler::Tasks.new do |gem| Jeweler::Tasks.new do |gem|
gem.name = "semlogger" gem.name = "slogger"
gem.summary = %Q{(Semi-)Structured Logger} gem.summary = %Q{(Semi-)Structured Logger}
gem.description = %Q{(Semi-)Structured Logger for Ruby (and Rails)} gem.description = %Q{(Semi-)Structured Logger for Ruby (and Rails)}
gem.email = %w[Denis.Knauf@gmail.com] gem.email = %w[Denis.Knauf@gmail.com]
gem.homepage = "http://github.com/DenisKnauf/semlogger" gem.homepage = "http://github.com/DenisKnauf/Slogger"
gem.authors = ["Denis Knauf"] gem.authors = ["Denis Knauf"]
gem.files = %w[AUTHORS README.md VERSION LICENSE lib/**/*.rb] gem.files = %w[AUTHORS README.md VERSION LICENSE lib/**/*.rb]
gem.require_paths = %w[lib] gem.require_paths = %w[lib]
@ -43,7 +43,7 @@ end
#task :default => :test #task :default => :test
require 'rdoc/task' require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc| Rake::RDocTask.new do |rdoc|
if File.exist? 'VERSION' if File.exist? 'VERSION'
version = File.read 'VERSION' version = File.read 'VERSION'
@ -52,7 +52,7 @@ Rake::RDocTask.new do |rdoc|
end end
rdoc.rdoc_dir = 'rdoc' rdoc.rdoc_dir = 'rdoc'
rdoc.title = "semlogger #{version}" rdoc.title = "Slogger #{version}"
rdoc.rdoc_files.include 'README.md' rdoc.rdoc_files.include 'README.md'
rdoc.rdoc_files.include 'AUTHORS' rdoc.rdoc_files.include 'AUTHORS'
rdoc.rdoc_files.include 'LICENSE' rdoc.rdoc_files.include 'LICENSE'

View file

@ -1 +1 @@
0.0.10 0.0.1

View file

@ -1,185 +0,0 @@
require 'json'
class Object
def to_semlogger
[
self.class.name.to_sym,
self.respond_to?( :serializable_hash) ? self.serializable_hash : self
]
end
end
class Exception
def to_semlogger
[:exception] + super
end
end
class String
def to_semlogger
[:String, self]
end
end
%w[Numeric FalseClass TrueClass NilClass].each do |cl|
Object.const_get( cl).class_eval do
def to_semlogger
[:const, self]
end
end
end
class Semlogger < ::Logger
class Base
class <<self
attr_accessor :logger
end
attr_accessor :logger
def initialize
@logger = self.class.logger
end
def add severity, logger = nil, &block
(logger || @logger).add severity, self, &block
end
::Semlogger::Severity.constants.each do |severity|
module_eval <<-EOC, __FILE__, __LINE__+1
def #{severity.downcase} *a, &e
add #{::Semlogger::Severity.const_get severity}, *a, &e
end
EOC
end
end
class CustomType < Base
def initialize name, *obj
@name, @obj = name.to_s.to_sym, obj
end
def to_semlogger
[@name] + @obj
end
end
attr_accessor :logdev, :level, :progname
# some libs use #log_level
def log_level=( level) @level = level end
def log_level() @level end
class <<self
attr_accessor :progname, :logger
def new_rails_logger config, logdev = nil
require 'semlogger/rack'
logdev ||= ::Rails.root.join 'log', "#{::Rails.env.to_s.gsub('%', '%%')}.%Y-%m-%d.%$.log"
logdev = logdev.to_s
logger = nil
if Rails.env.production?
logger = new logdev
logger.level = Semlogger::INFO
elsif Rails.env.development?
logger = new Semlogger::Multiplex.new( Semlogger::FInfo.new( Semlogger::Printer.new), Semlogger::Writer.new( logdev))
logger.level = Semlogger::DEBUG
else
logger = new logdev
logger.level = Semlogger::DEBUG
end
config.middleware.swap Rails::Rack::Logger, Semlogger::Rack, [], {reqid: :uuid}
config.logger = logger
end
def custom( *a) CustomType.new( *a).tap {|t| t.logger = self.logger } end
end
def custom( *a) CustomType.new( *a).tap {|t| t.logger = self } end
@@progname = nil
def initialize logdev = nil, *a, &e
case logdev
when String, nil then logdev = ::Semlogger::Writer.new logdev
end
@progname = a[0] || @@progname
@level, @data, @tags, @logdev = DEBUG, {}, [], logdev
self.class.logger = self if !self.class.logger && self.class.logger.is_a?( Semlogger::Default)
end
def tagged *tags, &e
@tags += tags.flatten.compact
tags = tags.size
yield
ensure
#tags.times { @tags.pop }
@tags.slice! -tags .. -1
end
def add severity, message = nil, progname = nil, &block
severity ||= UNKNOWN
return true if @logdev.nil? or severity < @level
progname ||= @progname
if message.nil?
if block_given?
message = yield
else
message = progname
progname = @progname
end
end
@logdev.add severity, Time.new, progname, format_data( @data), format_tags( @tags), format_msg( message)
end
def format_msg msg
msg = msg.to_semlogger
case msg
when Array then msg
else [msg.class.name.to_sym, msg.inspect]
end
end
def format_obj obj
case obj
when Proc then obj.call
else obj
end
end
def format_tags tags
tags = tags ? tags.dup : []
tags.map &method( :format_obj)
end
def format_data data
data = data ? data.dup : {}
data.each {|k,v| data[k] = format_obj v }
end
def data data, &e
return @data unless e
@data.update data
keys = data.keys
yield
ensure
keys.each &data.method( :delete)
end
def caller &e
data caller: Kernel.method(:caller), &e
end
def thread &e
data thread: Proc.new { Thread.current.object_id }, &e
end
end
require 'semlogger/rotate'
require 'semlogger/multiplex'
require 'semlogger/filter'
require 'semlogger/writer'
class Semlogger
class Default < Semlogger
end
self.logger ||= Default.new
end

View file

@ -1,51 +0,0 @@
class Semlogger::Rack < Rails::Rack::Logger
def initialize app, tags = nil, data = nil
super app, tags
@data = data || {}
end
def call_app request, env
path = request.filtered_path
Rails.logger.custom( :connection, request.ip, Thread.current.object_id, request.request_method, path).info
@app.call env
ensure
ActiveSupport::LogSubscriber.flush_all!
end
def call env
request = ActionDispatch::Request.new env
compute_tagged_ request do
compute_data_ request do
call_app request, env
end
end
end
def compute_tagged_ request
if Rails.logger.respond_to? :tagged
Rails.logger.tagged( compute_tags( request)) { yield }
else
yield
end
end
def compute_data_ request
if Rails.logger.respond_to? :data
Rails.logger.data( compute_data( request)) { yield }
else
yield
end
end
def compute_data request
data = @data.dup
data.each do |k, v|
case v
when Proc
data[k] = v.call request
when Symbol
data[k] = request.send v
end
end
end
end

82
lib/slogger.rb Normal file
View file

@ -0,0 +1,82 @@
require 'json'
class Slogger < ::Logger
def initialize logdev = nil, *a, &e
case logdev
when String, nil then logdev = ::Slogger::Writer.new logdev
end
@level, @data, @tags, @logdev = DEBUG, {}, [], logdev
end
def tagged tags, &e
@tags += tags
tags = tags.size
yield
ensure
tags.times { @tags.pop }
end
def add severity, message = nil, progname = nil, &block
severity ||= UNKNOWN
if @logdev.nil? or severity < @level
return true
end
progname ||= @progname
if message.nil?
if block_given?
message = yield
else
message = progname
progname = @progname
end
end
@logdev.add severity, Time.new, progname, format_data( @data), format_tags( @tags), format_msg( message)
end
def format_msg msg
case msg
when String, Numeric, true, false, nil then [msg]
when Exception then [:exception, msg.class.name, msg.message.to_s, msg.backtrace]
else [:obj, msg]
end
end
def format_obj obj
case obj
when Proc then obj.call
else obj
end
end
def format_tags tags
tags = tags ? tags.dup : []
tags.map &method( :format_obj)
end
def format_data data
data = data ? data.dup : {}
data.each {|k,v| data[k] = format_obj v }
end
def data data, &e
@data.update data
keys = data.keys
yield
ensure
keys.each &data.method( :delete)
end
def caller &e
data caller: Kernel.method(:caller), &e
end
def thread &e
data thread: Proc.new { Thread.current.object_id }, &e
end
end
require 'slogger/rotate'
require 'slogger/multiplex'
require 'slogger/rack'
require 'slogger/filter'
require 'slogger/writer'

View file

@ -1,4 +1,4 @@
class Semlogger::Filter class Slogger::Filter
attr_accessor :level, :logdev attr_accessor :level, :logdev
def initialize level, logdev def initialize level, logdev
@ -15,9 +15,9 @@ end
%w[debug info warn error fatal].each do |level| %w[debug info warn error fatal].each do |level|
eval <<-EOC eval <<-EOC
class ::Semlogger::F#{level.camelcase} < Semlogger::Filter class ::Slogger::F#{level.camelcase} < Slogger::Filter
def initialize *a def initialize *a
super Semlogger::#{level.upcase}, *a super Slogger::#{level.upcase}, *a
end end
end end
EOC EOC

View file

@ -1,4 +1,4 @@
class Semlogger::Formatter < ::Logger::Formatter class Slogger::Formatter < ::Logger::Formatter
def initialize tags, data def initialize tags, data
@tags, @data = tags, data @tags, @data = tags, data
end end

View file

@ -1,4 +1,4 @@
class Semlogger::Multiplex class Slogger::Multiplex
def initialize( *dests) @__dests__ = dests end def initialize( *dests) @__dests__ = dests end
def write( *a, &e) @__dests__.each {|d| d.write *a, &e } end def write( *a, &e) @__dests__.each {|d| d.write *a, &e } end

37
lib/slogger/rack.rb Normal file
View file

@ -0,0 +1,37 @@
class Slogger::Rack < Rails::Rack::Logger
def initialize app, tags = nil, data = nil
super app, tags
@data = data
end
def call_app env
request = ActionDispatch::Request.new env
path = request.filtered_path
Rails.logger.info [:connection, request.ip, Thread.current.object_id, request.request_method, path]
@app.call env
ensure
ActiveSupport::LogSubscriber.flush_all!
end
def call env
if @data
Rails.logger.data( compute_data( env)) { super env }
else
super env
end
end
def compute_data env
request = ActionDispatch::Request.new env
data = @data.dup
data.each do |k, v|
case v
when Proc
data[k] = v.call request
when Symbol
data[k] = request.send v
end
end
end
end

View file

@ -1,4 +1,4 @@
class Semlogger::Rotate class Slogger::Rotate
attr_reader :file attr_reader :file
def filename() @filename.dup end def filename() @filename.dup end
@ -9,7 +9,11 @@ class Semlogger::Rotate
def open_if def open_if
name = Time.now.strftime( @filename).gsub /%\$/, $$.to_s name = Time.now.strftime( @filename).gsub /%\$/, $$.to_s
close if @file and name != @file.path #p at: Time.now, open_if: name, cur: @file ? @file.path : nil, e: @file ? name == @file.path : nil
if @file and name == @file.path
@file.close
@file = nil
end
@file = File.open name, 'a' unless @file @file = File.open name, 'a' unless @file
@file @file
end end
@ -20,6 +24,5 @@ class Semlogger::Rotate
def close def close
@file.close @file.close
@file = nil
end end
end end

View file

@ -1,4 +1,4 @@
class Semlogger::Tailer < Rails::Rack::LogTailer class Slogger::Tailer < Rails::Rack::LogTailer
def initialize app, log = nil def initialize app, log = nil
@app = app @app = app
log ||= Rails.root.join( 'log', Rails.env).to_s.gsub('%', '%%') + '.%Y-%m-%d.%$.log' log ||= Rails.root.join( 'log', Rails.env).to_s.gsub('%', '%%') + '.%Y-%m-%d.%$.log'

View file

@ -0,0 +1,28 @@
class Slogger::Transaction
include UUIDTools
attr_reader :app, :id, :default_id
class <<self
attr_accessor :transaction
def to_s() transaction.to_s end
def id() transaction.id end
end
def new_id
UUID.random_create
end
def initialize app, logger = nil, default_id = nil
@app, @default_id = app, default_id || new_id
(logger||self.class).transaction = self
end
def call *paras
@id = new_id
@app.call *paras
ensure
@id = @default_id
end
def to_s() @id.to_s end
end

View file

@ -1,21 +1,17 @@
class Semlogger::Output class Slogger::Output
end end
class Semlogger::Writer < Semlogger::Output class Slogger::Writer < Slogger::Output
def initialize logdev = nil def initialize logdev = nil
@logdev = case logdev @logdev = logdev || ::Slogger::Rotate.new( ::Rails.root.join( 'log', ::Rails.env).to_s.gsub('%', '%%') + '.%Y-%m-%d.%$.log')
when String then ::Semlogger::Rotate.new logdev
when nil then ::Semlogger::Rotate.new "log/#{File.basename $0}.%Y-%m-%d.%$.log"
else logdev
end
end end
def add severity, time, progname, data, tags, message def add severity, time, progname, data, tags, message
@logdev.write [severity, time.xmlschema(9), progname, data, tags, message].to_json+"\n" @logdev.write [severity, time, progname, data, tags, message].to_json
end end
end end
class Semlogger::Printer < Semlogger::Output class Slogger::Printer < Slogger::Output
def initialize logdev = nil def initialize logdev = nil
@logdev = logdev || $stdout @logdev = logdev || $stdout
@last_reqid = nil @last_reqid = nil
@ -28,7 +24,8 @@ class Semlogger::Printer < Semlogger::Output
r = "Exception: #{message[2]} (#{message[1]}" r = "Exception: #{message[2]} (#{message[1]}"
r << "\n\t" << message[3].join( "\n\t") if message[3] r << "\n\t" << message[3].join( "\n\t") if message[3]
r r
when :String, :const then message[1] when String, Numeric, true, false, nil then message[0]
when :obj then message[1].inspect
else message.inspect else message.inspect
end end
reqid = data[:reqid] reqid = data[:reqid]

View file

@ -1,50 +0,0 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = "semlogger"
s.version = "0.0.10"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Denis Knauf"]
s.date = "2012-11-21"
s.description = "(Semi-)Structured Logger for Ruby (and Rails)"
s.email = ["Denis.Knauf@gmail.com"]
s.extra_rdoc_files = [
"LICENSE",
"README.md"
]
s.files = [
"AUTHORS",
"LICENSE",
"README.md",
"VERSION",
"lib/semlogger.rb",
"lib/semlogger/filter.rb",
"lib/semlogger/formatter.rb",
"lib/semlogger/multiplex.rb",
"lib/semlogger/rack.rb",
"lib/semlogger/rotate.rb",
"lib/semlogger/tailer.rb",
"lib/semlogger/writer.rb"
]
s.homepage = "http://github.com/DenisKnauf/semlogger"
s.require_paths = ["lib"]
s.rubygems_version = "1.8.23"
s.summary = "(Semi-)Structured Logger"
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<json>, [">= 0"])
else
s.add_dependency(%q<json>, [">= 0"])
end
else
s.add_dependency(%q<json>, [">= 0"])
end
end