Compare commits
30 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | d1694702b8 | |
Denis Knauf | 6d2157caff | |
Denis Knauf | 25ff6d52d8 | |
Denis Knauf | 595f4ac6f1 | |
Denis Knauf | 8edb8ccdea | |
Denis Knauf | 26853e31e2 | |
Denis Knauf | fd8860766b | |
Denis Knauf | 3874aa0e43 | |
Denis Knauf | 3b20edf5cf | |
Denis Knauf | e04b61dd4a | |
Denis Knauf | eba19017af | |
Denis Knauf | 1108a9331c | |
Denis Knauf | e9cba75802 | |
Denis Knauf | 57896e3145 | |
Denis Knauf | 4e803a26e7 | |
Denis Knauf | 95417dec97 | |
Denis Knauf | eee7d885cc | |
Denis Knauf | 6c9b038de0 | |
Denis Knauf | d4dd5dfd0e | |
Denis Knauf | c44010dd64 | |
Denis Knauf | a7745f28a1 | |
Denis Knauf | 742bb3b01e | |
Denis Knauf | 59957f03b7 | |
Denis Knauf | 0f6eee9e91 | |
Denis Knauf | 868507672a | |
Denis Knauf | f06b2be077 | |
Denis Knauf | 849eeba2b0 | |
Denis Knauf | 334fc414a0 | |
Denis Knauf | e8223e84a9 | |
Denis Knauf | 431028811e |
|
@ -1,5 +1,4 @@
|
|||
*.gemspec
|
||||
*.gem
|
||||
t*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Slogger
|
||||
Semlogger
|
||||
=======
|
||||
|
||||
(Semi-)Structured Logger for Ruby (and Rails)
|
||||
(Semi-)Structured Logger for Ruby (and Rails)
|
||||
|
|
8
Rakefile
8
Rakefile
|
@ -4,11 +4,11 @@ require 'rake'
|
|||
begin
|
||||
require 'jeweler'
|
||||
Jeweler::Tasks.new do |gem|
|
||||
gem.name = "slogger"
|
||||
gem.name = "semlogger"
|
||||
gem.summary = %Q{(Semi-)Structured Logger}
|
||||
gem.description = %Q{(Semi-)Structured Logger for Ruby (and Rails)}
|
||||
gem.email = %w[Denis.Knauf@gmail.com]
|
||||
gem.homepage = "http://github.com/DenisKnauf/Slogger"
|
||||
gem.homepage = "http://github.com/DenisKnauf/semlogger"
|
||||
gem.authors = ["Denis Knauf"]
|
||||
gem.files = %w[AUTHORS README.md VERSION LICENSE lib/**/*.rb]
|
||||
gem.require_paths = %w[lib]
|
||||
|
@ -43,7 +43,7 @@ end
|
|||
|
||||
#task :default => :test
|
||||
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
if File.exist? 'VERSION'
|
||||
version = File.read 'VERSION'
|
||||
|
@ -52,7 +52,7 @@ Rake::RDocTask.new do |rdoc|
|
|||
end
|
||||
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = "Slogger #{version}"
|
||||
rdoc.title = "semlogger #{version}"
|
||||
rdoc.rdoc_files.include 'README.md'
|
||||
rdoc.rdoc_files.include 'AUTHORS'
|
||||
rdoc.rdoc_files.include 'LICENSE'
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
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
|
|
@ -1,4 +1,4 @@
|
|||
class Slogger::Filter
|
||||
class Semlogger::Filter
|
||||
attr_accessor :level, :logdev
|
||||
|
||||
def initialize level, logdev
|
||||
|
@ -15,9 +15,9 @@ end
|
|||
|
||||
%w[debug info warn error fatal].each do |level|
|
||||
eval <<-EOC
|
||||
class ::Slogger::F#{level.camelcase} < Slogger::Filter
|
||||
class ::Semlogger::F#{level.camelcase} < Semlogger::Filter
|
||||
def initialize *a
|
||||
super Slogger::#{level.upcase}, *a
|
||||
super Semlogger::#{level.upcase}, *a
|
||||
end
|
||||
end
|
||||
EOC
|
|
@ -1,4 +1,4 @@
|
|||
class Slogger::Formatter < ::Logger::Formatter
|
||||
class Semlogger::Formatter < ::Logger::Formatter
|
||||
def initialize tags, data
|
||||
@tags, @data = tags, data
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
class Slogger::Multiplex
|
||||
class Semlogger::Multiplex
|
||||
def initialize( *dests) @__dests__ = dests end
|
||||
|
||||
def write( *a, &e) @__dests__.each {|d| d.write *a, &e } end
|
|
@ -0,0 +1,51 @@
|
|||
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
|
|
@ -1,4 +1,4 @@
|
|||
class Slogger::Rotate
|
||||
class Semlogger::Rotate
|
||||
attr_reader :file
|
||||
|
||||
def filename() @filename.dup end
|
||||
|
@ -9,11 +9,7 @@ class Slogger::Rotate
|
|||
|
||||
def open_if
|
||||
name = Time.now.strftime( @filename).gsub /%\$/, $$.to_s
|
||||
#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
|
||||
close if @file and name != @file.path
|
||||
@file = File.open name, 'a' unless @file
|
||||
@file
|
||||
end
|
||||
|
@ -24,5 +20,6 @@ class Slogger::Rotate
|
|||
|
||||
def close
|
||||
@file.close
|
||||
@file = nil
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
class Slogger::Tailer < Rails::Rack::LogTailer
|
||||
class Semlogger::Tailer < Rails::Rack::LogTailer
|
||||
def initialize app, log = nil
|
||||
@app = app
|
||||
log ||= Rails.root.join( 'log', Rails.env).to_s.gsub('%', '%%') + '.%Y-%m-%d.%$.log'
|
|
@ -1,17 +1,21 @@
|
|||
class Slogger::Output
|
||||
class Semlogger::Output
|
||||
end
|
||||
|
||||
class Slogger::Writer < Slogger::Output
|
||||
class Semlogger::Writer < Semlogger::Output
|
||||
def initialize logdev = nil
|
||||
@logdev = logdev || ::Slogger::Rotate.new( ::Rails.root.join( 'log', ::Rails.env).to_s.gsub('%', '%%') + '.%Y-%m-%d.%$.log')
|
||||
@logdev = case logdev
|
||||
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
|
||||
|
||||
def add severity, time, progname, data, tags, message
|
||||
@logdev.write [severity, time, progname, data, tags, message].to_json
|
||||
@logdev.write [severity, time.xmlschema(9), progname, data, tags, message].to_json+"\n"
|
||||
end
|
||||
end
|
||||
|
||||
class Slogger::Printer < Slogger::Output
|
||||
class Semlogger::Printer < Semlogger::Output
|
||||
def initialize logdev = nil
|
||||
@logdev = logdev || $stdout
|
||||
@last_reqid = nil
|
||||
|
@ -24,8 +28,7 @@ class Slogger::Printer < Slogger::Output
|
|||
r = "Exception: #{message[2]} (#{message[1]}"
|
||||
r << "\n\t" << message[3].join( "\n\t") if message[3]
|
||||
r
|
||||
when String, Numeric, true, false, nil then message[0]
|
||||
when :obj then message[1].inspect
|
||||
when :String, :const then message[1]
|
||||
else message.inspect
|
||||
end
|
||||
reqid = data[:reqid]
|
|
@ -1,82 +0,0 @@
|
|||
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'
|
|
@ -1,37 +0,0 @@
|
|||
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
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -0,0 +1,50 @@
|
|||
# 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
|
||||
|
Loading…
Reference in New Issue