Browse files

Add sentry middleware.

  • Loading branch information...
1 parent 34faabc commit b69cd868a00f24d8d77bd3327be15a4b3b92167e @myronmarston myronmarston committed Feb 1, 2013
Showing with 153 additions and 0 deletions.
  1. +70 −0 lib/qless/middleware/sentry.rb
  2. +1 −0 qless.gemspec
  3. +82 −0 spec/unit/middleware/sentry_spec.rb
View
70 lib/qless/middleware/sentry.rb
@@ -0,0 +1,70 @@
+require 'raven'
+
+module Qless
+ module Middleware
+ # This middleware logs errors to the sentry exception notification service:
+ # http://getsentry.com/
+ module Sentry
+ def around_perform(job)
+ super
+ rescue Exception => e
+ SentryLogger.new(e, job).log
+ raise
+ end
+
+ # Logs a single exception to Sentry, adding pertinent job info.
+ class SentryLogger
+ def initialize(exception, job)
+ @exception, @job = exception, job
+ end
+
+ def log
+ event = ::Raven::Event.capture_exception(@exception) do |evt|
+ evt.extra = { job: job_metadata }
+ end
+
+ safely_send event
+ end
+
+ private
+
+ def safely_send(event)
+ return unless event
+ ::Raven.send(event)
+ rescue
+ # We don't want to silence our errors when the Sentry server
+ # responds with an error. We'll still see the errors on the
+ # Qless Web UI.
+ end
+
+
+ def job_metadata
+ {
+ jid: @job.jid,
+ klass: @job.klass_name,
+ history: job_history,
+ data: @job.data,
+ queue: @job.queue_name,
+ worker: @job.worker_name,
+ tags: @job.tags,
+ priority: @job.priority
+ }
+ end
+
+ # We want to log formatted timestamps rather than integer timestamps
+ def job_history
+ @job.history.map do |history_event|
+ history_event.each_with_object({}) do |(key, value), hash|
+ hash[key] = if value.is_a?(Integer)
+ Time.at(value).getutc.iso8601
+ else
+ value
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
View
1 qless.gemspec
@@ -46,4 +46,5 @@ Gem::Specification.new do |s|
s.add_development_dependency "poltergeist" , "~> 1.0"
s.add_development_dependency "launchy" , "~> 2.1.0"
s.add_development_dependency "simplecov" , "~> 0.6.2"
+ s.add_development_dependency 'sentry-raven', "~> 0.4"
end
View
82 spec/unit/middleware/sentry_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+require 'qless/middleware/sentry'
+require 'qless'
+require 'qless/worker'
+
+module Qless
+ module Middleware
+ describe Sentry do
+ let(:client) { fire_double("Qless::Client").as_null_object }
+
+ let(:klass) do
+ Class.new do
+ def self.perform(job)
+ raise "job failure"
+ end
+ end
+ end
+
+ let(:time_1) { Time.utc(2012, 8, 1, 12, 30) }
+ let(:time_2) { Time.utc(2012, 8, 1, 12, 31) }
+
+ let(:history_event) do
+ {'popped' => time_2.to_i,
+ 'put' => time_1.to_i,
+ 'q' => 'test_error',
+ 'worker' => 'Myrons-Macbook-Pro.local-44396'}
+ end
+
+ let(:job) do
+ stub_const("MyJob", klass)
+ Qless::Job.build(client, MyJob,
+ data: { "some" => "data" },
+ worker: 'w1', queue: 'q1',
+ jid: 'abc', history: [history_event],
+ tags: ['x', 'y'], priority: 10)
+
+ end
+
+ def perform_job
+ worker = Qless::Worker.new(stub)
+ worker.extend Qless::Middleware::Sentry
+ worker.perform(job)
+ end
+
+ it 'logs jobs with errors to sentry' do
+ sent_event = nil
+ ::Raven.stub(:send) { |e| sent_event = e }
+
+ # it's important the job still fails normally
+ job.should_receive(:fail)
+
+ perform_job
+
+ expect(sent_event.message).to include("job failure")
+ expect(sent_event.extra[:job]).to include(
+ jid: 'abc',
+ klass: 'MyJob',
+ data: { "some" => "data" },
+ queue: 'q1',
+ worker: 'w1',
+ tags: ['x', 'y'],
+ priority: 10
+ )
+
+ expect(sent_event.extra[:job][:history].first).to include(
+ 'put' => time_1.iso8601, 'popped' => time_2.iso8601
+ )
+ end
+
+ it 'does not silence the original error when sentry responds with an error' do
+ ::Raven.stub(:send) { raise ::Raven::Error, "sentry failure" }
+ job.should_receive(:fail) do |_, message|
+ expect(message).to include("job failure")
+ expect(message).not_to include("sentry failure")
+ end
+
+ perform_job
+ end
+ end
+ end
+end
+

0 comments on commit b69cd86

Please sign in to comment.