Merge pull request #3190 from riking/thrown_logging
Delete old ErrorLog, use Logster for 500 errors
This commit is contained in:
commit
6960639c58
|
@ -65,7 +65,7 @@ class ApplicationController < ActionController::Base
|
||||||
ActionController::UnknownController,
|
ActionController::UnknownController,
|
||||||
AbstractController::ActionNotFound].include? exception.class
|
AbstractController::ActionNotFound].include? exception.class
|
||||||
begin
|
begin
|
||||||
ErrorLog.report_async!(exception, self, request, current_user)
|
Discourse.handle_request_exception(exception, self, request, current_user)
|
||||||
rescue
|
rescue
|
||||||
# dont care give up
|
# dont care give up
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,7 +92,7 @@ class SessionController < ApplicationController
|
||||||
SingleSignOn::ACCESSORS.each do |a|
|
SingleSignOn::ACCESSORS.each do |a|
|
||||||
details[a] = sso.send(a)
|
details[a] = sso.send(a)
|
||||||
end
|
end
|
||||||
Discourse.handle_exception(e, details)
|
Discourse.handle_job_exception(e, details)
|
||||||
|
|
||||||
render text: I18n.t("sso.unknown_error"), status: 500
|
render text: I18n.t("sso.unknown_error"), status: 500
|
||||||
end
|
end
|
||||||
|
|
|
@ -108,7 +108,7 @@ module Jobs
|
||||||
begin
|
begin
|
||||||
retval = execute(opts)
|
retval = execute(opts)
|
||||||
rescue => exc
|
rescue => exc
|
||||||
Discourse.handle_exception(exc, error_context(opts))
|
Discourse.handle_job_exception(exc, error_context(opts))
|
||||||
end
|
end
|
||||||
return retval
|
return retval
|
||||||
end
|
end
|
||||||
|
@ -172,7 +172,7 @@ module Jobs
|
||||||
|
|
||||||
if exceptions.length > 0
|
if exceptions.length > 0
|
||||||
exceptions.each do |exception_hash|
|
exceptions.each do |exception_hash|
|
||||||
Discourse.handle_exception(exception_hash[:ex],
|
Discourse.handle_job_exception(exception_hash[:ex],
|
||||||
error_context(opts, exception_hash[:code], exception_hash[:other]))
|
error_context(opts, exception_hash[:code], exception_hash[:other]))
|
||||||
end
|
end
|
||||||
raise HandledExceptionWrapper.new exceptions[0][:ex]
|
raise HandledExceptionWrapper.new exceptions[0][:ex]
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Jobs
|
||||||
message = UserNotifications.mailing_list_notify(user, post)
|
message = UserNotifications.mailing_list_notify(user, post)
|
||||||
Email::Sender.new(message, :mailing_list, user).send
|
Email::Sender.new(message, :mailing_list, user).send
|
||||||
rescue => e
|
rescue => e
|
||||||
Discourse.handle_exception(e, error_context(
|
Discourse.handle_job_exception(e, error_context(
|
||||||
args,
|
args,
|
||||||
"Sending post to mailing list subscribers", {
|
"Sending post to mailing list subscribers", {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Jobs
|
||||||
unless UserAvatar.where("last_gravatar_download_attempt IS NULL").limit(1).first
|
unless UserAvatar.where("last_gravatar_download_attempt IS NULL").limit(1).first
|
||||||
problems = Post.rebake_old(250)
|
problems = Post.rebake_old(250)
|
||||||
problems.each do |hash|
|
problems.each do |hash|
|
||||||
Discourse.handle_exception(hash[:ex], error_context(args, "Rebaking post id #{hash[:post].id}", post_id: hash[:post].id))
|
Discourse.handle_job_exception(hash[:ex], error_context(args, "Rebaking post id #{hash[:post].id}", post_id: hash[:post].id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ module Jobs
|
||||||
problems = UserProfile.rebake_old(250)
|
problems = UserProfile.rebake_old(250)
|
||||||
problems.each do |hash|
|
problems.each do |hash|
|
||||||
user_id = hash[:profile].user_id
|
user_id = hash[:profile].user_id
|
||||||
Discourse.handle_exception(hash[:ex], error_context(args, "Rebaking user id #{user_id}", user_id: user_id))
|
Discourse.handle_job_exception(hash[:ex], error_context(args, "Rebaking user id #{user_id}", user_id: user_id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ module Jobs
|
||||||
client_message = RejectionMailer.send_rejection(message_template, message.from, template_args)
|
client_message = RejectionMailer.send_rejection(message_template, message.from, template_args)
|
||||||
Email::Sender.new(client_message, message_template).send
|
Email::Sender.new(client_message, message_template).send
|
||||||
else
|
else
|
||||||
Discourse.handle_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string))
|
Discourse.handle_job_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ module Jobs
|
||||||
pop.finish
|
pop.finish
|
||||||
end
|
end
|
||||||
rescue Net::POPAuthenticationError => e
|
rescue Net::POPAuthenticationError => e
|
||||||
Discourse.handle_exception(e, error_context(@args, "Signing in to poll incoming email"))
|
Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming email"))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
# TODO:
|
|
||||||
# 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
|
|
||||||
|
|
||||||
class ErrorLog
|
|
||||||
|
|
||||||
@lock = Mutex.new
|
|
||||||
|
|
||||||
def self.filename
|
|
||||||
"#{Rails.root}/log/#{Rails.env}_errors.log"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.clear!(_guid)
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.clear_all!()
|
|
||||||
File.delete(ErrorLog.filename) if File.exists?(ErrorLog.filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.report_async!(exception, controller, request, user)
|
|
||||||
Thread.new do
|
|
||||||
report!(exception, controller, request, user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.report!(exception, controller, request, user)
|
|
||||||
add_row!(
|
|
||||||
date: DateTime.now,
|
|
||||||
guid: SecureRandom.uuid,
|
|
||||||
user_id: user && user.id,
|
|
||||||
parameters: request && request.filtered_parameters.to_json,
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
@lock.synchronize do
|
|
||||||
File.open(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)
|
|
||||||
pos = 0
|
|
||||||
return [] unless File.exists?(filename)
|
|
||||||
|
|
||||||
loop do
|
|
||||||
lines = ""
|
|
||||||
File.open(self.filename, "r") do |f|
|
|
||||||
f.flock(File::LOCK_SH)
|
|
||||||
f.pos = pos
|
|
||||||
while !f.eof?
|
|
||||||
line = f.readline
|
|
||||||
lines << line
|
|
||||||
break if line.starts_with? "</hash>"
|
|
||||||
end
|
|
||||||
pos = f.pos
|
|
||||||
end
|
|
||||||
if lines != "" && skip == 0
|
|
||||||
h = {}
|
|
||||||
e = Nokogiri.parse(lines).children[0]
|
|
||||||
e.children.each do |inner|
|
|
||||||
h[inner.name] = inner.text
|
|
||||||
end
|
|
||||||
yield h
|
|
||||||
end
|
|
||||||
skip-=1 if skip > 0
|
|
||||||
break if lines == ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
private_class_method :sanitize_backtrace
|
|
||||||
end
|
|
|
@ -33,7 +33,7 @@ if Sidekiq.server?
|
||||||
manager.tick
|
manager.tick
|
||||||
rescue => e
|
rescue => e
|
||||||
# the show must go on
|
# the show must go on
|
||||||
Discourse.handle_exception(e, {message: "While ticking scheduling manager"})
|
Discourse.handle_job_exception(e, {message: "While ticking scheduling manager"})
|
||||||
end
|
end
|
||||||
sleep 1
|
sleep 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Discourse
|
||||||
# error_context() method in Jobs::Base to pass the job arguments and any
|
# error_context() method in Jobs::Base to pass the job arguments and any
|
||||||
# other desired context.
|
# other desired context.
|
||||||
# See app/jobs/base.rb for the error_context function.
|
# See app/jobs/base.rb for the error_context function.
|
||||||
def self.handle_exception(ex, context = {}, parent_logger = nil)
|
def self.handle_job_exception(ex, context = {}, parent_logger = nil)
|
||||||
context ||= {}
|
context ||= {}
|
||||||
parent_logger ||= SidekiqExceptionHandler
|
parent_logger ||= SidekiqExceptionHandler
|
||||||
|
|
||||||
|
@ -26,6 +26,29 @@ module Discourse
|
||||||
}.merge(context))
|
}.merge(context))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.handle_request_exception(ex, controller, request, current_user)
|
||||||
|
cm = RailsMultisite::ConnectionManagement
|
||||||
|
context = {
|
||||||
|
current_db: cm.current_db,
|
||||||
|
current_hostname: cm.current_hostname,
|
||||||
|
rails_action: controller.action_name,
|
||||||
|
rails_controller: controller.controller_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
env = request.env.dup
|
||||||
|
|
||||||
|
context.each do |key, value|
|
||||||
|
Logster.add_to_env(env, key, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
Thread.current[Logster::Logger::LOGSTER_ENV] = env
|
||||||
|
Logster.logger.fatal("#{ex.class.to_s}: #{ex.message} in #{controller.controller_name}##{controller.action_name}")
|
||||||
|
ensure
|
||||||
|
Thread.current[Logster::Logger::LOGSTER_ENV] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Expected less matches than what we got in a find
|
# Expected less matches than what we got in a find
|
||||||
class TooManyMatches < Exception; end
|
class TooManyMatches < Exception; end
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ module Oneboxer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rescue => e
|
rescue => e
|
||||||
Discourse.handle_exception(e, message: "While trying to onebox a URL", url: url)
|
Discourse.handle_job_exception(e, message: "While trying to onebox a URL", url: url)
|
||||||
# return a blank hash, so rest of the code works
|
# return a blank hash, so rest of the code works
|
||||||
{preview: "", onebox: ""}
|
{preview: "", onebox: ""}
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,10 +67,10 @@ module Scheduler
|
||||||
RailsMultisite::ConnectionManagement.establish_connection(db: db) if db
|
RailsMultisite::ConnectionManagement.establish_connection(db: db) if db
|
||||||
job.call
|
job.call
|
||||||
rescue => ex
|
rescue => ex
|
||||||
Discourse.handle_exception(ex, {message: "Running deferred code '#{desc}'"})
|
Discourse.handle_job_exception(ex, {message: "Running deferred code '#{desc}'"})
|
||||||
end
|
end
|
||||||
rescue => ex
|
rescue => ex
|
||||||
Discourse.handle_exception(ex, {message: "Processing deferred code queue"})
|
Discourse.handle_job_exception(ex, {message: "Processing deferred code queue"})
|
||||||
ensure
|
ensure
|
||||||
ActiveRecord::Base.connection_handler.clear_active_connections!
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,13 +42,13 @@ module Scheduler
|
||||||
def keep_alive
|
def keep_alive
|
||||||
@manager.keep_alive
|
@manager.keep_alive
|
||||||
rescue => ex
|
rescue => ex
|
||||||
Discourse.handle_exception(ex, {message: "Scheduling manager keep-alive"})
|
Discourse.handle_job_exception(ex, {message: "Scheduling manager keep-alive"})
|
||||||
end
|
end
|
||||||
|
|
||||||
def reschedule_orphans
|
def reschedule_orphans
|
||||||
@manager.reschedule_orphans!
|
@manager.reschedule_orphans!
|
||||||
rescue => ex
|
rescue => ex
|
||||||
Discourse.handle_exception(ex, {message: "Scheduling manager orphan rescheduler"})
|
Discourse.handle_job_exception(ex, {message: "Scheduling manager orphan rescheduler"})
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_queue
|
def process_queue
|
||||||
|
@ -66,7 +66,7 @@ module Scheduler
|
||||||
# Discourse.handle_exception was already called, and we don't have any extra info to give
|
# Discourse.handle_exception was already called, and we don't have any extra info to give
|
||||||
failed = true
|
failed = true
|
||||||
rescue => e
|
rescue => e
|
||||||
Discourse.handle_exception(e, {message: "Running a scheduled job", job: klass})
|
Discourse.handle_job_exception(e, {message: "Running a scheduled job", job: klass})
|
||||||
failed = true
|
failed = true
|
||||||
end
|
end
|
||||||
duration = ((Time.now.to_f - start) * 1000).to_i
|
duration = ((Time.now.to_f - start) * 1000).to_i
|
||||||
|
@ -77,7 +77,7 @@ module Scheduler
|
||||||
@mutex.synchronize { info.write! }
|
@mutex.synchronize { info.write! }
|
||||||
end
|
end
|
||||||
rescue => ex
|
rescue => ex
|
||||||
Discourse.handle_exception(ex, {message: "Processing scheduled job queue"})
|
Discourse.handle_job_exception(ex, {message: "Processing scheduled job queue"})
|
||||||
ensure
|
ensure
|
||||||
@running = false
|
@running = false
|
||||||
end
|
end
|
||||||
|
|
|
@ -139,7 +139,7 @@ describe Discourse do
|
||||||
it "should not fail when called" do
|
it "should not fail when called" do
|
||||||
exception = StandardError.new
|
exception = StandardError.new
|
||||||
|
|
||||||
Discourse.handle_exception(exception, nil, nil)
|
Discourse.handle_job_exception(exception, nil, nil)
|
||||||
expect(logger.exception).to eq(exception)
|
expect(logger.exception).to eq(exception)
|
||||||
expect(logger.context.keys).to eq([:current_db, :current_hostname])
|
expect(logger.context.keys).to eq([:current_db, :current_hostname])
|
||||||
end
|
end
|
||||||
|
@ -147,7 +147,7 @@ describe Discourse do
|
||||||
it "correctly passes extra context" do
|
it "correctly passes extra context" do
|
||||||
exception = StandardError.new
|
exception = StandardError.new
|
||||||
|
|
||||||
Discourse.handle_exception(exception, {message: "Doing a test", post_id: 31}, nil)
|
Discourse.handle_job_exception(exception, {message: "Doing a test", post_id: 31}, nil)
|
||||||
expect(logger.exception).to eq(exception)
|
expect(logger.exception).to eq(exception)
|
||||||
expect(logger.context.keys.sort).to eq([:current_db, :current_hostname, :message, :post_id].sort)
|
expect(logger.context.keys.sort).to eq([:current_db, :current_hostname, :message, :post_id].sort)
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,7 +29,7 @@ describe Jobs::Base do
|
||||||
it 'handles errors in multisite' do
|
it 'handles errors in multisite' do
|
||||||
RailsMultisite::ConnectionManagement.expects(:all_dbs).returns(['default','default','default'])
|
RailsMultisite::ConnectionManagement.expects(:all_dbs).returns(['default','default','default'])
|
||||||
# one exception per database
|
# one exception per database
|
||||||
Discourse.expects(:handle_exception).times(3)
|
Discourse.expects(:handle_job_exception).times(3)
|
||||||
|
|
||||||
bad = BadJob.new
|
bad = BadJob.new
|
||||||
expect{bad.perform({})}.to raise_error
|
expect{bad.perform({})}.to raise_error
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe Jobs::PollMailbox do
|
||||||
|
|
||||||
Net::POP3.any_instance.expects(:start).raises(error)
|
Net::POP3.any_instance.expects(:start).raises(error)
|
||||||
|
|
||||||
Discourse.expects(:handle_exception)
|
Discourse.expects(:handle_job_exception)
|
||||||
|
|
||||||
poller.poll_pop3
|
poller.poll_pop3
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
describe ErrorLog do
|
|
||||||
|
|
||||||
def boom
|
|
||||||
raise "boom"
|
|
||||||
end
|
|
||||||
|
|
||||||
def exception
|
|
||||||
begin
|
|
||||||
boom
|
|
||||||
rescue => e
|
|
||||||
return e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def controller
|
|
||||||
DraftController.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def request
|
|
||||||
ActionController::TestRequest.new(host: 'test')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "add_row!" do
|
|
||||||
it "creates a non empty file on first call" do
|
|
||||||
ErrorLog.clear_all!
|
|
||||||
ErrorLog.add_row!(hello: "world")
|
|
||||||
expect(File.exists?(ErrorLog.filename)).to eq true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "logging data" do
|
|
||||||
it "is able to read the data it writes" do
|
|
||||||
ErrorLog.clear_all!
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
i = 0
|
|
||||||
ErrorLog.each do |h|
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
expect(i).to eq 2
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is able to skip rows" do
|
|
||||||
ErrorLog.clear_all!
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
ErrorLog.report!(exception, controller, request, nil)
|
|
||||||
i = 0
|
|
||||||
ErrorLog.skip(3) do |h|
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
expect(i).to eq 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
Loading…
Reference in New Issue