discourse/app/models/error_log.rb

112 lines
3.0 KiB
Ruby
Raw Normal View History

2013-02-07 23:45:24 +08:00
# TODO:
2013-02-06 03:16:51 +08:00
# a mechanism to iterate through errors in reverse
# async logging should queue, if dupe stack traces are found in batch error should be merged into prev one
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
class ErrorLog
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
@lock = Mutex.new
def self.filename
"#{Rails.root}/log/#{Rails.env}_errors.log"
end
def self.clear!(guid)
raise "not implemented"
end
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
def self.clear_all!()
File.delete(ErrorLog.filename) if File.exists?(ErrorLog.filename)
end
2013-02-07 23:45:24 +08:00
def self.report_async!(exception, controller, request, user)
2013-02-06 03:16:51 +08:00
Thread.new do
2013-02-07 23:45:24 +08:00
self.report!(exception, controller, request, user)
2013-02-06 03:16:51 +08:00
end
end
2013-02-07 23:45:24 +08:00
def self.report!(exception, controller, request, user)
2013-02-06 03:16:51 +08:00
add_row!(
:date => DateTime.now,
:guid => SecureRandom.uuid,
2013-02-07 23:45:24 +08:00
:user_id => user && user.id,
:request => filter_sensitive_post_data_parameters(controller, request.parameters).inspect,
2013-02-06 03:16:51 +08:00
:action => controller.action_name,
:controller => controller.controller_name,
:backtrace => sanitize_backtrace(exception.backtrace).join("\n"),
:message => exception.message,
:url => "#{request.protocol}#{request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]}#{request.fullpath}",
:exception_class => exception.class.to_s
)
end
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
def self.add_row!(hash)
data = hash.to_xml(skip_instruct: true)
# use background thread to write the log cause it may block if it gets backed up
2013-02-07 23:45:24 +08:00
@lock.synchronize do
2013-02-06 03:16:51 +08:00
File.open(self.filename, "a") do |f|
f.flock(File::LOCK_EX)
f.write(data)
f.close
end
end
end
def self.each(&blk)
skip(0,&blk)
end
def self.skip(skip=0)
data = nil
pos = 0
return [] unless File.exists?(self.filename)
2013-02-07 23:45:24 +08:00
loop do
2013-02-06 03:16:51 +08:00
lines = ""
File.open(self.filename, "r") do |f|
f.flock(File::LOCK_SH)
f.pos = pos
while !f.eof?
line = f.readline
lines << line
2013-02-07 23:45:24 +08:00
break if line.starts_with? "</hash>"
2013-02-06 03:16:51 +08:00
end
pos = f.pos
end
if lines != "" && skip == 0
h = {}
2013-02-07 23:45:24 +08:00
e = Nokogiri.parse(lines).children[0]
2013-02-06 03:16:51 +08:00
e.children.each do |inner|
h[inner.name] = inner.text
end
yield h
end
skip-=1 if skip > 0
break if lines == ""
2013-02-07 23:45:24 +08:00
end
2013-02-06 03:16:51 +08:00
end
private
def self.sanitize_backtrace(trace)
re = Regexp.new(/^#{Regexp.escape(Rails.root.to_s)}/)
trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
end
def self.exclude_raw_post_parameters?(controller)
controller && controller.respond_to?(:filter_parameters)
end
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
def self.filter_sensitive_post_data_parameters(controller, parameters)
exclude_raw_post_parameters?(controller) ? controller.__send__(:filter_parameters, parameters) : parameters
end
2013-02-07 23:45:24 +08:00
2013-02-06 03:16:51 +08:00
def self.filter_sensitive_post_data_from_env(env_key, env_value, controller)
return env_value unless exclude_raw_post_parameters?
return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
return controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
end
end