").append(st.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,a||[e.responseText,t,e])}),this},st.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){st.fn[t]=function(e){return this.on(t,e)}}),st.each(["get","post"],function(e,n){st[n]=function(e,r,i,o){return st.isFunction(r)&&(o=o||i,i=r,r=t),st.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),st.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Dn,type:"GET",isLocal:Fn.test(jn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":In,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":st.parseJSON,"text xml":st.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?H(H(e,st.ajaxSettings),t):H(st.ajaxSettings,e)},ajaxPrefilter:D(Wn),ajaxTransport:D($n),ajax:function(e,n){function r(e,n,r,s){var l,f,v,b,T,N=n;2!==x&&(x=2,u&&clearTimeout(u),i=t,a=s||"",w.readyState=e>0?4:0,r&&(b=M(p,w,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=w.getResponseHeader("Last-Modified"),T&&(st.lastModified[o]=T),T=w.getResponseHeader("etag"),T&&(st.etag[o]=T)),304===e?(l=!0,N="notmodified"):(l=q(p,b),N=l.state,f=l.data,v=l.error,l=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),w.status=e,w.statusText=(n||N)+"",l?g.resolveWith(d,[f,N,w]):g.rejectWith(d,[w,N,v]),w.statusCode(y),y=t,c&&h.trigger(l?"ajaxSuccess":"ajaxError",[w,p,l?f:v]),m.fireWith(d,[w,N]),c&&(h.trigger("ajaxComplete",[w,p]),--st.active||st.event.trigger("ajaxStop")))}"object"==typeof e&&(n=e,e=t),n=n||{};var i,o,a,s,u,l,c,f,p=st.ajaxSetup({},n),d=p.context||p,h=p.context&&(d.nodeType||d.jquery)?st(d):st.event,g=st.Deferred(),m=st.Callbacks("once memory"),y=p.statusCode||{},v={},b={},x=0,T="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!s)for(s={};t=_n.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=b[n]=b[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)y[t]=[y[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(g.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,p.url=((e||p.url||Dn)+"").replace(Mn,"").replace(Bn,jn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=st.trim(p.dataType||"*").toLowerCase().match(lt)||[""],null==p.crossDomain&&(l=Pn.exec(p.url.toLowerCase()),p.crossDomain=!(!l||l[1]===jn[1]&&l[2]===jn[2]&&(l[3]||("http:"===l[1]?80:443))==(jn[3]||("http:"===jn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=st.param(p.data,p.traditional)),L(Wn,p,n,w),2===x)return w;c=p.global,c&&0===st.active++&&st.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!On.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(Hn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=qn.test(o)?o.replace(qn,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),p.ifModified&&(st.lastModified[o]&&w.setRequestHeader("If-Modified-Since",st.lastModified[o]),st.etag[o]&&w.setRequestHeader("If-None-Match",st.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&w.setRequestHeader("Content-Type",p.contentType),w.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+In+"; q=0.01":""):p.accepts["*"]);for(f in p.headers)w.setRequestHeader(f,p.headers[f]);if(p.beforeSend&&(p.beforeSend.call(d,w,p)===!1||2===x))return w.abort();T="abort";for(f in{success:1,error:1,complete:1})w[f](p[f]);if(i=L($n,p,n,w)){w.readyState=1,c&&h.trigger("ajaxSend",[w,p]),p.async&&p.timeout>0&&(u=setTimeout(function(){w.abort("timeout")},p.timeout));try{x=1,i.send(v,r)}catch(N){if(!(2>x))throw N;r(-1,N)}}else r(-1,"No Transport");return w},getScript:function(e,n){return st.get(e,t,n,"script")},getJSON:function(e,t,n){return st.get(e,t,n,"json")}}),st.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return st.globalEval(e),e}}}),st.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),st.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=V.head||st("head")[0]||V.documentElement;return{send:function(t,i){n=V.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Xn=[],Un=/(=)\?(?=&|$)|\?\?/;st.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xn.pop()||st.expando+"_"+Ln++;return this[e]=!0,e}}),st.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Un.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Un.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=st.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Un,"$1"+o):n.jsonp!==!1&&(n.url+=(Hn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||st.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Xn.push(o)),s&&st.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Vn,Yn,Jn=0,Gn=e.ActiveXObject&&function(){var e;for(e in Vn)Vn[e](t,!0)};st.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&_()||F()}:_,Yn=st.ajaxSettings.xhr(),st.support.cors=!!Yn&&"withCredentials"in Yn,Yn=st.support.ajax=!!Yn,Yn&&st.ajaxTransport(function(n){if(!n.crossDomain||st.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,f,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=st.noop,Gn&&delete Vn[a]),i)4!==u.readyState&&u.abort();else{f={},s=u.status,p=u.responseXML,c=u.getAllResponseHeaders(),p&&p.documentElement&&(f.xml=p),"string"==typeof u.responseText&&(f.text=u.responseText);try{l=u.statusText}catch(d){l=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=f.text?200:404}}catch(h){i||o(-1,h)}f&&o(s,l,f,c)},n.async?4===u.readyState?setTimeout(r):(a=++Jn,Gn&&(Vn||(Vn={},st(e).unload(Gn)),Vn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Qn,Kn,Zn=/^(?:toggle|show|hide)$/,er=RegExp("^(?:([+-])=|)("+ut+")([a-z%]*)$","i"),tr=/queueHooks$/,nr=[W],rr={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=er.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(st.cssNumber[e]?"":"px"),"px"!==r&&s){s=st.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,st.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};st.Animation=st.extend(P,{tweener:function(e,t){st.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],rr[n]=rr[n]||[],rr[n].unshift(t)},prefilter:function(e,t){t?nr.unshift(e):nr.push(e)}}),st.Tween=$,$.prototype={constructor:$,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(st.cssNumber[n]?"":"px")},cur:function(){var e=$.propHooks[this.prop];return e&&e.get?e.get(this):$.propHooks._default.get(this)},run:function(e){var t,n=$.propHooks[this.prop];return this.pos=t=this.options.duration?st.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):$.propHooks._default.set(this),this}},$.prototype.init.prototype=$.prototype,$.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=st.css(e.elem,e.prop,"auto"),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){st.fx.step[e.prop]?st.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[st.cssProps[e.prop]]||st.cssHooks[e.prop])?st.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},$.propHooks.scrollTop=$.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},st.each(["toggle","show","hide"],function(e,t){var n=st.fn[t];st.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(I(t,!0),e,r,i)}}),st.fn.extend({fadeTo:function(e,t,n,r){return this.filter(w).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=st.isEmptyObject(e),o=st.speed(t,n,r),a=function(){var t=P(this,st.extend({},e),o);a.finish=function(){t.stop(!0)},(i||st._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=st.timers,a=st._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&tr.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&st.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=st._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=st.timers,a=r?r.length:0;for(n.finish=!0,st.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),st.each({slideDown:I("show"),slideUp:I("hide"),slideToggle:I("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){st.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),st.speed=function(e,t,n){var r=e&&"object"==typeof e?st.extend({},e):{complete:n||!n&&t||st.isFunction(e)&&e,duration:e,easing:n&&t||t&&!st.isFunction(t)&&t};return r.duration=st.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in st.fx.speeds?st.fx.speeds[r.duration]:st.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){st.isFunction(r.old)&&r.old.call(this),r.queue&&st.dequeue(this,r.queue)},r},st.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},st.timers=[],st.fx=$.prototype.init,st.fx.tick=function(){var e,n=st.timers,r=0;for(Qn=st.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||st.fx.stop(),Qn=t},st.fx.timer=function(e){e()&&st.timers.push(e)&&st.fx.start()},st.fx.interval=13,st.fx.start=function(){Kn||(Kn=setInterval(st.fx.tick,st.fx.interval))},st.fx.stop=function(){clearInterval(Kn),Kn=null},st.fx.speeds={slow:600,fast:200,_default:400},st.fx.step={},st.expr&&st.expr.filters&&(st.expr.filters.animated=function(e){return st.grep(st.timers,function(t){return e===t.elem}).length}),st.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){st.offset.setOffset(this,e,t)});var n,r,i={top:0,left:0},o=this[0],a=o&&o.ownerDocument;if(a)return n=a.documentElement,st.contains(n,o)?(o.getBoundingClientRect!==t&&(i=o.getBoundingClientRect()),r=z(a),{top:i.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:i.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):i},st.offset={setOffset:function(e,t,n){var r=st.css(e,"position");"static"===r&&(e.style.position="relative");var i,o,a=st(e),s=a.offset(),u=st.css(e,"top"),l=st.css(e,"left"),c=("absolute"===r||"fixed"===r)&&st.inArray("auto",[u,l])>-1,f={},p={};c?(p=a.position(),i=p.top,o=p.left):(i=parseFloat(u)||0,o=parseFloat(l)||0),st.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+i),null!=t.left&&(f.left=t.left-s.left+o),"using"in t?t.using.call(e,f):a.css(f)}},st.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===st.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),st.nodeName(e[0],"html")||(n=e.offset()),n.top+=st.css(e[0],"borderTopWidth",!0),n.left+=st.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-st.css(r,"marginTop",!0),left:t.left-n.left-st.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||V.documentElement;e&&!st.nodeName(e,"html")&&"static"===st.css(e,"position");)e=e.offsetParent;return e||V.documentElement})}}),st.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);st.fn[e]=function(i){return st.access(this,function(e,i,o){var a=z(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?st(a).scrollLeft():o,r?o:st(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}}),st.each({Height:"height",Width:"width"},function(e,n){st.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){st.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return st.access(this,function(n,r,i){var o;return st.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?st.css(n,r,s):st.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=st,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return st})})(window);
-//@ sourceMappingURL=jquery.min.map
\ No newline at end of file
diff --git a/example/padrino/test/test.rake b/example/padrino/test/test.rake
deleted file mode 100644
index e6910056e..000000000
--- a/example/padrino/test/test.rake
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'rake/testtask'
-
-test_tasks = Dir['test/*/'].map { |d| File.basename(d) }
-
-test_tasks.each do |folder|
- Rake::TestTask.new("test:#{folder}") do |test|
- test.pattern = "test/#{folder}/**/*_test.rb"
- test.verbose = true
- end
-end
-
-desc "Run application test suite"
-task 'test' => test_tasks.map { |f| "test:#{f}" }
diff --git a/example/padrino/test/test_config.rb b/example/padrino/test/test_config.rb
deleted file mode 100644
index 1dab63b95..000000000
--- a/example/padrino/test/test_config.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-RACK_ENV = 'test' unless defined?(RACK_ENV)
-require File.expand_path(File.dirname(__FILE__) + "/../config/boot")
-require 'mocha/api'
-# Specify your app using the #app helper inside a context.
-# Takes either an app class or a block argument.
- include Mocha::API
-# app { Padrino.application }
-# app { BugsnagPadrino::App.tap { |app| } }
-
-class Riot::Situation
- include Mocha::API
- include Rack::Test::Methods
-
- # You can use this method to custom specify a Rack app
- # you want rack-test to invoke:
- #
- # app BugsnagPadrino::App
- # app BugsnagPadrino::App.tap { |a| }
- # app(BugsnagPadrino::App) do
- # set :foo, :bar
- # end
- #
- def app(app = nil, &blk)
- @app ||= block_given? ? app.instance_eval(&blk) : app
- @app ||= Padrino.application
- end
-end
-
-class Riot::Context
- include Mocha::API
-end
-
diff --git a/example/que/Gemfile b/example/que/Gemfile
index b185c575f..6aa3bfb1c 100644
--- a/example/que/Gemfile
+++ b/example/que/Gemfile
@@ -3,4 +3,5 @@ source 'https://rubygems.org'
gem 'que'
gem 'activerecord'
gem 'pg'
+gem 'rake'
gem 'bugsnag', path: '../../'
diff --git a/example/que/README.md b/example/que/README.md
index 101c49761..8a4132985 100644
--- a/example/que/README.md
+++ b/example/que/README.md
@@ -1,15 +1,45 @@
-## Que example
+# Using Bugsnag with Que
-A small app demonstrating reporting an error to Bugsnag from a Que job.
+This example shows how to use Bugsnag in conjunction with Que to report any exceptions that occur in your applications.
-### Running the app
+First, install dependencies
+```shell
+bundle install
+```
-The included application has a single job which updates a user record if no
-error occurs (comment out the `raise` to see the intended effect).
+## Setting up a database
+Que requires a database backend in order to queue jobs. By default this database will be PostgreSQL although this can be changed via options as detailed in [the que documentation](https://github.com/chanks/que).
-* Install the dependencies: `bundle install`
-* Set the `DBNAME` environment variable to the name of the database to use
- (otherwise the postgresql default will be used)
-* Migrate the database: `bundle exec rake db:migrate`
-* Enqueue a job: `bundle exec rake job:enqueue`
-* Start a worker: `bundle exec rake job:work`
+Once PostgreSQL is set up as detailed using [the PostgreSQL documentation](https://www.postgresql.org/docs/), ensure Que can connect correctly by setting the environment variable `DBNAME` to the name of an existing PostgreSQL database.
+
+## Configuring Bugsnag with Que
+
+Bugsnag can be configured in one of two ways in your Que app:
+
+1. require `bugsnag` in your application and call `Bugsnag.configure` with a block, setting the appropriate configuration options:
+```ruby
+Bugsnag.configure do |config|
+ config.api_key = "YOUR_API_KEY"
+end
+```
+
+2. require `bugsnag` in your application and input configuration options through environment variables, such as setting `BUGSNAG_API_KEY` to `YOUR_API_KEY`.
+
+All configuration options can be found in the [Bugsnag documentation](https://docs.bugsnag.com/platforms/ruby/other/configuration-options/)
+
+## Running the example
+
+Run the database migration using rake:
+```shell
+bundle exec rake migrate:up
+```
+
+After this is complete, queue up our test job:
+```shell
+bundle exec rake job:enqueue
+```
+
+Then execute the worker and see the errors appear on the [Bugsnag dashboard](https://app.bugsnag.com):
+```shell
+bundle exec rake job:work
+```
diff --git a/example/que/Rakefile b/example/que/Rakefile
index ad8be21aa..77c657549 100644
--- a/example/que/Rakefile
+++ b/example/que/Rakefile
@@ -3,8 +3,7 @@ namespace :migrate do
task :up do
require_relative 'app/migrations'
CreateUsersTable.migrate(:up)
-
- require_relative 'app'
+ require_relative 'app/jobs'
Que.migrate!
end
diff --git a/example/que/app/jobs.rb b/example/que/app/jobs.rb
index d5e77e8a3..8f1b4a8e9 100644
--- a/example/que/app/jobs.rb
+++ b/example/que/app/jobs.rb
@@ -4,7 +4,7 @@
require 'bugsnag'
Bugsnag.configure do |config|
- config.api_key = 'f35a2472bd230ac0ab0f52715bbdc65d'
+ config.api_key = 'YOUR_API_KEY'
end
Que.connection = ActiveRecord
diff --git a/example/que/app/migrations.rb b/example/que/app/migrations.rb
index 498c95c38..6defc03d4 100644
--- a/example/que/app/migrations.rb
+++ b/example/que/app/migrations.rb
@@ -1,7 +1,7 @@
require 'active_record'
require_relative 'model'
-class CreateUsersTable < ActiveRecord::Migration
+class CreateUsersTable < ActiveRecord::Migration[5.1]
def up
create_table :users do |t|
t.string :name
diff --git a/example/rack/Gemfile b/example/rack/Gemfile
index dc9792492..42b669e90 100644
--- a/example/rack/Gemfile
+++ b/example/rack/Gemfile
@@ -1,7 +1,7 @@
source 'https://rubygems.org'
if ENV['INTEGRATION_LANGUAGE']
- gem 'bugsnag', path: '../../../../'
+ gem 'bugsnag', path: '../../'
else
gem 'bugsnag'
end
diff --git a/example/rack/README.md b/example/rack/README.md
index 2f34fefb5..74cfdffa1 100644
--- a/example/rack/README.md
+++ b/example/rack/README.md
@@ -1,70 +1,40 @@
# Bugsnag Rack demo
-This Rack application demonstrates how to use Bugsnag with Rack. Before testing
-it, open up the `config.ru` file and configure your API key (you can find the
-configuration block at the bottom of the file).
+This Rack application demonstrates how to use Bugsnag with Rack.
+Further details about integrating Bugsnag with Rack applications can be found [here.](https://docs.bugsnag.com/platforms/ruby/rack/)
-```
-Bugsnag.configure do |config|
- config.api_key = '0a6f5add590596f93a8d601ea89af841'
-end
-```
-
-The other way to configure the API key is to export the `BUGSNAG_API_KEY`
-environment variable.
-
-Activate the Bugsnag Rack middleware by wrapping the `BugsnagDemo` application
-(again, look for it at the bottom of `config.ru`).
-
-```
-Bugsnag::Rack.new(BugsnagDemo.new)
-```
+Install dependencies
-Make use you are not using `ShowExceptions`. Otherwise, in the development
-environment, automatic notifications won't work, as Rack would be swallowing
-exceptions from Bugsnag.
-
-Install dependencies.
-
-```
+```shell
bundle install
```
-Launch the Rack application.
+## Configuring Bugsnag and Rack
-```
-bundle exec ruby config.ru
-```
+1. Set up the Bugsnag configuration in ```server.rb``` by calling `Bugsnag.configure` and setting your API key and any other options [as detailed in the configuration options](https://docs.bugsnag.com/platforms/ruby/rack/configuration-options/):
+ ```ruby
+ Bugsnag.configure do |config|
+ config.api_key = 'YOUR_API_KEY'
+ end
+ ```
+
+2. Alternatively the API key can be set by exported it as a `BUGSNAG_API_KEY` environment variable.
-Next, open your project's dashboard on Bugsnag.
+3. To enable automatic notification in the Rack server the server object must be wrapped in an instance of `Bugsnag::Rack` which is then passed to the `Rack::Server` to be started:
+ ```ruby
+ server = BugsnagDemo.new
+ wrapped_app = Bugsnag::Rack.new(server)
+ Rack::Server.start(app: wrapped_app)
+ ```
-1. [crash](http://localhost:9292/crash)
-
-Crashes the application and sends a notification about the nature of the crash.
-Basically, almost any unhandled exception sends a notification to Bugsnag. See
-the line mentioning `get '/crash'`.
+4. Ensure that `ShowExceptions` is set to false, otherwise notifications will not be sent while in development mode.
-1. [crash and use callbacks](http://localhost:9292/crash_with_callback)
-
-Before crashing, the application would append the Diagnostics tab with some
-predefined information, attached by means of a callback. See the line mentioning
-`get '/crash_with_callback'`.
+## Running the example
-1. [notify](http://localhost:9292/notify)
-
-Bugsnag Ruby provides a way to send notifications on demand by means of
-`Bugsnag.notify`. This API allows to send notifications manually, without
-crashing your application. See the line mentioning `get '/notify'`.
+Run the example using:
-1. [notify with meta data](http://localhost:9292/notify_meta)
-
-Same as `notify`, but also attaches meta data. The meta data is any additional
-information you want to attach to an exception. In this artificial case
-additional information with be sent and displayed in a new tab called
-"Diagnostics". See the line mentioning `get '/notify_meta'`.
+```shell
+bundle exec ruby server.rb
+```
-1. [severity](http://localhost:9292/severity)
-
-Bugsnag supports three severities: 'error', 'warning' and 'info'. You can set
-the severity by passing one of these objects as a string to '#notify'. See the
-line mentioning `get '/severity'`.
+Once the server is running head to the default path for more information on Bugsnag logging examples.
\ No newline at end of file
diff --git a/example/rack/config.ru b/example/rack/server.rb
similarity index 80%
rename from example/rack/config.ru
rename to example/rack/server.rb
index 476edb7f5..c02887894 100644
--- a/example/rack/config.ru
+++ b/example/rack/server.rb
@@ -1,5 +1,4 @@
require 'rack'
-require 'rack/showexceptions'
require 'rack/request'
require 'rack/response'
@@ -8,7 +7,7 @@
Bugsnag.configure do |config|
- config.api_key = 'f35a2472bd230ac0ab0f52715bbdc65d'
+ config.api_key = 'YOUR_API_KEY'
end
class BugsnagDemo
@@ -20,12 +19,12 @@ def call(env)
raise RuntimeError.new('Bugsnag Rack demo says: It crashed! Go check ' +
'bugsnag.com for a new notification!')
when '/crash_with_callback'
- Bugsnag.before_notify_callbacks << proc { |notification|
+ Bugsnag.before_notify_callbacks << proc { |report|
new_tab = {
message: 'Rack demo says: Everything is great',
code: 200
}
- notification.add_tab(:diagnostics, new_tab)
+ report.add_tab(:diagnostics, new_tab)
}
msg = 'Bugsnag Rack demo says: It crashed! But, due to the attached callback' +
@@ -38,36 +37,36 @@ def call(env)
text = "Bugsnag Rack demo says: It didn't crash! " +
'But still go check
https://bugsnag.com' +
' for a new notification.'
- when '/notify_meta'
- meta_data = {
- :user => {
+ when '/notify_data'
+ error = RuntimeError.new("Bugsnag Rack demo says: False alarm, your application didn't crash")
+ Bugsnag.notify error do |report|
+ report.add_tab(:user, {
:username => "bob-hoskins",
:email => 'bugsnag@bugsnag.com',
:registered_user => true
- },
-
- :diagnostics => {
+ })
+ report.add_tab(:diagnostics, {
:message => 'Rack demo says: Everything is great',
:code => 200
- }
- }
- error = RuntimeError.new("Bugsnag Rack demo says: False alarm, your application didn't crash")
- Bugsnag.notify(error, meta_data)
+ })
+ end
text = "Bugsnag Rack demo says: It didn't crash! " +
'But still go check
https://bugsnag.com' +
' for a new notification. Check out the User tab for the meta data'
- when '/severity'
+ when '/notify_severity'
msg = "Bugsnag Rack demo says: Look at the circle on the right side. It's different"
error = RuntimeError.new(msg)
- Bugsnag.notify(error, severity: 'info')
- msg
+ Bugsnag.notify error do |report|
+ report.severity = 'info'
+ end
+ text = msg
else
opts = {
fenced_code_blocks: true
}
renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML, opts)
- text = renderer.render(File.read(File.expand_path('README.md')))
+ text = renderer.render(File.read(File.expand_path('templates/index.md')))
end
res = Rack::Response.new
diff --git a/example/rack/templates/index.md b/example/rack/templates/index.md
new file mode 100644
index 000000000..52695efe2
--- /dev/null
+++ b/example/rack/templates/index.md
@@ -0,0 +1,25 @@
+# Bugsnag Rack demo
+
+This application demonstrates the use of Bugsnag with the Rack web framework.
+
+While testing the examples open [your dashboard](https://app.bugsnag.com) in order to see the example errors and exceptions being received.
+
+1. [Crash](/crash)
+
+ Raises an error within the framework, generating a report in the Bugsnag dashboard.
+
+2. [Crash and use callbacks](/crash_with_callback)
+
+ Raises an exception within the framework, but with additional data attached to the report. By registering a callback before the error occurs useful data can be attached as a tab in the Bugsnag dashboard.
+
+3. [Notify](/notify)
+
+ Sends Bugsnag a report on demand using `bugsnag.notify`. Allows details of handled errors or information to be sent to the Bugsnag dashboard without crashing your code.
+
+4. [Notify with data](/notify_data)
+
+ Same as `notify` but allows you to attach additional data within a `block`, similar to the `before_notify_callbacks` example above. In this case we're adding information about the user to go into the `user` tab, and additional diagnostics as a `diagnostics` tab.
+
+5. [Set the severity](/notify_severity)
+
+ This uses the same mechanism as adding meta-data, but allows you to set he `severity` when notifying Bugsnag of the error. Valid severities are `error`, `warning`, and `info`. Have a look on the dashboard to see the difference in these severities.
\ No newline at end of file
diff --git a/example/rails-32/.gitignore b/example/rails-32/.gitignore
deleted file mode 100644
index eb3489a98..000000000
--- a/example/rails-32/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-# See http://help.github.com/ignore-files/ for more about ignoring files.
-#
-# If you find yourself ignoring temporary files generated by your text editor
-# or operating system, you probably want to add a global ignore instead:
-# git config --global core.excludesfile ~/.gitignore_global
-
-# Ignore bundler config
-/.bundle
-
-# Ignore the default SQLite database.
-/db/*.sqlite3
-
-# Ignore all logfiles and tempfiles.
-/log/*.log
-/tmp
diff --git a/example/rails-32/Gemfile b/example/rails-32/Gemfile
index 0cd5ea4b9..09a043f5b 100644
--- a/example/rails-32/Gemfile
+++ b/example/rails-32/Gemfile
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
-gem 'rails', '3.2.13'
+gem 'rails', '3.2.22.5'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
@@ -37,3 +37,6 @@ gem 'jquery-rails'
# To use debugger
# gem 'debugger'
+
+# Added at 2017-09-28 14:54:05 +0100 by amoinet:
+gem "redcarpet", "~> 3.4"
diff --git a/example/rails-32/README.md b/example/rails-32/README.md
new file mode 100644
index 000000000..a548534ec
--- /dev/null
+++ b/example/rails-32/README.md
@@ -0,0 +1,33 @@
+# Bugsnag Rails v3.2 demo
+
+This Rails application demonstrates how to use Bugsnag with Rails v3.2.
+Further details about integrating Bugsnag with Rack applications can be found [here.](https://docs.bugsnag.com/platforms/ruby/rails/)
+
+Install dependencies
+
+```shell
+bundle install
+```
+
+## Configuring Bugsnag and Rails v3.2
+
+There are two methods of configuring Bugsnag within a Rails application:
+
+1. Your `API_KEY` can be exported as an environment variable `BUGSNAG_API_KEY`.
+
+2. Generate a bugsnag configuration file at ```config/initializers/bugsnag.rb``` which can be populated with the [available configuration options](https://docs.bugsnag.com/platforms/ruby/rails/configuration-options/) by running the rails command:
+ ```shell
+ rails generate bugsnag YOUR_API_KEY_HERE
+ ```
+
+This is sufficient to start reporting unhandled exceptions to Bugsnag.
+
+## Running the example
+
+Run the example using:
+
+```shell
+bundle exec rails server
+```
+
+Once the server is running head to the default path for more information on Bugsnag logging examples.
diff --git a/example/rails-32/README.rdoc b/example/rails-32/README.rdoc
deleted file mode 100644
index 3e1c15c81..000000000
--- a/example/rails-32/README.rdoc
+++ /dev/null
@@ -1,261 +0,0 @@
-== Welcome to Rails
-
-Rails is a web-application framework that includes everything needed to create
-database-backed web applications according to the Model-View-Control pattern.
-
-This pattern splits the view (also called the presentation) into "dumb"
-templates that are primarily responsible for inserting pre-built data in between
-HTML tags. The model contains the "smart" domain objects (such as Account,
-Product, Person, Post) that holds all the business logic and knows how to
-persist themselves to a database. The controller handles the incoming requests
-(such as Save New Account, Update Product, Show Post) by manipulating the model
-and directing data to the view.
-
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
-
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
-
-
-== Getting Started
-
-1. At the command prompt, create a new Rails application:
-
rails new myapp (where
myapp is the application name)
-
-2. Change directory to
myapp and start the web server:
-
cd myapp; rails server (run with --help for options)
-
-3. Go to http://localhost:3000/ and you'll see:
- "Welcome aboard: You're riding Ruby on Rails!"
-
-4. Follow the guidelines to start developing your application. You can find
-the following resources handy:
-
-* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
-* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
-
-
-== Debugging Rails
-
-Sometimes your application goes wrong. Fortunately there are a lot of tools that
-will help you debug it and get it back on the rails.
-
-First area to check is the application log files. Have "tail -f" commands
-running on the server.log and development.log. Rails will automatically display
-debugging and runtime information to these files. Debugging info will also be
-shown in the browser on requests from 127.0.0.1.
-
-You can also log your own messages directly into the log file from your code
-using the Ruby logger class from inside your controllers. Example:
-
- class WeblogController < ActionController::Base
- def destroy
- @weblog = Weblog.find(params[:id])
- @weblog.destroy
- logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
- end
- end
-
-The result will be a message in your log file along the lines of:
-
- Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
-
-More information on how to use the logger is at http://www.ruby-doc.org/core/
-
-Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
-several books available online as well:
-
-* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
-* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
-
-These two books will bring you up to speed on the Ruby language and also on
-programming in general.
-
-
-== Debugger
-
-Debugger support is available through the debugger command when you start your
-Mongrel or WEBrick server with --debugger. This means that you can break out of
-execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install ruby-debug to run the server in debugging
-mode. With gems, use
sudo gem install ruby-debug. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.all
- debugger
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the server window. Here you can do things like:
-
- >> @posts.inspect
- => "[#
nil, "body"=>nil, "id"=>"1"}>,
- #"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
- >> @posts.first.title = "hello from a debugger"
- => "hello from a debugger"
-
-...and even better, you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you can enter "cont".
-
-
-== Console
-
-The console is a Ruby shell, which allows you to interact with your
-application's domain model. Here you'll have all parts of the application
-configured, just like it is when the application is running. You can inspect
-domain models, change values, and save to the database. Starting the script
-without arguments will launch it in the development environment.
-
-To start the console, run rails console from the application
-directory.
-
-Options:
-
-* Passing the -s, --sandbox argument will rollback any modifications
- made to the database.
-* Passing an environment name as an argument will load the corresponding
- environment. Example: rails console production.
-
-To reload your controllers and models after launching the console run
-reload!
-
-More information about irb can be found at:
-link:http://www.rubycentral.org/pickaxe/irb.html
-
-
-== dbconsole
-
-You can go to the command line of your database directly through rails
-dbconsole. You would be connected to the database with the credentials
-defined in database.yml. Starting the script without arguments will connect you
-to the development database. Passing an argument will connect you to a different
-database, like rails dbconsole production. Currently works for MySQL,
-PostgreSQL and SQLite 3.
-
-== Description of Contents
-
-The default directory structure of a generated Ruby on Rails application:
-
- |-- app
- | |-- assets
- | | |-- images
- | | |-- javascripts
- | | `-- stylesheets
- | |-- controllers
- | |-- helpers
- | |-- mailers
- | |-- models
- | `-- views
- | `-- layouts
- |-- config
- | |-- environments
- | |-- initializers
- | `-- locales
- |-- db
- |-- doc
- |-- lib
- | |-- assets
- | `-- tasks
- |-- log
- |-- public
- |-- script
- |-- test
- | |-- fixtures
- | |-- functional
- | |-- integration
- | |-- performance
- | `-- unit
- |-- tmp
- | `-- cache
- | `-- assets
- `-- vendor
- |-- assets
- | |-- javascripts
- | `-- stylesheets
- `-- plugins
-
-app
- Holds all the code that's specific to this particular application.
-
-app/assets
- Contains subdirectories for images, stylesheets, and JavaScript files.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from
- ApplicationController which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb. Models descend from
- ActiveRecord::Base by default.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.html.erb for the WeblogsController#index action. All views use
- eRuby syntax by default.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the
- common header/footer method of wrapping views. In your views, define a layout
- using the layout :default and create a file named default.html.erb.
- Inside default.html.erb, call <% yield %> to render the view using this
- layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are
- generated for you automatically when using generators for controllers.
- Helpers can be used to wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database,
- and other dependencies.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all the
- sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when
- generated using rake doc:app
-
-lib
- Application specific libraries. Basically, any kind of custom code that
- doesn't belong under controllers, models, or helpers. This directory is in
- the load path.
-
-public
- The directory available for the web server. Also contains the dispatchers and the
- default HTML files. This should be set as the DOCUMENT_ROOT of your web
- server.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures. When using the rails generate
- command, template test files will be generated for you and placed in this
- directory.
-
-vendor
- External libraries that the application depends on. Also includes the plugins
- subdirectory. If the app has frozen rails, those gems also go here, under
- vendor/rails/. This directory is in the load path.
diff --git a/example/rails-32/app/controllers/application_controller.rb b/example/rails-32/app/controllers/application_controller.rb
index 473548d74..4d2f430b3 100644
--- a/example/rails-32/app/controllers/application_controller.rb
+++ b/example/rails-32/app/controllers/application_controller.rb
@@ -2,6 +2,59 @@ class ApplicationController < ActionController::Base
protect_from_forgery
def index
- raise "Yo"
+ @text = File.read(File.expand_path('app/views/index.md'))
end
+
+ def crash
+ raise Exception.new('Bugsnag Rails demo says: It crashed! Go check ' +
+ 'bugsnag.com for a new notification')
+ end
+
+ def callback
+ Bugsnag.before_notify_callbacks << proc { |report|
+ new_tab = {
+ message: 'Rails v3.2 demo says: Everything is great',
+ code: 200
+ }
+ report.add_tab(:diagnostics, new_tab)
+ }
+ raise RuntimeError.new('Bugsnag Rails demo says: It crashed! But, due to the attached callback' +
+ ' the exception has meta information. Go check' +
+ ' bugsnag.com for a new notification (see the Diagnostics tab)!')
+ end
+
+ def notify
+ Bugsnag.notify(RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash"))
+ @text = "Bugsnag Rack demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification.'
+ end
+
+ def data
+ error = RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash")
+ Bugsnag.notify error do |report|
+ report.add_tab(:user, {
+ :username => "bob-hoskins",
+ :email => 'bugsnag@bugsnag.com',
+ :registered_user => true
+ })
+ report.add_tab(:diagnostics, {
+ :message => 'Rails demo says: Everything is great',
+ :code => 200
+ })
+ end
+ @text = "Bugsnag Rails demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification. Check out the User tab for the meta data'
+ end
+
+ def severity
+ msg = "Bugsnag Rails demo says: Look at the circle on the right side. It's different"
+ error = RuntimeError.new(msg)
+ Bugsnag.notify error do |report|
+ report.severity = 'info'
+ end
+ @text = msg
+ end
+
end
diff --git a/example/rails-32/app/helpers/application_helper.rb b/example/rails-32/app/helpers/application_helper.rb
index de6be7945..6d77519db 100644
--- a/example/rails-32/app/helpers/application_helper.rb
+++ b/example/rails-32/app/helpers/application_helper.rb
@@ -1,2 +1,22 @@
module ApplicationHelper
+ def markdown(text)
+ options = {
+ filter_html: true,
+ hard_wrap: true,
+ link_attributes: { rel: 'nofollow', target: "_blank" },
+ space_after_headers: true,
+ fenced_code_blocks: true
+ }
+
+ extensions = {
+ autolink: true,
+ superscript: true,
+ disable_indented_code_blocks: true
+ }
+
+ renderer = Redcarpet::Render::HTML.new(options)
+ markdown = Redcarpet::Markdown.new(renderer, extensions)
+
+ markdown.render(text).html_safe
+ end
end
diff --git a/example/rails-32/app/views/application/data.html.erb b/example/rails-32/app/views/application/data.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-32/app/views/application/data.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-32/app/views/application/index.html.erb b/example/rails-32/app/views/application/index.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-32/app/views/application/index.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-32/app/views/application/notify.html.erb b/example/rails-32/app/views/application/notify.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-32/app/views/application/notify.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-32/app/views/application/severity.html.erb b/example/rails-32/app/views/application/severity.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-32/app/views/application/severity.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-32/app/views/index.md b/example/rails-32/app/views/index.md
new file mode 100644
index 000000000..efbc05be3
--- /dev/null
+++ b/example/rails-32/app/views/index.md
@@ -0,0 +1,25 @@
+# Bugsnag Rails v3.2 demo
+
+This application demonstrates the use of Bugsnag with the Rails web framework.
+
+While testing the examples open [your dashboard](https://app.bugsnag.com) in order to see the example errors and exceptions being received.
+
+1. [Crash](/crash)
+
+ Raises an error within the framework, generating a report in the Bugsnag dashboard.
+
+2. [Crash and use callbacks](/crash_with_callback)
+
+ Raises an exception within the framework, but with additional data attached to the report. By registering a callback before the error occurs useful data can be attached as a tab in the Bugsnag dashboard.
+
+3. [Notify](/notify)
+
+ Sends Bugsnag a report on demand using `bugsnag.notify`. Allows details of handled errors or information to be sent to the Bugsnag dashboard without crashing your code.
+
+4. [Notify with data](/notify_data)
+
+ Same as `notify` but allows you to attach additional data within a `block`, similar to the `before_notify_callbacks` example above. In this case we're adding information about the user to go into the `user` tab, and additional diagnostics as a `diagnostics` tab.
+
+5. [Set the severity](/notify_severity)
+
+ This uses the same mechanism as adding meta-data, but allows you to set he `severity` when notifying Bugsnag of the error. Valid severities are `error`, `warning`, and `info`. Have a look on the dashboard to see the difference in these severities.
\ No newline at end of file
diff --git a/example/rails-32/config/initializers/bugsnag.rb b/example/rails-32/config/initializers/bugsnag.rb
deleted file mode 100644
index 0f3db4807..000000000
--- a/example/rails-32/config/initializers/bugsnag.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugsnag.configure do |config|
- config.api_key = "066f5ad3590596f9aa8d601ea89af845"
-end
diff --git a/example/rails-32/config/routes.rb b/example/rails-32/config/routes.rb
index b1b5ac3f6..d657db6c3 100644
--- a/example/rails-32/config/routes.rb
+++ b/example/rails-32/config/routes.rb
@@ -49,6 +49,11 @@
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
root :to => 'application#index'
+ match 'crash' => 'application#crash'
+ match 'crash_with_callback' => 'application#callback'
+ match 'notify' => 'application#notify'
+ match 'notify_data' => 'application#data'
+ match 'notify_severity' => 'application#severity'
# See how all your routes lay out with "rake routes"
diff --git a/example/rails-32/db/development.sqlite3 b/example/rails-32/db/development.sqlite3
new file mode 100644
index 000000000..85ab63529
Binary files /dev/null and b/example/rails-32/db/development.sqlite3 differ
diff --git a/example/rails-32/db/seeds.rb b/example/rails-32/db/seeds.rb
deleted file mode 100644
index 4edb1e857..000000000
--- a/example/rails-32/db/seeds.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file should contain all the record creation needed to seed the database with its default values.
-# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
-#
-# Examples:
-#
-# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
-# Mayor.create(name: 'Emanuel', city: cities.first)
diff --git a/example/rails-32/lib/tasks/.gitkeep b/example/rails-32/lib/tasks/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-32/log/.gitkeep b/example/rails-32/log/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-32/test/fixtures/.gitkeep b/example/rails-32/test/fixtures/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-32/test/functional/.gitkeep b/example/rails-32/test/functional/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-32/test/integration/.gitkeep b/example/rails-32/test/integration/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-32/test/performance/browsing_test.rb b/example/rails-32/test/performance/browsing_test.rb
deleted file mode 100644
index 3fea27b91..000000000
--- a/example/rails-32/test/performance/browsing_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class BrowsingTest < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
- # :output => 'tmp/performance', :formats => [:flat] }
-
- def test_homepage
- get '/'
- end
-end
diff --git a/example/rails-32/test/test_helper.rb b/example/rails-32/test/test_helper.rb
deleted file mode 100644
index 8bf1192ff..000000000
--- a/example/rails-32/test/test_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-ENV["RAILS_ENV"] = "test"
-require File.expand_path('../../config/environment', __FILE__)
-require 'rails/test_help'
-
-class ActiveSupport::TestCase
- # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
- #
- # Note: You'll currently still have to declare fixtures explicitly in integration tests
- # -- they do not yet inherit this setting
- fixtures :all
-
- # Add more helper methods to be used by all tests here...
-end
diff --git a/example/rails-32/test/unit/.gitkeep b/example/rails-32/test/unit/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/Gemfile b/example/rails-42/Gemfile
index cbd91c464..044605255 100644
--- a/example/rails-42/Gemfile
+++ b/example/rails-42/Gemfile
@@ -45,3 +45,9 @@ group :development, :test do
end
gem "sidekiq"
+
+# Added at 2017-09-29 12:13:32 +0100 by amoinet:
+gem "redcarpet", "~> 3.4"
+
+gem 'delayed_job_active_record'
+gem 'daemons'
\ No newline at end of file
diff --git a/example/rails-42/README.md b/example/rails-42/README.md
index 7779b0afc..204eaa424 100644
--- a/example/rails-42/README.md
+++ b/example/rails-42/README.md
@@ -1,9 +1,58 @@
-# rails 4.2 example
+# Bugsnag Rails v4.2 demo (with delayed_job)
-## Running the app
+This Rails application demonstrates how to use Bugsnag with Rails v4.2.
+Further details about integrating Bugsnag with Rack applications can be found [here.](https://docs.bugsnag.com/platforms/ruby/rails/)
-* Set your API key in `config/initializers/bugsnag.rb`
-* Install the dependencies: `bundle install`
-* Migrate the database: `bundle exec rake db:migrate`
-* Run the app: `bundle exec rackup`
-* Navigate to a web browser and open `localhost:9292` to see an error
+Install dependencies
+
+```shell
+bundle install
+```
+
+## Configuring Bugsnag and Rails v4.2
+
+There are two methods of configuring Bugsnag within a Rails application:
+
+1. Your `API_KEY` can be exported as an environment variable `BUGSNAG_API_KEY`.
+
+2. Generate a bugsnag configuration file at ```config/initializers/bugsnag.rb``` which can be populated with the [available configuration options](https://docs.bugsnag.com/platforms/ruby/rails/configuration-options/) by running the rails command:
+ ```shell
+ rails generate bugsnag YOUR_API_KEY_HERE
+ ```
+
+This is sufficient to start reporting unhandled exceptions to Bugsnag.
+
+## Running the example
+
+Run the example using:
+
+```shell
+bundle exec rails server
+```
+
+Once the server is running head to the default path for more information on Bugsnag logging examples.
+
+# Running delayed job
+
+This example comes packaged with a delayed job setup to demonstrate how errors are reported through delayed job. Once the rails setup is complete with an initializer at ```config/initializers/bugsnag.rb``` you don't need to do anything else with delayed job, just run the examples!
+
+The examples can be found at ```app/helpers/test_delayed_job_helper```
+
+In order to run the delayed job example:
+
+1. Open the rails console using the command:
+```shell
+bundle exec bin/rails console
+```
+
+2. Queue up the examples you wish to run. At the moment there are two examples `crash` and `notify`, which are queued by passing the symbol to the `helper.test_dj` like:
+```ruby
+helper.test_dj :crash
+helper.test_dj :notify
+```
+
+3. Exit the rails console using the `exit` command.
+4. Run the queue using rake:
+```shell
+bundle exec rake jobs:work
+```
diff --git a/example/rails-42/app/controllers/application_controller.rb b/example/rails-42/app/controllers/application_controller.rb
index 55f1e3588..33f2701ff 100644
--- a/example/rails-42/app/controllers/application_controller.rb
+++ b/example/rails-42/app/controllers/application_controller.rb
@@ -1,9 +1,59 @@
class ApplicationController < ActionController::Base
- # Prevent CSRF attacks by raising an exception.
- # For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def index
- raise "YO"
+ @text = File.read(File.expand_path('app/views/index.md'))
+ end
+
+ def crash
+ raise Exception.new('Bugsnag Rails demo says: It crashed! Go check ' +
+ 'bugsnag.com for a new notification')
+ end
+
+ def callback
+ Bugsnag.before_notify_callbacks << proc { |report|
+ new_tab = {
+ message: 'Rails v4.2 demo says: Everything is great',
+ code: 200
+ }
+ report.add_tab(:diagnostics, new_tab)
+ }
+ raise RuntimeError.new('Bugsnag Rails demo says: It crashed! But, due to the attached callback' +
+ ' the exception has meta information. Go check' +
+ ' bugsnag.com for a new notification (see the Diagnostics tab)!')
+ end
+
+ def notify
+ Bugsnag.notify(RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash"))
+ @text = "Bugsnag Rack demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification.'
+ end
+
+ def data
+ error = RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash")
+ Bugsnag.notify error do |report|
+ report.add_tab(:user, {
+ :username => "bob-hoskins",
+ :email => 'bugsnag@bugsnag.com',
+ :registered_user => true
+ })
+ report.add_tab(:diagnostics, {
+ :message => 'Rails demo says: Everything is great',
+ :code => 200
+ })
+ end
+ @text = "Bugsnag Rails demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification. Check out the User tab for the meta data'
+ end
+
+ def severity
+ msg = "Bugsnag Rails demo says: Look at the circle on the right side. It's different"
+ error = RuntimeError.new(msg)
+ Bugsnag.notify error do |report|
+ report.severity = 'info'
+ end
+ @text = msg
end
end
diff --git a/example/rails-42/app/helpers/application_helper.rb b/example/rails-42/app/helpers/application_helper.rb
index de6be7945..6d77519db 100644
--- a/example/rails-42/app/helpers/application_helper.rb
+++ b/example/rails-42/app/helpers/application_helper.rb
@@ -1,2 +1,22 @@
module ApplicationHelper
+ def markdown(text)
+ options = {
+ filter_html: true,
+ hard_wrap: true,
+ link_attributes: { rel: 'nofollow', target: "_blank" },
+ space_after_headers: true,
+ fenced_code_blocks: true
+ }
+
+ extensions = {
+ autolink: true,
+ superscript: true,
+ disable_indented_code_blocks: true
+ }
+
+ renderer = Redcarpet::Render::HTML.new(options)
+ markdown = Redcarpet::Markdown.new(renderer, extensions)
+
+ markdown.render(text).html_safe
+ end
end
diff --git a/example/rails-42/app/helpers/test_delayed_job_helper.rb b/example/rails-42/app/helpers/test_delayed_job_helper.rb
new file mode 100644
index 000000000..61997c092
--- /dev/null
+++ b/example/rails-42/app/helpers/test_delayed_job_helper.rb
@@ -0,0 +1,23 @@
+require "bugsnag"
+
+module TestDelayedJobHelper
+ def test_dj(func)
+ case func
+ when :crash
+ Testers.delay.crash
+ when :notify
+ Testers.delay.notify
+ end
+ end
+
+ class Testers
+ def self.crash
+ raise StandardError
+ end
+
+ def self.notify
+ Bugsnag.notify(StandardError.new "notify error")
+ end
+ end
+end
+
diff --git a/example/rails-42/app/views/application/data.html.erb b/example/rails-42/app/views/application/data.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-42/app/views/application/data.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-42/app/views/application/index.html.erb b/example/rails-42/app/views/application/index.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-42/app/views/application/index.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-42/app/views/application/notify.html.erb b/example/rails-42/app/views/application/notify.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-42/app/views/application/notify.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-42/app/views/application/severity.html.erb b/example/rails-42/app/views/application/severity.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-42/app/views/application/severity.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-42/app/views/index.md b/example/rails-42/app/views/index.md
new file mode 100644
index 000000000..4b37c029c
--- /dev/null
+++ b/example/rails-42/app/views/index.md
@@ -0,0 +1,25 @@
+# Bugsnag Rails v4.2 demo
+
+This application demonstrates the use of Bugsnag with the Rails web framework.
+
+While testing the examples open [your dashboard](https://app.bugsnag.com) in order to see the example errors and exceptions being received.
+
+1. [Crash](/crash)
+
+ Raises an error within the framework, generating a report in the Bugsnag dashboard.
+
+2. [Crash and use callbacks](/crash_with_callback)
+
+ Raises an exception within the framework, but with additional data attached to the report. By registering a callback before the error occurs useful data can be attached as a tab in the Bugsnag dashboard.
+
+3. [Notify](/notify)
+
+ Sends Bugsnag a report on demand using `bugsnag.notify`. Allows details of handled errors or information to be sent to the Bugsnag dashboard without crashing your code.
+
+4. [Notify with data](/notify_data)
+
+ Same as `notify` but allows you to attach additional data within a `block`, similar to the `before_notify_callbacks` example above. In this case we're adding information about the user to go into the `user` tab, and additional diagnostics as a `diagnostics` tab.
+
+5. [Set the severity](/notify_severity)
+
+ This uses the same mechanism as adding meta-data, but allows you to set he `severity` when notifying Bugsnag of the error. Valid severities are `error`, `warning`, and `info`. Have a look on the dashboard to see the difference in these severities.
\ No newline at end of file
diff --git a/example/rails-42/app/workers/hard_worker.rb b/example/rails-42/app/workers/hard_worker.rb
deleted file mode 100644
index becd8ed0a..000000000
--- a/example/rails-42/app/workers/hard_worker.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class HardWorker
- include Sidekiq::Worker
- def perform(how_hard, how_long)
- puts "Workin' #{how_hard} #{how_long}"
- raise 'Uh oh!'
- end
-end
-
-#HardWorker.perform_async('bob', 5)
diff --git a/example/rails-42/config/application.rb b/example/rails-42/config/application.rb
index 610d941ac..9dc198d1a 100644
--- a/example/rails-42/config/application.rb
+++ b/example/rails-42/config/application.rb
@@ -22,5 +22,6 @@ class Application < Rails::Application
# Do not swallow errors in after_commit/after_rollback callbacks.
config.active_record.raise_in_transactional_callbacks = true
+ config.active_job.queue_adapter = :delayed_job
end
end
diff --git a/example/rails-42/config/initializers/bugsnag.rb b/example/rails-42/config/initializers/bugsnag.rb
deleted file mode 100644
index 0f3db4807..000000000
--- a/example/rails-42/config/initializers/bugsnag.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugsnag.configure do |config|
- config.api_key = "066f5ad3590596f9aa8d601ea89af845"
-end
diff --git a/example/rails-42/config/routes.rb b/example/rails-42/config/routes.rb
index 79275189e..9a3a1b9f0 100644
--- a/example/rails-42/config/routes.rb
+++ b/example/rails-42/config/routes.rb
@@ -1,58 +1,10 @@
Rails.application.routes.draw do
root :to => 'application#index'
- # The priority is based upon order of creation: first created -> highest priority.
- # See how all your routes lay out with "rake routes".
+ get 'crash' => 'application#crash'
+ get 'crash_with_callback' => 'application#callback'
+ get 'notify' => 'application#notify'
+ get 'notify_data' => 'application#data'
+ get 'notify_severity' => 'application#severity'
- # You can have the root of your site routed with "root"
- # root 'welcome#index'
-
- # Example of regular route:
- # get 'products/:id' => 'catalog#view'
-
- # Example of named route that can be invoked with purchase_url(id: product.id)
- # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
-
- # Example resource route (maps HTTP verbs to controller actions automatically):
- # resources :products
-
- # Example resource route with options:
- # resources :products do
- # member do
- # get 'short'
- # post 'toggle'
- # end
- #
- # collection do
- # get 'sold'
- # end
- # end
-
- # Example resource route with sub-resources:
- # resources :products do
- # resources :comments, :sales
- # resource :seller
- # end
-
- # Example resource route with more complex sub-resources:
- # resources :products do
- # resources :comments
- # resources :sales do
- # get 'recent', on: :collection
- # end
- # end
-
- # Example resource route with concerns:
- # concern :toggleable do
- # post 'toggle'
- # end
- # resources :posts, concerns: :toggleable
- # resources :photos, concerns: :toggleable
-
- # Example resource route within a namespace:
- # namespace :admin do
- # # Directs /admin/products/* to Admin::ProductsController
- # # (app/controllers/admin/products_controller.rb)
- # resources :products
- # end
end
diff --git a/example/rails-42/db/migrate/20171006132129_create_delayed_jobs.rb b/example/rails-42/db/migrate/20171006132129_create_delayed_jobs.rb
new file mode 100644
index 000000000..27fdcf6cc
--- /dev/null
+++ b/example/rails-42/db/migrate/20171006132129_create_delayed_jobs.rb
@@ -0,0 +1,22 @@
+class CreateDelayedJobs < ActiveRecord::Migration
+ def self.up
+ create_table :delayed_jobs, force: true do |table|
+ table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
+ table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
+ table.text :handler, null: false # YAML-encoded string of the object that will do work
+ table.text :last_error # reason for last failure (See Note below)
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
+ table.datetime :locked_at # Set when a client is working on this object
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
+ table.string :locked_by # Who is working on this object (if locked)
+ table.string :queue # The name of the queue this job is in
+ table.timestamps null: true
+ end
+
+ add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
+ end
+
+ def self.down
+ drop_table :delayed_jobs
+ end
+end
diff --git a/example/rails-42/db/schema.rb b/example/rails-42/db/schema.rb
new file mode 100644
index 000000000..06777146f
--- /dev/null
+++ b/example/rails-42/db/schema.rb
@@ -0,0 +1,32 @@
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema.define(version: 20171006132129) do
+
+ create_table "delayed_jobs", force: :cascade do |t|
+ t.integer "priority", default: 0, null: false
+ t.integer "attempts", default: 0, null: false
+ t.text "handler", null: false
+ t.text "last_error"
+ t.datetime "run_at"
+ t.datetime "locked_at"
+ t.datetime "failed_at"
+ t.string "locked_by"
+ t.string "queue"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority"
+
+end
diff --git a/example/rails-42/lib/assets/.keep b/example/rails-42/lib/assets/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/lib/tasks/.keep b/example/rails-42/lib/tasks/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/lib/tasks/test.rake b/example/rails-42/lib/tasks/test.rake
deleted file mode 100644
index dcffebe04..000000000
--- a/example/rails-42/lib/tasks/test.rake
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace :test do
- task :uncaught => :environment do
- raise "Uncaught rake"
- end
-end
diff --git a/example/rails-42/log/.keep b/example/rails-42/log/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/controllers/.keep b/example/rails-42/test/controllers/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/fixtures/.keep b/example/rails-42/test/fixtures/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/helpers/.keep b/example/rails-42/test/helpers/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/integration/.keep b/example/rails-42/test/integration/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/mailers/.keep b/example/rails-42/test/mailers/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/models/.keep b/example/rails-42/test/models/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/example/rails-42/test/test_helper.rb b/example/rails-42/test/test_helper.rb
deleted file mode 100644
index 92e39b2d7..000000000
--- a/example/rails-42/test/test_helper.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-ENV['RAILS_ENV'] ||= 'test'
-require File.expand_path('../../config/environment', __FILE__)
-require 'rails/test_help'
-
-class ActiveSupport::TestCase
- # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
- fixtures :all
-
- # Add more helper methods to be used by all tests here...
-end
diff --git a/example/rails-51/.gitignore b/example/rails-51/.gitignore
new file mode 100644
index 000000000..dcc5b36f3
--- /dev/null
+++ b/example/rails-51/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile '~/.gitignore_global'
+
+# Ignore bundler config.
+/.bundle
+
+# Ignore the default SQLite database.
+/db/*.sqlite3
+/db/*.sqlite3-journal
+
+# Ignore all logfiles and tempfiles.
+/log/*
+/tmp/*
+!/log/.keep
+!/tmp/.keep
+
+/node_modules
+/yarn-error.log
+
+.byebug_history
diff --git a/example/rails-51/Gemfile b/example/rails-51/Gemfile
new file mode 100644
index 000000000..465e96fdc
--- /dev/null
+++ b/example/rails-51/Gemfile
@@ -0,0 +1,57 @@
+source 'https://rubygems.org'
+
+git_source(:github) do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ "https://github.com/#{repo_name}.git"
+end
+
+gem "bugsnag" , path: "../../"
+
+# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
+gem 'rails', '~> 5.1.4'
+# Use sqlite3 as the database for Active Record
+gem 'sqlite3'
+# Use Puma as the app server
+gem 'puma', '~> 3.7'
+# Use SCSS for stylesheets
+gem 'sass-rails', '~> 5.0'
+# Use Uglifier as compressor for JavaScript assets
+gem 'uglifier', '>= 1.3.0'
+# See https://github.com/rails/execjs#readme for more supported runtimes
+# gem 'therubyracer', platforms: :ruby
+
+# Use CoffeeScript for .coffee assets and views
+gem 'coffee-rails', '~> 4.2'
+# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
+gem 'turbolinks', '~> 5'
+# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
+gem 'jbuilder', '~> 2.5'
+# Use Redis adapter to run Action Cable in production
+# gem 'redis', '~> 3.0'
+# Use ActiveModel has_secure_password
+# gem 'bcrypt', '~> 3.1.7'
+
+# Use Capistrano for deployment
+# gem 'capistrano-rails', group: :development
+
+group :development, :test do
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+ # Adds support for Capybara system testing and selenium driver
+ gem 'capybara', '~> 2.13'
+ gem 'selenium-webdriver'
+end
+
+group :development do
+ # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
+ gem 'web-console', '>= 3.3.0'
+ gem 'listen', '>= 3.0.5', '< 3.2'
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
+ gem 'spring'
+ gem 'spring-watcher-listen', '~> 2.0.0'
+end
+
+# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
+gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+
+gem 'redcarpet'
diff --git a/example/rails-51/README.md b/example/rails-51/README.md
new file mode 100644
index 000000000..7891cb733
--- /dev/null
+++ b/example/rails-51/README.md
@@ -0,0 +1,33 @@
+# Bugsnag Rails v5.1 demo
+
+This Rails application demonstrates how to use Bugsnag with Rails v5.1.
+Further details about integrating Bugsnag with Rack applications can be found [here.](https://docs.bugsnag.com/platforms/ruby/rails/)
+
+Install dependencies
+
+```shell
+bundle install
+```
+
+## Configuring Bugsnag and Rails v5.1
+
+There are two methods of configuring Bugsnag within a Rails application:
+
+1. Your `API_KEY` can be exported as an environment variable `BUGSNAG_API_KEY`.
+
+2. Generate a bugsnag configuration file at ```config/initializers/bugsnag.rb``` which can be populated with the [available configuration options](https://docs.bugsnag.com/platforms/ruby/rails/configuration-options/) by running the rails command:
+ ```shell
+ bin/rails generate bugsnag YOUR_API_KEY_HERE
+ ```
+
+This is sufficient to start reporting unhandled exceptions to Bugsnag.
+
+## Running the example
+
+Run the example using:
+
+```shell
+bundle exec rails server
+```
+
+Once the server is running head to the default path for more information on Bugsnag logging examples.
diff --git a/example/rails-51/Rakefile b/example/rails-51/Rakefile
new file mode 100644
index 000000000..e85f91391
--- /dev/null
+++ b/example/rails-51/Rakefile
@@ -0,0 +1,6 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require_relative 'config/application'
+
+Rails.application.load_tasks
diff --git a/example/rails-51/app/assets/config/manifest.js b/example/rails-51/app/assets/config/manifest.js
new file mode 100644
index 000000000..b16e53d6d
--- /dev/null
+++ b/example/rails-51/app/assets/config/manifest.js
@@ -0,0 +1,3 @@
+//= link_tree ../images
+//= link_directory ../javascripts .js
+//= link_directory ../stylesheets .css
diff --git a/example/rails-42/app/controllers/concerns/.keep b/example/rails-51/app/assets/images/.keep
similarity index 100%
rename from example/rails-42/app/controllers/concerns/.keep
rename to example/rails-51/app/assets/images/.keep
diff --git a/example/rails-51/app/assets/javascripts/application.js b/example/rails-51/app/assets/javascripts/application.js
new file mode 100644
index 000000000..46b20359f
--- /dev/null
+++ b/example/rails-51/app/assets/javascripts/application.js
@@ -0,0 +1,15 @@
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
+// vendor/assets/javascripts directory can be referenced here using a relative path.
+//
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// compiled file. JavaScript code in this file should be added after the last require_* statement.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+//= require rails-ujs
+//= require turbolinks
+//= require_tree .
diff --git a/example/rails-51/app/assets/javascripts/cable.js b/example/rails-51/app/assets/javascripts/cable.js
new file mode 100644
index 000000000..739aa5f02
--- /dev/null
+++ b/example/rails-51/app/assets/javascripts/cable.js
@@ -0,0 +1,13 @@
+// Action Cable provides the framework to deal with WebSockets in Rails.
+// You can generate new channels where WebSocket features live using the `rails generate channel` command.
+//
+//= require action_cable
+//= require_self
+//= require_tree ./channels
+
+(function() {
+ this.App || (this.App = {});
+
+ App.cable = ActionCable.createConsumer();
+
+}).call(this);
diff --git a/example/rails-42/app/mailers/.keep b/example/rails-51/app/assets/javascripts/channels/.keep
similarity index 100%
rename from example/rails-42/app/mailers/.keep
rename to example/rails-51/app/assets/javascripts/channels/.keep
diff --git a/example/rails-51/app/assets/stylesheets/application.css b/example/rails-51/app/assets/stylesheets/application.css
new file mode 100644
index 000000000..d05ea0f51
--- /dev/null
+++ b/example/rails-51/app/assets/stylesheets/application.css
@@ -0,0 +1,15 @@
+/*
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
+ * files in this directory. Styles in this file should be added after the last require_* statement.
+ * It is generally better to create a new file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/example/rails-51/app/controllers/application_controller.rb b/example/rails-51/app/controllers/application_controller.rb
new file mode 100644
index 000000000..33f2701ff
--- /dev/null
+++ b/example/rails-51/app/controllers/application_controller.rb
@@ -0,0 +1,59 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery with: :exception
+
+ def index
+ @text = File.read(File.expand_path('app/views/index.md'))
+ end
+
+ def crash
+ raise Exception.new('Bugsnag Rails demo says: It crashed! Go check ' +
+ 'bugsnag.com for a new notification')
+ end
+
+ def callback
+ Bugsnag.before_notify_callbacks << proc { |report|
+ new_tab = {
+ message: 'Rails v4.2 demo says: Everything is great',
+ code: 200
+ }
+ report.add_tab(:diagnostics, new_tab)
+ }
+ raise RuntimeError.new('Bugsnag Rails demo says: It crashed! But, due to the attached callback' +
+ ' the exception has meta information. Go check' +
+ ' bugsnag.com for a new notification (see the Diagnostics tab)!')
+ end
+
+ def notify
+ Bugsnag.notify(RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash"))
+ @text = "Bugsnag Rack demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification.'
+ end
+
+ def data
+ error = RuntimeError.new("Bugsnag Rails demo says: False alarm, your application didn't crash")
+ Bugsnag.notify error do |report|
+ report.add_tab(:user, {
+ :username => "bob-hoskins",
+ :email => 'bugsnag@bugsnag.com',
+ :registered_user => true
+ })
+ report.add_tab(:diagnostics, {
+ :message => 'Rails demo says: Everything is great',
+ :code => 200
+ })
+ end
+ @text = "Bugsnag Rails demo says: It didn't crash! " +
+ 'But still go check https://bugsnag.com' +
+ ' for a new notification. Check out the User tab for the meta data'
+ end
+
+ def severity
+ msg = "Bugsnag Rails demo says: Look at the circle on the right side. It's different"
+ error = RuntimeError.new(msg)
+ Bugsnag.notify error do |report|
+ report.severity = 'info'
+ end
+ @text = msg
+ end
+end
diff --git a/example/rails-51/app/helpers/application_helper.rb b/example/rails-51/app/helpers/application_helper.rb
new file mode 100644
index 000000000..6d77519db
--- /dev/null
+++ b/example/rails-51/app/helpers/application_helper.rb
@@ -0,0 +1,22 @@
+module ApplicationHelper
+ def markdown(text)
+ options = {
+ filter_html: true,
+ hard_wrap: true,
+ link_attributes: { rel: 'nofollow', target: "_blank" },
+ space_after_headers: true,
+ fenced_code_blocks: true
+ }
+
+ extensions = {
+ autolink: true,
+ superscript: true,
+ disable_indented_code_blocks: true
+ }
+
+ renderer = Redcarpet::Render::HTML.new(options)
+ markdown = Redcarpet::Markdown.new(renderer, extensions)
+
+ markdown.render(text).html_safe
+ end
+end
diff --git a/example/rails-51/app/views/application/data.html.erb b/example/rails-51/app/views/application/data.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-51/app/views/application/data.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-51/app/views/application/index.html.erb b/example/rails-51/app/views/application/index.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-51/app/views/application/index.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-51/app/views/application/notify.html.erb b/example/rails-51/app/views/application/notify.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-51/app/views/application/notify.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-51/app/views/application/severity.html.erb b/example/rails-51/app/views/application/severity.html.erb
new file mode 100644
index 000000000..aa2786a7d
--- /dev/null
+++ b/example/rails-51/app/views/application/severity.html.erb
@@ -0,0 +1 @@
+<%= markdown(@text)%>
\ No newline at end of file
diff --git a/example/rails-51/app/views/index.md b/example/rails-51/app/views/index.md
new file mode 100644
index 000000000..4dac75585
--- /dev/null
+++ b/example/rails-51/app/views/index.md
@@ -0,0 +1,25 @@
+# Bugsnag Rails v5.1 demo
+
+This application demonstrates the use of Bugsnag with the Rails web framework.
+
+While testing the examples open [your dashboard](https://app.bugsnag.com) in order to see the example errors and exceptions being received.
+
+1. [Crash](/crash)
+
+ Raises an error within the framework, generating a report in the Bugsnag dashboard.
+
+2. [Crash and use callbacks](/crash_with_callback)
+
+ Raises an exception within the framework, but with additional data attached to the report. By registering a callback before the error occurs useful data can be attached as a tab in the Bugsnag dashboard.
+
+3. [Notify](/notify)
+
+ Sends Bugsnag a report on demand using `bugsnag.notify`. Allows details of handled errors or information to be sent to the Bugsnag dashboard without crashing your code.
+
+4. [Notify with data](/notify_data)
+
+ Same as `notify` but allows you to attach additional data within a `block`, similar to the `before_notify_callbacks` example above. In this case we're adding information about the user to go into the `user` tab, and additional diagnostics as a `diagnostics` tab.
+
+5. [Set the severity](/notify_severity)
+
+ This uses the same mechanism as adding meta-data, but allows you to set he `severity` when notifying Bugsnag of the error. Valid severities are `error`, `warning`, and `info`. Have a look on the dashboard to see the difference in these severities.
\ No newline at end of file
diff --git a/example/rails-51/app/views/layouts/application.html.erb b/example/rails-51/app/views/layouts/application.html.erb
new file mode 100644
index 000000000..2906ab464
--- /dev/null
+++ b/example/rails-51/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+
+
+
+ Rails51
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ <%= csrf_meta_tags %>
+
+
+
+<%= yield %>
+
+
+
diff --git a/example/rails-51/config.ru b/example/rails-51/config.ru
new file mode 100644
index 000000000..f7ba0b527
--- /dev/null
+++ b/example/rails-51/config.ru
@@ -0,0 +1,5 @@
+# This file is used by Rack-based servers to start the application.
+
+require_relative 'config/environment'
+
+run Rails.application
diff --git a/example/rails-51/config/application.rb b/example/rails-51/config/application.rb
new file mode 100644
index 000000000..5c8da3bc1
--- /dev/null
+++ b/example/rails-51/config/application.rb
@@ -0,0 +1,18 @@
+require_relative 'boot'
+
+require 'rails/all'
+
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
+
+module Rails52
+ class Application < Rails::Application
+ # Initialize configuration defaults for originally generated Rails version.
+ config.load_defaults 5.1
+
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+ end
+end
diff --git a/example/rails-51/config/boot.rb b/example/rails-51/config/boot.rb
new file mode 100644
index 000000000..30f5120df
--- /dev/null
+++ b/example/rails-51/config/boot.rb
@@ -0,0 +1,3 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+
+require 'bundler/setup' # Set up gems listed in the Gemfile.
diff --git a/example/rails-51/config/cable.yml b/example/rails-51/config/cable.yml
new file mode 100644
index 000000000..8a19eab08
--- /dev/null
+++ b/example/rails-51/config/cable.yml
@@ -0,0 +1,10 @@
+development:
+ adapter: async
+
+test:
+ adapter: async
+
+production:
+ adapter: redis
+ url: redis://localhost:6379/1
+ channel_prefix: rails-52_production
diff --git a/example/rails-51/config/database.yml b/example/rails-51/config/database.yml
new file mode 100644
index 000000000..0d02f2498
--- /dev/null
+++ b/example/rails-51/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+#
+default: &default
+ adapter: sqlite3
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ timeout: 5000
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/example/rails-51/config/environment.rb b/example/rails-51/config/environment.rb
new file mode 100644
index 000000000..426333bb4
--- /dev/null
+++ b/example/rails-51/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require_relative 'application'
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/example/rails-51/config/environments/development.rb b/example/rails-51/config/environments/development.rb
new file mode 100644
index 000000000..5187e2218
--- /dev/null
+++ b/example/rails-51/config/environments/development.rb
@@ -0,0 +1,54 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ if Rails.root.join('tmp/caching-dev.txt').exist?
+ config.action_controller.perform_caching = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+
+ config.action_mailer.perform_caching = false
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Suppress logger output for asset requests.
+ config.assets.quiet = true
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/example/rails-51/config/environments/production.rb b/example/rails-51/config/environments/production.rb
new file mode 100644
index 000000000..961695e9c
--- /dev/null
+++ b/example/rails-51/config/environments/production.rb
@@ -0,0 +1,91 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Attempt to read encrypted secrets from `config/secrets.yml.enc`.
+ # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
+ # `config/secrets.yml.key`.
+ config.read_encrypted_secrets = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Mount Action Cable outside main process or domain
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = 'wss://example.com/cable'
+ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Use a real queuing backend for Active Job (and separate queues per environment)
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "rails-52_#{Rails.env}"
+ config.action_mailer.perform_caching = false
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+end
diff --git a/example/rails-51/config/environments/test.rb b/example/rails-51/config/environments/test.rb
new file mode 100644
index 000000000..8e5cbde53
--- /dev/null
+++ b/example/rails-51/config/environments/test.rb
@@ -0,0 +1,42 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+ config.action_mailer.perform_caching = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/example/rails-51/config/initializers/application_controller_renderer.rb b/example/rails-51/config/initializers/application_controller_renderer.rb
new file mode 100644
index 000000000..89d2efab2
--- /dev/null
+++ b/example/rails-51/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/example/rails-51/config/initializers/assets.rb b/example/rails-51/config/initializers/assets.rb
new file mode 100644
index 000000000..4b828e80c
--- /dev/null
+++ b/example/rails-51/config/initializers/assets.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join('node_modules')
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/example/rails-51/config/initializers/backtrace_silencers.rb b/example/rails-51/config/initializers/backtrace_silencers.rb
new file mode 100644
index 000000000..59385cdf3
--- /dev/null
+++ b/example/rails-51/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/example/rails-51/config/initializers/bugsnag.rb b/example/rails-51/config/initializers/bugsnag.rb
new file mode 100644
index 000000000..200cb5051
--- /dev/null
+++ b/example/rails-51/config/initializers/bugsnag.rb
@@ -0,0 +1,3 @@
+Bugsnag.configure do |config|
+ config.api_key = "f35a2472bd230ac0ab0f52715bbdc65d"
+end
diff --git a/example/rails-51/config/initializers/cookies_serializer.rb b/example/rails-51/config/initializers/cookies_serializer.rb
new file mode 100644
index 000000000..5a6a32d37
--- /dev/null
+++ b/example/rails-51/config/initializers/cookies_serializer.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/example/rails-51/config/initializers/filter_parameter_logging.rb b/example/rails-51/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 000000000..4a994e1e7
--- /dev/null
+++ b/example/rails-51/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/example/rails-51/config/initializers/inflections.rb b/example/rails-51/config/initializers/inflections.rb
new file mode 100644
index 000000000..ac033bf9d
--- /dev/null
+++ b/example/rails-51/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/example/rails-51/config/initializers/mime_types.rb b/example/rails-51/config/initializers/mime_types.rb
new file mode 100644
index 000000000..dc1899682
--- /dev/null
+++ b/example/rails-51/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/example/rails-51/config/initializers/wrap_parameters.rb b/example/rails-51/config/initializers/wrap_parameters.rb
new file mode 100644
index 000000000..bbfc3961b
--- /dev/null
+++ b/example/rails-51/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/example/rails-51/config/locales/en.yml b/example/rails-51/config/locales/en.yml
new file mode 100644
index 000000000..decc5a857
--- /dev/null
+++ b/example/rails-51/config/locales/en.yml
@@ -0,0 +1,33 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# The following keys must be escaped otherwise they will not be retrieved by
+# the default I18n backend:
+#
+# true, false, on, off, yes, no
+#
+# Instead, surround them with single quotes.
+#
+# en:
+# 'true': 'foo'
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/example/rails-51/config/puma.rb b/example/rails-51/config/puma.rb
new file mode 100644
index 000000000..1e19380dc
--- /dev/null
+++ b/example/rails-51/config/puma.rb
@@ -0,0 +1,56 @@
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma. Default is set to 5 threads for minimum
+# and maximum; this matches the default thread size of Active Record.
+#
+threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+threads threads_count, threads_count
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+#
+port ENV.fetch("PORT") { 3000 }
+
+# Specifies the `environment` that Puma will run in.
+#
+environment ENV.fetch("RAILS_ENV") { "development" }
+
+# Specifies the number of `workers` to boot in clustered mode.
+# Workers are forked webserver processes. If using threads and workers together
+# the concurrency of the application would be max `threads` * `workers`.
+# Workers do not work on JRuby or Windows (both of which do not support
+# processes).
+#
+# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+
+# Use the `preload_app!` method when specifying a `workers` number.
+# This directive tells Puma to first boot the application and load code
+# before forking the application. This takes advantage of Copy On Write
+# process behavior so workers use less memory. If you use this option
+# you need to make sure to reconnect any threads in the `on_worker_boot`
+# block.
+#
+# preload_app!
+
+# If you are preloading your application and using Active Record, it's
+# recommended that you close any connections to the database before workers
+# are forked to prevent connection leakage.
+#
+# before_fork do
+# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
+# end
+
+# The code in the `on_worker_boot` will be called if you are using
+# clustered mode by specifying a number of `workers`. After each worker
+# process is booted, this block will be run. If you are using the `preload_app!`
+# option, you will want to use this block to reconnect to any threads
+# or connections that may have been created at application boot, as Ruby
+# cannot share connections between processes.
+#
+# on_worker_boot do
+# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
+# end
+#
+
+# Allow puma to be restarted by `rails restart` command.
+plugin :tmp_restart
diff --git a/example/rails-51/config/routes.rb b/example/rails-51/config/routes.rb
new file mode 100644
index 000000000..2b169b6d8
--- /dev/null
+++ b/example/rails-51/config/routes.rb
@@ -0,0 +1,9 @@
+Rails.application.routes.draw do
+ # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+ get '/', to: 'application#index'
+ get '/crash', to: 'application#crash'
+ get '/crash_with_callback', to: 'application#callback'
+ get '/notify', to: 'application#notify'
+ get '/notify_data', to: 'application#data'
+ get '/notify_severity', to: 'application#severity'
+end
diff --git a/example/rails-51/config/secrets.yml b/example/rails-51/config/secrets.yml
new file mode 100644
index 000000000..4998e7e47
--- /dev/null
+++ b/example/rails-51/config/secrets.yml
@@ -0,0 +1,32 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rails secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+# Shared secrets are available across all environments.
+
+# shared:
+# api_key: a1B2c3D4e5F6
+
+# Environmental secrets are only available for that specific environment.
+
+development:
+ secret_key_base: 2b81b7259b2843d11e9b9ce98872027bcce4f5c043b8f428e978d93627538b36f05a485b7898a378d80604dd623b8f0094aa4c1643eadffd5ed75bfe3446e11d
+
+test:
+ secret_key_base: 0b6d7f7e7eb96c729f3ebdd4b0868b8e3003ad8ebac8c4ce08f61d67b3184bca171364661988179c31e85e07b2d379bfb5f2d7570004805675432ae87811ad4a
+
+# Do not keep production secrets in the unencrypted secrets file.
+# Instead, either read values from the environment.
+# Or, use `bin/rails secrets:setup` to configure encrypted secrets
+# and move the `production:` environment over there.
+
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/example/rails-51/config/spring.rb b/example/rails-51/config/spring.rb
new file mode 100644
index 000000000..c9119b40c
--- /dev/null
+++ b/example/rails-51/config/spring.rb
@@ -0,0 +1,6 @@
+%w(
+ .ruby-version
+ .rbenv-vars
+ tmp/restart.txt
+ tmp/caching-dev.txt
+).each { |path| Spring.watch(path) }
diff --git a/example/rails-51/db/seeds.rb b/example/rails-51/db/seeds.rb
new file mode 100644
index 000000000..1beea2acc
--- /dev/null
+++ b/example/rails-51/db/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
+#
+# Examples:
+#
+# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
+# Character.create(name: 'Luke', movie: movies.first)
diff --git a/example/rails-42/app/models/.keep b/example/rails-51/lib/assets/.keep
similarity index 100%
rename from example/rails-42/app/models/.keep
rename to example/rails-51/lib/assets/.keep
diff --git a/example/rails-42/app/models/concerns/.keep b/example/rails-51/lib/tasks/.keep
similarity index 100%
rename from example/rails-42/app/models/concerns/.keep
rename to example/rails-51/lib/tasks/.keep
diff --git a/example/rails-51/package.json b/example/rails-51/package.json
new file mode 100644
index 000000000..81ffb7178
--- /dev/null
+++ b/example/rails-51/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "rails-52",
+ "private": true,
+ "dependencies": {}
+}
diff --git a/example/rails-51/public/404.html b/example/rails-51/public/404.html
new file mode 100644
index 000000000..2be3af26f
--- /dev/null
+++ b/example/rails-51/public/404.html
@@ -0,0 +1,67 @@
+
+
+
+ The page you were looking for doesn't exist (404)
+
+
+
+
+
+
+
+
+
The page you were looking for doesn't exist.
+
You may have mistyped the address or the page may have moved.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/example/rails-51/public/422.html b/example/rails-51/public/422.html
new file mode 100644
index 000000000..c08eac0d1
--- /dev/null
+++ b/example/rails-51/public/422.html
@@ -0,0 +1,67 @@
+
+
+
+ The change you wanted was rejected (422)
+
+
+
+
+
+
+
+
+
The change you wanted was rejected.
+
Maybe you tried to change something you didn't have access to.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/example/rails-51/public/500.html b/example/rails-51/public/500.html
new file mode 100644
index 000000000..78a030af2
--- /dev/null
+++ b/example/rails-51/public/500.html
@@ -0,0 +1,66 @@
+
+
+
+ We're sorry, but something went wrong (500)
+
+
+
+
+
+
+
+
+
We're sorry, but something went wrong.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/example/rails-32/app/mailers/.gitkeep b/example/rails-51/public/apple-touch-icon-precomposed.png
similarity index 100%
rename from example/rails-32/app/mailers/.gitkeep
rename to example/rails-51/public/apple-touch-icon-precomposed.png
diff --git a/example/rails-32/app/models/.gitkeep b/example/rails-51/public/apple-touch-icon.png
similarity index 100%
rename from example/rails-32/app/models/.gitkeep
rename to example/rails-51/public/apple-touch-icon.png
diff --git a/example/rails-32/lib/assets/.gitkeep b/example/rails-51/public/favicon.ico
similarity index 100%
rename from example/rails-32/lib/assets/.gitkeep
rename to example/rails-51/public/favicon.ico
diff --git a/example/rails-51/public/robots.txt b/example/rails-51/public/robots.txt
new file mode 100644
index 000000000..37b576a4a
--- /dev/null
+++ b/example/rails-51/public/robots.txt
@@ -0,0 +1 @@
+# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
diff --git a/example/resque/README.md b/example/resque/README.md
new file mode 100644
index 000000000..49511e511
--- /dev/null
+++ b/example/resque/README.md
@@ -0,0 +1,62 @@
+# Bugsnag Resque demo
+
+This Resque application demonstrates how to use Bugsnag with Resque.
+Further details about integrating Bugsnag with Resque can be found [here.](https://docs.bugsnag.com/platforms/ruby/other/)
+
+Install dependencies
+
+```shell
+bundle install
+```
+
+Resque requires a datastore to run, this example uses [redis](https://redis.io), installation instructions for which can be found [here](https://redis.io/topics/quickstart) and an official docker image can be found [here](https://hub.docker.com/_/redis/).
+
+## Configuring Bugsnag and Resque
+
+Configure your `API_KEY` and any other configuration as detailed in the [available configuration options](https://docs.bugsnag.com/platforms/ruby/other/configuration-options/) by calling `Bugsnag.configure` and passing the options to the `configuration` object returned to a provided block:
+ ```ruby
+ Bugsnag.configure do |configuration|
+ configuration.api_key = "YOUR_API_KEY"
+ end
+ ```
+
+In the applications ```Rakefile``` ensure that the `bugsnag/integrations/rake` middleware is loaded to automatically handled any errors.
+
+## Running the examples
+
+Each of the examples can be run individually to verify the behaviour through the [Bugsnag dashboard](https://app.bugsnag.com):
+
+1. Crash
+
+This example shows unhandled errors being captured by Bugsnag and a notification automatically being sent to the Bugsnag dashboard. You can run this example with:
+```shell
+QUEUE=crash bundle exec rake resque:work
+```
+
+2. Crash with a callback
+
+This example is similar to the first, but by registering a callback before the error is caught we can attach additional information that can be viewed in the Bugsnag dashboard. Run this example with:
+```shell
+QUEUE=callback bundle exec rake resque:work
+```
+
+3. Notify
+
+This example will send a notification without crashing the Resque worker. This allows you to send notifications of handled errors to the Bugsnag dashboard. Run this example with:
+```shell
+QUEUE=notify bundle exec rake resque:work
+```
+
+4. Notify with attached data
+
+This example is similar to the above, however adds additional data to the notification using a block. Check out the `diagnostics` and `queue` tabs on the error in the dashboard. Run this example with:
+```shell
+QUEUE=data bundle exec rake resque:work
+```
+
+5. Notify with a custom severity
+
+Finally, this example is similar to the previous, but sets a custom severity on the notification. This will be reflected in the coloured circle shown alongside the event in the bugsnag dashboard. Run this example with:
+```shell
+QUEUE=severity bundle exec rake resque:work
+```
diff --git a/example/resque/Rakefile b/example/resque/Rakefile
index 2b96dea92..decc81ba1 100644
--- a/example/resque/Rakefile
+++ b/example/resque/Rakefile
@@ -1,8 +1,3 @@
load "app.rb"
require "resque/tasks"
-require "bugsnag/rake"
-
-desc "Remove all events that are too old to be accessed"
-task :test_rake do
- raise "DWERP"
-end
+require "bugsnag/integrations/rake"
diff --git a/example/resque/app.rb b/example/resque/app.rb
index 33f457aa3..0fdc27fc3 100644
--- a/example/resque/app.rb
+++ b/example/resque/app.rb
@@ -2,20 +2,78 @@
require 'bugsnag'
Bugsnag.configure do |config|
- config.api_key = '066f5ad3590596f9aa8d601ea89af845'
+ config.api_key = 'YOUR_API_KEY'
end
-# This is a simple Resque job.
-class Archive
- @queue = :test
+# Unhandled Exception example
+class Crash
+ @queue = :crash
- def self.perform(how_hard="super hard", how_long=1)
- puts "Workin' #{how_hard} #{how_long}"
- raise 'Uh oh!'
+ def self.perform
+ raise Exception.new "Crashed - Check your Bugsnag dashboard"
end
end
-Resque.enqueue(Archive, "super hard", 1)
+# Unhandled with callback Exception example
+class Callback
+ @queue = :callback
-# To run a worker run
-#QUEUE=* rake resque:work
+ def self.perform
+ Bugsnag.before_notify_callbacks << proc { |report|
+ new_tab = {
+ message: 'Resque demo says: Everything is great',
+ code: 200
+ }
+ report.add_tab(:diagnostics, new_tab)
+ }
+ raise Exception.new "Crashed - Check the Bugsnag dashboard for diagnostic data"
+ end
+end
+
+# Handled example
+class Notify
+ @queue = :notify
+
+ def self.perform
+ Bugsnag.notify(Exception.new "Didn't crash, but sent a notification anyway")
+ puts "The Resque worker hasn't crashed, but it has sent a notification, so go check out the dashboard!"
+ end
+end
+
+# Handled example with additional data
+class Data
+ @queue = :data
+
+ def self.perform
+ error = Exception.new "Didn't crash, but sent a notification anyway"
+ Bugsnag.notify error do |report|
+ report.add_tab(:queue, {
+ :name => "data",
+ :fatal => false
+ })
+ report.add_tab(:diagnostics, {
+ :message => 'Resque demo says: Everything is great',
+ })
+ end
+ puts "The Resque worker hasn't crashed, but it has sent a notification, with additional data to the dashboard"
+ end
+end
+
+# Handled example with set severity
+class Severity
+ @queue = :severity
+
+ def self.perform
+ error = Exception.new "Didn't crash, but sent a notification anyway"
+ Bugsnag.notify error do |report|
+ report.severity = "info"
+ end
+ puts "The Resque worker hasn't crashed but check the severity of the dashboard notification"
+ end
+end
+
+Resque.enqueue(Crash)
+Resque.enqueue(Callback)
+Resque.enqueue(Notify)
+Resque.enqueue(Data)
+Resque.enqueue(Severity)
diff --git a/example/shoryuken/Gemfile b/example/shoryuken/Gemfile
index a25ede615..ca89ba82a 100644
--- a/example/shoryuken/Gemfile
+++ b/example/shoryuken/Gemfile
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
gem "bugsnag", path: "../../"
gem "shoryuken"
+gem "aws-sdk-sqs"
diff --git a/example/shoryuken/README.md b/example/shoryuken/README.md
new file mode 100644
index 000000000..e1f53ceaf
--- /dev/null
+++ b/example/shoryuken/README.md
@@ -0,0 +1,42 @@
+# Using Bugsnag with Shoryuken
+
+This example shows how to use Bugsnag in conjunction with Shoryuken to report any exceptions that occur in your applications.
+
+First, install dependencies
+```shell
+bundle install
+```
+
+## Configuring Bugsnag with Shoryuken
+
+Bugsnag can be configured in one of two ways in your Shoryuken app:
+
+1. require `bugsnag` in your application and call `Bugsnag.configure` with a block, setting the appropriate configuration options:
+```ruby
+Bugsnag.configure do |config|
+ config.api_key = "YOUR_API_KEY"
+end
+```
+
+2. require `bugsnag` in your application and input configuration options through environment variables, such as setting `BUGSNAG_API_KEY` to `YOUR_API_KEY`.
+
+All configuration options can be found in the [Bugsnag documentation](https://docs.bugsnag.com/platforms/ruby/other/configuration-options/)
+
+## Running the example
+
+Set up your AWS credentials as required in the [Configure the AWS Client](https://github.com/phstc/shoryuken/wiki/Configure-the-AWS-Client) section of the Shoryuken getting started docs.
+
+Start the Shoryuken processor with:
+```shell
+bundle exec shoryuken -r ./shoryuken.rb
+```
+
+In a seperate terminal instance open the console session with:
+```shell
+bundle exec irb -r ./shoryuken.rb
+```
+
+Where you can queue a message with the command:
+```ruby
+BugsnagTest.perform_async "Hello world"
+```
\ No newline at end of file
diff --git a/example/shoryuken/shoryuken.rb b/example/shoryuken/shoryuken.rb
index 4abbf4d2b..15e3aee33 100644
--- a/example/shoryuken/shoryuken.rb
+++ b/example/shoryuken/shoryuken.rb
@@ -2,19 +2,10 @@
require 'bugsnag'
Bugsnag.configure do |config|
- config.api_key = 'YOUR BUGSNAG API KEY'
+ config.api_key = 'YOUR_API_KEY'
end
-Shoryuken.configure_server do |config|
- if defined?(Rails)
- # Replace Rails logger so messages are logged wherever Shoryuken is logging
- # Note: this entire block is only run by the processor, so we don't overwrite
- # the logger when the app is running as usual.
- Rails.logger = Shoryuken::Logging.logger
- end
-end
-
-class PlainOldRuby
+class BugsnagTest
include Shoryuken::Worker
shoryuken_options queue: 'connector_development_default', auto_delete: true
@@ -24,18 +15,3 @@ def perform(sqs_msg, body)
raise 'Uh oh!'
end
end
-
-# Start up shoryuken processor via:
-#
-# bundle exec shoryuken -r ./shoryuken.rb -C ./shoryuken.yml
-
-# Then you can open up console session like so:
-#
-# export AWS_ACCESS_KEY_ID=ABCDEFG123456789
-# export AWS_SECRET_ACCESS_KEY=9876543210ZYX
-# export AWS_REGION=ap-southeast-2
-#
-# irb -r ./shoryuken.rb
-#
-# where you can then say
-# PlainOldRuby.perform_async "hello"
diff --git a/example/sidekiq/README.md b/example/sidekiq/README.md
new file mode 100644
index 000000000..f537923f7
--- /dev/null
+++ b/example/sidekiq/README.md
@@ -0,0 +1,74 @@
+# Bugsnag Sidekiq demo
+
+This Sidekiq application demonstrates how to use Bugsnag with Sidekiq.
+Further details about integrating Bugsnag with Sidekiq can be found [here.](https://docs.bugsnag.com/platforms/ruby/sidekiq/)
+
+Install dependencies
+
+```shell
+bundle install
+```
+
+Sidekiq requires a datastore to run, this examples uses [redis](https://redis.io), installation instructions for which can be found [here](https://redis.io/topics/quickstart) and an official docker image can be found [here](https://hub.docker.com/_/redis/).
+
+## Configuring Bugsnag and Sidekiq
+
+Configure your `API_KEY` and any other configuration as detailed in the [available configuration options](https://docs.bugsnag.com/platforms/ruby/sidekiq/configuration-options/) by calling `Bugsnag.configure` and passing the options to the `configuration` object yielded to a provided block:
+ ```ruby
+ Bugsnag.configure do |configuration|
+ configuration.api_key = "YOUR_API_KEY"
+ end
+ ```
+
+## Running the examples
+
+Once the app is configured it can be run using two terminal windows. In the first terminal the Sidekiq application will be started using:
+
+```shell
+bundle exec sidekiq -r ./sidekiq.rb
+```
+
+Once this is running, in the second terminal, you will need to open an interactive Ruby instance with the script loaded into it by running the command:
+
+```shell
+bundle exec irb -r ./sidekiq.rb
+```
+
+This will then allow the workers to be started via Ruby commands in the interactive Ruby instance.
+
+Each of the examples can be run individually to verify the behaviour through the [Bugsnag dashboard](https://app.bugsnag.com):
+
+1. Crash
+
+This example shows unhandled errors being captured by Bugsnag and a notification automatically being sent to the Bugsnag dashboard. You can run this example with:
+```ruby
+Crash.perform_async
+```
+
+2. Crash with a callback
+
+This example is similar to the first, but by registering a callback before the error is caught we can attach additional information that can be viewed in the Bugsnag dashboard. Run this example with:
+```ruby
+Callback.perform_async
+```
+
+3. Notify
+
+This example will send a notification without crashing the Resque worker. This allows you to send notifications of handled errors to the Bugsnag dashboard. Run this example with:
+```ruby
+Notify.perform_async
+```
+
+4. Notify with attached data
+
+This example is similar to the above, however adds additional data to the notification using a block. Check out the `diagnostics` and `queue` tabs on the error in the dashboard. Run this example with:
+```ruby
+Metadata.perform_async
+```
+
+5. Notify with a custom severity
+
+Finally, this example is similar to the previous, but sets a custom severity on the notification. This will be reflected in the coloured circle shown alongside the event in the bugsnag dashboard. Run this example with:
+```ruby
+Severity.perform_async
+```
diff --git a/example/sidekiq/sidekiq.rb b/example/sidekiq/sidekiq.rb
index c2a2d84fb..3edf8663a 100644
--- a/example/sidekiq/sidekiq.rb
+++ b/example/sidekiq/sidekiq.rb
@@ -2,7 +2,7 @@
require 'bugsnag'
Bugsnag.configure do |config|
- config.api_key = '066f5ad3590596f9aa8d601ea89af845'
+ config.api_key = 'YOUR_API_KEY'
end
# If your client is single-threaded, we just need a single connection in our Redis connection pool
@@ -15,18 +15,75 @@
config.redis = { :namespace => 'x' }
end
-# Start up sidekiq via
-# bundle exec sidekiq -r ./sidekiq.rb
-# and then you can open up an IRB session like so:
-# irb -r ./sidekiq.rb
-# where you can then say
-# PlainOldRuby.perform_async "like a dog", 3
-#
-class PlainOldRuby
+# Unhandled example
+class Crash
include Sidekiq::Worker
+ sidekiq_options :retry => false
- def perform(how_hard="super hard", how_long=1)
- puts "Workin' #{how_hard} #{how_long}"
- raise 'Uh oh!'
+ def perform
+ raise Exception.new "Crashed - Check your Bugsnag dashboard"
end
end
+
+# Unhandled example with callback
+class Callback
+ include Sidekiq::Worker
+ sidekiq_options :retry => false
+
+ def perform
+ Bugsnag.before_notify_callbacks << proc { |report|
+ new_tab = {
+ message: 'Sidekiq demo says: Everything is great',
+ code: 200
+ }
+ report.add_tab(:diagnostics, new_tab)
+ }
+ raise Exception.new "Crashed - Check the Bugsnag dashboard for diagnostic data"
+ end
+end
+
+# Handled example
+class Notify
+ include Sidekiq::Worker
+ sidekiq_options :retry => false
+
+ def perform
+ Bugsnag.notify(Exception.new "Didn't crash, but sent a notification anyway")
+ puts "The Sidekiq worker hasn't crashed, but it has sent a notification, so go check out the dashboard!"
+ end
+end
+
+# Handled example with additional data
+class Metadata
+ include Sidekiq::Worker
+ sidekiq_options :retry => false
+
+ def perform
+ error = Exception.new "Didn't crash, but sent a notification anyway"
+ Bugsnag.notify error do |report|
+ report.add_tab(:function, {
+ :name => "Metadata",
+ :fatal => false
+ })
+ report.add_tab(:diagnostics, {
+ :message => 'Sidekiq demo says: Everything is great',
+ })
+ end
+ puts "The Sidekiq worker hasn't crashed, but it has sent a notification, with additional data to the dashboard"
+ end
+end
+
+
+# Handled example with set severity
+class Severity
+ include Sidekiq::Worker
+ sidekiq_options :retry => false
+
+ def perform
+ error = Exception.new "Didn't crash, but sent a notification anyway"
+ Bugsnag.notify error do |report|
+ report.severity = "info"
+ end
+ puts "The Sidekiq worker hasn't crashed but check the severity of the dashboard notification"
+ end
+end
\ No newline at end of file
diff --git a/example/sinatra/Gemfile b/example/sinatra/Gemfile
index a7f9f2c1d..72cbdbaf2 100644
--- a/example/sinatra/Gemfile
+++ b/example/sinatra/Gemfile
@@ -1,7 +1,7 @@
source 'https://rubygems.org'
if ENV['INTEGRATION_LANGUAGE']
- gem 'bugsnag', path: '../../../../'
+ gem 'bugsnag', path: '../../'
else
gem 'bugsnag'
end
diff --git a/example/sinatra/README.md b/example/sinatra/README.md
index ed0268fa5..004649289 100644
--- a/example/sinatra/README.md
+++ b/example/sinatra/README.md
@@ -1,84 +1,43 @@
# Bugsnag Sinatra demo
-This Sinatra application demonstrates how to use Bugsnag with Sinatra. Before
-testing it, open up the `config.ru` file (or any other file with your
-configuration details) and configure your API key.
+This Sinatra application demonstrates how to use Bugsnag with Sinatra. Further details about integrating Bugsnag with Sidekiq can
-```
-Bugsnag.configure do |config|
- config.api_key = '0a6f5add590596f93a8d601ea89af841'
-end
+Install dependencies
+
+```shell
+bundle install
```
-The other way to configure the API key is to export the `BUGSNAG_API_KEY`
-environment variable.
+## Configuring Bugsnag and Sinatra
-In the same file activate the Bugsnag Rack middleware.
+The `API_KEY` can be set in one of two ways:
-```
-use Bugsnag::Rack
-```
+1. Export the `API_KEY` as an environment variable, `BUGSNAG_API_KEY` to be used when running the server.
-Be sure that `raise_errors` is set to `true` and `show_exceptions` is set to
-`false`. Otherwise, in the development environment, automatic notifications
-won't work, as Sinatra would be swallowing exceptions from Bugsnag.
+2. Configure the `API_KEY` and any other configuration as detailed in the [available configuration options](https://docs.bugsnag.com/platforms/ruby/rack/configuration-options/) by calling `Bugsnag.configure` and passing the options to the `configuration` object yielded to a provided block:
+ ```ruby
+ Bugsnag.configure do |configuration|
+ configuration.api_key = "YOUR_API_KEY"
+ end
+ ```
+Make sure the server is using the correct Bugsnag Rack middleware by activating it at the top of the file:
+```ruby
+use Bugsnag::Rack
```
+
+Finally, to make sure exceptions are automatically cap[tured and notified in development or testing modes, ensure that the `raise_errors` option is set to `true`, and the `show_exceptions` option is set to `false`:
+```ruby
set :raise_errors, true
set :show_exceptions, false
```
-If you would like to use custom error handlers, then you need to notify Bugsnag
-explicitly.
-
-```
-error 500 do
- Bugsnag.auto_notify($!)
- erb :'errors/500'
-end
-```
-
-Install dependencies.
-
-```
-bundle install
-```
+## Running the examples
-Launch the Sinatra application.
+Run the example using:
-```
+```shell
bundle exec rackup
```
-Next, open your project's dashboard on Bugsnag.
-
-1. [crash](http://localhost:9292/crash)
-
-Crashes the application and sends a notification about the nature of the crash.
-Basically, almost any unhandled exception sends a notification to Bugsnag. See
-the line mentioning `get '/crash'`.
-
-1. [crash and use callbacks](http://localhost:9292/crash_with_callback)
-
-Before crashing, the application would append the Diagnostics tab with some
-predefined information, attached by means of a callback. See the line mentioning
-`get '/crash_with_callback'`.
-
-1. [notify](http://localhost:9292/notify)
-
-Bugsnag Ruby provides a way to send notifications on demand by means of
-`Bugsnag.notify`. This API allows to send notifications manually, without
-crashing your application. See the line mentioning `get '/notify'`.
-
-1. [notify with meta data](http://localhost:9292/notify_meta)
-
-Same as `notify`, but also attaches meta data. The meta data is any additional
-information you want to attach to an exception. In this artificial case
-additional information with be sent and displayed in a new tab called
-"Diagnostics". See the line mentioning `get '/notify_meta'`.
-
-1. [severity](http://localhost:9292/severity)
-
-Bugsnag supports three severities: 'error', 'warning' and 'info'. You can set
-the severity by passing one of these objects as a string to '#notify'. See the
-line mentioning `get '/severity'`.
+Next, open your project's dashboard on Bugsnag, and go to the default server route.
\ No newline at end of file
diff --git a/example/sinatra/config.ru b/example/sinatra/config.ru
index 1c46f6965..68efa300b 100644
--- a/example/sinatra/config.ru
+++ b/example/sinatra/config.ru
@@ -7,7 +7,7 @@ set :raise_errors, true
set :show_exceptions, false
Bugsnag.configure do |config|
- config.api_key = 'f35a2472bd230ac0ab0f52715bbdc65d'
+ config.api_key = 'YOUR_API_KEY'
end
get '/' do
@@ -15,7 +15,7 @@ get '/' do
fenced_code_blocks: true
}
renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML, opts)
- renderer.render(File.read(File.expand_path('README.md')))
+ renderer.render(File.read(File.expand_path('templates/index.md')))
end
get '/crash' do
@@ -46,31 +46,31 @@ get '/notify' do
' for a new notification.'
end
-get '/notify_meta' do
- meta_data = {
- :user => {
+get '/notify_data' do
+ error = RuntimeError.new("Bugsnag Sinatra demo says: False alarm, your application didn't crash")
+ Bugsnag.notify error do |report|
+ report.add_tab(:user, {
:username => "bob-hoskins",
:email => 'bugsnag@bugsnag.com',
:registered_user => true
- },
-
- :diagnostics => {
+ })
+ report.add_tab(:diagnostics, {
:message => 'Sinatra demo says: Everything is great',
:code => 200
- }
- }
- error = RuntimeError.new("Bugsnag Sinatra demo says: False alarm, your application didn't crash")
- Bugsnag.notify(error, meta_data)
+ })
+ end
"Bugsnag Sinatra demo says: It didn't crash! " +
'But still go check https://bugsnag.com' +
' for a new notification. Check out the User tab for the meta data'
end
-get '/severity' do
+get '/notify_severity' do
msg = "Bugsnag Sinatra demo says: Look at the circle on the right side. It's different"
error = RuntimeError.new(msg)
- Bugsnag.notify(error, severity: 'info')
+ Bugsnag.notify error do |report|
+ report.severity = 'info'
+ end
msg
end
diff --git a/example/sinatra/templates/index.md b/example/sinatra/templates/index.md
new file mode 100644
index 000000000..52695efe2
--- /dev/null
+++ b/example/sinatra/templates/index.md
@@ -0,0 +1,25 @@
+# Bugsnag Rack demo
+
+This application demonstrates the use of Bugsnag with the Rack web framework.
+
+While testing the examples open [your dashboard](https://app.bugsnag.com) in order to see the example errors and exceptions being received.
+
+1. [Crash](/crash)
+
+ Raises an error within the framework, generating a report in the Bugsnag dashboard.
+
+2. [Crash and use callbacks](/crash_with_callback)
+
+ Raises an exception within the framework, but with additional data attached to the report. By registering a callback before the error occurs useful data can be attached as a tab in the Bugsnag dashboard.
+
+3. [Notify](/notify)
+
+ Sends Bugsnag a report on demand using `bugsnag.notify`. Allows details of handled errors or information to be sent to the Bugsnag dashboard without crashing your code.
+
+4. [Notify with data](/notify_data)
+
+ Same as `notify` but allows you to attach additional data within a `block`, similar to the `before_notify_callbacks` example above. In this case we're adding information about the user to go into the `user` tab, and additional diagnostics as a `diagnostics` tab.
+
+5. [Set the severity](/notify_severity)
+
+ This uses the same mechanism as adding meta-data, but allows you to set he `severity` when notifying Bugsnag of the error. Valid severities are `error`, `warning`, and `info`. Have a look on the dashboard to see the difference in these severities.
\ No newline at end of file
diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb
index e14ea2407..4a92160f7 100644
--- a/lib/bugsnag.rb
+++ b/lib/bugsnag.rb
@@ -4,16 +4,16 @@
require "bugsnag/version"
require "bugsnag/configuration"
require "bugsnag/meta_data"
-require "bugsnag/notification"
+require "bugsnag/report"
require "bugsnag/cleaner"
require "bugsnag/helpers"
-require "bugsnag/deploy"
require "bugsnag/delivery"
require "bugsnag/delivery/synchronous"
require "bugsnag/delivery/thread_queue"
-require "bugsnag/rack"
+require "bugsnag/integrations/rack"
+require "bugsnag/integrations/railtie" if defined?(Rails::Railtie)
require "bugsnag/middleware/rack_request"
require "bugsnag/middleware/warden_user"
@@ -27,92 +27,88 @@
require "bugsnag/middleware/classify_error"
module Bugsnag
- LOG_PREFIX = "** [Bugsnag] "
LOCK = Mutex.new
class << self
# Configure the Bugsnag notifier application-wide settings.
- def configure(config_hash=nil)
- if config_hash
- config_hash.each do |k,v|
- configuration.send("#{k}=", v) rescue nil if configuration.respond_to?("#{k}=")
- end
- end
-
+ def configure
yield(configuration) if block_given?
- # Use resque for asynchronous notification if required
- require "bugsnag/delay/resque" if configuration.delay_with_resque && defined?(Resque)
-
- # Add info error classifier to internal middleware
- configuration.internal_middleware.use(Bugsnag::Middleware::ClassifyError)
-
- # Warn if an api_key hasn't been set
@key_warning = false unless defined?(@key_warning)
-
- if !configuration.api_key && !@key_warning
- warn "No API key has been set, check your configuration"
+ if !configuration.valid_api_key? && !@key_warning
+ configuration.warn("No valid API key has been set, notifications will not be sent")
@key_warning = true
end
+ end
- # Log that we are ready to rock
- @logged_ready = false unless defined?(@logged_ready)
+ # Explicitly notify of an exception
+ def notify(exception, auto_notify=false, &block)
- if configuration.api_key && !@logged_ready
- log "Bugsnag exception handler #{VERSION} ready"
- @logged_ready = true
+ if !configuration.auto_notify && auto_notify
+ configuration.debug("Not notifying because auto_notify is disabled")
+ return
end
- end
- # Explicitly notify of an exception
- def notify(exception, overrides=nil, request_data=nil, &block)
- notification = Notification.new(exception, configuration, overrides, request_data)
+ if !configuration.valid_api_key?
+ configuration.debug("Not notifying due to an invalid api_key")
+ return
+ end
- initial_severity = notification.severity
- initial_reason = notification.severity_reason
+ if !configuration.should_notify_release_stage?
+ configuration.debug("Not notifying due to notify_release_stages :#{configuration.notify_release_stages.inspect}")
+ return
+ end
- yield(notification) if block_given?
+ report = Report.new(exception, configuration, auto_notify)
- if notification.severity != initial_severity
- notification.severity_reason = {
- :type => Bugsnag::Notification::USER_CALLBACK_SET_SEVERITY
- }
- else
- notification.severity_reason = initial_reason
+ # If this is an auto_notify we yield the block before the any middleware is run
+ yield(report) if block_given? && auto_notify
+ if report.ignore?
+ configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in auto_notify block")
+ return
end
- unless notification.ignore?
- notification.deliver
- notification
- else
- false
+ # Run internal middleware
+ configuration.internal_middleware.run(report)
+ if report.ignore?
+ configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in internal middlewares")
+ return
end
- end
- alias_method :notify_or_ignore, :notify
-
- # Auto notify of an exception, called from rails and rack exception
- # rescuers, unless auto notification is disabled, or we should ignore this
- # error class
- def auto_notify(exception, overrides=nil, request_data=nil, &block)
- overrides ||= {}
- overrides[:severity] = "error" unless overrides.has_key? :severity
- overrides[:unhandled] = true unless overrides.has_key? :unhandled
- notify_or_ignore(exception, overrides, request_data, &block) if configuration.auto_notify
- end
- # Log wrapper
- def log(message)
- configuration.logger.info("#{LOG_PREFIX}#{message}")
- end
+ # Store before_middleware severity reason for future reference
+ initial_severity = report.severity
+ initial_reason = report.severity_reason
- # Warning logger
- def warn(message)
- configuration.logger.warn("#{LOG_PREFIX}#{message}")
- end
+ # Run users middleware
+ configuration.middleware.run(report) do
+ if report.ignore?
+ configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in user provided middleware")
+ return
+ end
+
+ # If this is not an auto_notify then the block was provided by the user. This should be the last
+ # block that is run as it is the users "most specific" block.
+ yield(report) if block_given? && !auto_notify
+ if report.ignore?
+ configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in user provided block")
+ return
+ end
+
+ # Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case
+ if report.severity != initial_severity
+ report.severity_reason = {
+ :type => Report::USER_CALLBACK_SET_SEVERITY
+ }
+ else
+ report.severity_reason = initial_reason
+ end
- # Debug logger
- def debug(message)
- configuration.logger.info("#{LOG_PREFIX}#{message}") if configuration.debug
+ # Deliver
+ configuration.info("Notifying #{configuration.endpoint} of #{report.exceptions.last[:errorClass]}")
+ payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(report.as_json))
+ configuration.debug("Payload: #{payload_string}")
+ Bugsnag::Delivery[configuration.delivery_method].deliver(configuration.endpoint, payload_string, configuration)
+ end
end
# Configuration getters
@@ -121,34 +117,16 @@ def configuration
@configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new }
end
- # Set "per-request" data, temporal data for use in bugsnag middleware
- def set_request_data(key, value)
- Bugsnag.configuration.set_request_data(key, value)
- end
-
- # Clear all "per-request" data, temporal data for use in bugsnag middleware
- # This method should be called after each distinct request or session ends
- # Eg. After completing a page request in a web app
- def clear_request_data
- Bugsnag.configuration.clear_request_data
- end
-
# Allow access to "before notify" callbacks
def before_notify_callbacks
Bugsnag.configuration.request_data[:before_callbacks] ||= []
end
-
- # Allow access to "after notify" callbacks
- def after_notify_callbacks
- Bugsnag.configuration.request_data[:after_callbacks] ||= []
- end
end
end
-require "bugsnag/railtie" if defined?(Rails::Railtie)
[:resque, :sidekiq, :mailman, :delayed_job, :shoryuken, :que].each do |integration|
begin
- require "bugsnag/#{integration}"
+ require "bugsnag/integrations/#{integration}"
rescue LoadError
end
end
diff --git a/lib/bugsnag/capistrano.rb b/lib/bugsnag/capistrano.rb
deleted file mode 100644
index 1b03a127e..000000000
--- a/lib/bugsnag/capistrano.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require "bugsnag"
-
-if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
- load File.expand_path('../tasks/bugsnag.cap', __FILE__)
-else
- require_relative 'capistrano2'
-end
diff --git a/lib/bugsnag/capistrano2.rb b/lib/bugsnag/capistrano2.rb
deleted file mode 100644
index 83662a4b3..000000000
--- a/lib/bugsnag/capistrano2.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module Bugsnag
- module Capistrano
- def self.load_into(configuration)
- configuration.load do
- after "deploy", "bugsnag:deploy"
- after "deploy:migrations", "bugsnag:deploy"
-
- namespace :bugsnag do
- desc "Notify Bugsnag that new production code has been deployed"
- task :deploy, :except => { :no_release => true }, :on_error => :continue do
- begin
- Bugsnag::Deploy.notify({
- :api_key => fetch(:bugsnag_api_key, ENV["BUGSNAG_API_KEY"]),
- :release_stage => ENV["BUGSNAG_RELEASE_STAGE"] || fetch(:rails_env, "production"),
- :revision => fetch(:current_revision, ENV["BUGSNAG_REVISION"]),
- :repository => fetch(:repository, ENV["BUGSNAG_REPOSITORY"]),
- :branch => fetch(:branch, ENV["BUGSNAG_BRANCH"],
- :app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]))
- })
- rescue
- logger.important("Bugsnag deploy notification failed, #{$!.inspect}")
- end
-
- logger.info "Bugsnag deploy notification complete."
- end
- end
- end
- end
- end
-end
-
-Bugsnag::Capistrano.load_into(Capistrano::Configuration.instance) if Capistrano::Configuration.instance
diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb
index c5c9bb9e4..c25703283 100644
--- a/lib/bugsnag/configuration.rb
+++ b/lib/bugsnag/configuration.rb
@@ -2,6 +2,11 @@
require "socket"
require "logger"
require "bugsnag/middleware_stack"
+require "bugsnag/middleware/callbacks"
+require "bugsnag/middleware/exception_meta_data"
+require "bugsnag/middleware/ignore_error_class"
+require "bugsnag/middleware/suggestion_data"
+require "bugsnag/middleware/classify_error"
module Bugsnag
class Configuration
@@ -9,35 +14,30 @@ class Configuration
attr_accessor :release_stage
attr_accessor :notify_release_stages
attr_accessor :auto_notify
- attr_accessor :use_ssl
attr_accessor :ca_file
attr_accessor :send_environment
attr_accessor :send_code
attr_accessor :project_root
- attr_accessor :vendor_paths
attr_accessor :app_version
attr_accessor :app_type
- attr_accessor :params_filters
- attr_accessor :ignore_user_agents
+ attr_accessor :meta_data_filters
attr_accessor :endpoint
attr_accessor :logger
attr_accessor :middleware
attr_accessor :internal_middleware
- attr_accessor :delay_with_resque
- attr_accessor :debug
attr_accessor :proxy_host
attr_accessor :proxy_port
attr_accessor :proxy_user
attr_accessor :proxy_password
attr_accessor :timeout
attr_accessor :hostname
- attr_writer :ignore_classes
+ attr_accessor :ignore_classes
+ API_KEY_REGEX = /[0-9a-f]{32}/i
THREAD_LOCAL_NAME = "bugsnag_req_data"
+ DEFAULT_ENDPOINT = "https://notify.bugsnag.com"
- DEFAULT_ENDPOINT = "notify.bugsnag.com"
-
- DEFAULT_PARAMS_FILTERS = [
+ DEFAULT_META_DATA_FILTERS = [
/authorization/i,
/cookie/i,
/password/i,
@@ -45,36 +45,39 @@ class Configuration
"rack.request.form_vars"
].freeze
- DEFAULT_IGNORE_USER_AGENTS = [].freeze
-
- DEFAULT_DELIVERY_METHOD = :thread_queue
-
def initialize
@mutex = Mutex.new
# Set up the defaults
self.auto_notify = true
- self.use_ssl = true
self.send_environment = false
self.send_code = true
- self.params_filters = Set.new(DEFAULT_PARAMS_FILTERS)
- self.ignore_classes = Set.new()
- self.ignore_user_agents = Set.new(DEFAULT_IGNORE_USER_AGENTS)
+ self.meta_data_filters = Set.new(DEFAULT_META_DATA_FILTERS)
+ self.ignore_classes = Set.new([])
self.endpoint = DEFAULT_ENDPOINT
self.hostname = default_hostname
self.timeout = 15
- self.vendor_paths = [%r{vendor/}]
self.notify_release_stages = nil
# Read the API key from the environment
self.api_key = ENV["BUGSNAG_API_KEY"]
+ # Read NET::HTTP proxy environment variable
+ self.proxy_host = ENV["http_proxy"]
+
# Set up logging
self.logger = Logger.new(STDOUT)
- self.logger.level = Logger::WARN
+ self.logger.level = Logger::INFO
+ self.logger.formatter = proc do |severity, datetime, progname, msg|
+ "** [Bugsnag] #{datetime}: #{msg}\n"
+ end
# Configure the bugsnag middleware stack
self.internal_middleware = Bugsnag::MiddlewareStack.new
+ self.internal_middleware.use Bugsnag::Middleware::ExceptionMetaData
+ self.internal_middleware.use Bugsnag::Middleware::IgnoreErrorClass
+ self.internal_middleware.use Bugsnag::Middleware::SuggestionData
+ self.internal_middleware.use Bugsnag::Middleware::ClassifyError
self.middleware = Bugsnag::MiddlewareStack.new
self.middleware.use Bugsnag::Middleware::Callbacks
@@ -85,7 +88,7 @@ def initialize
# notification endpoint.
#
def delivery_method
- @delivery_method || @default_delivery_method || DEFAULT_DELIVERY_METHOD
+ @delivery_method || @default_delivery_method || :thread_queue
end
##
@@ -104,13 +107,12 @@ def default_delivery_method=(delivery_method)
@default_delivery_method = delivery_method
end
- # Accept both String and Class instances as an ignored class
- def ignore_classes
- @mutex.synchronize { @ignore_classes.map! { |klass| klass.is_a?(Class) ? klass.name : klass } }
+ def should_notify_release_stage?
+ @release_stage.nil? || @notify_release_stages.nil? || @notify_release_stages.include?(@release_stage)
end
- def should_notify?
- @release_stage.nil? || @notify_release_stages.nil? || @notify_release_stages.include?(@release_stage)
+ def valid_api_key?
+ !api_key.nil? && api_key =~ API_KEY_REGEX
end
def request_data
@@ -129,6 +131,20 @@ def clear_request_data
Thread.current[THREAD_LOCAL_NAME] = nil
end
+ def info(message)
+ logger.info(message)
+ end
+
+ # Warning logger
+ def warn(message)
+ logger.warn(message)
+ end
+
+ # Debug logger
+ def debug(message)
+ logger.debug(message)
+ end
+
private
def default_hostname
diff --git a/lib/bugsnag/delay/resque.rb b/lib/bugsnag/delay/resque.rb
deleted file mode 100644
index 610bd137e..000000000
--- a/lib/bugsnag/delay/resque.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module Bugsnag
- module Delay
- class Resque
- @queue = "bugsnag"
- def self.perform(*args)
- Bugsnag::Notification.deliver_exception_payload_without_resque(*args)
- end
- end
- end
-end
-
-Bugsnag::Notification.class_eval do
- class << self
- def deliver_exception_payload_with_resque(*args)
- Resque.enqueue(Bugsnag::Delay::Resque, *args)
- end
-
- alias_method :deliver_exception_payload_without_resque, :deliver_exception_payload
- alias_method :deliver_exception_payload, :deliver_exception_payload_with_resque
- end
-end
\ No newline at end of file
diff --git a/lib/bugsnag/delivery.rb b/lib/bugsnag/delivery.rb
index 2ce55fbc8..a78577dd1 100644
--- a/lib/bugsnag/delivery.rb
+++ b/lib/bugsnag/delivery.rb
@@ -1,10 +1,19 @@
module Bugsnag
module Delivery
class << self
+ # Add a delivery method to the list of supported methods. Any registered
+ # method can then be used by name in Configuration.
+ #
+ # require 'bugsnag'
+ # Bugsnag::Delivery.register(:my_delivery_queue, MyDeliveryQueue)
+ # Bugsnag.configure do |config|
+ # config.delivery_method = :my_delivery_queue
+ # end
def register(name, delivery_method)
delivery_methods[name.to_sym] = delivery_method
end
+ # Reference a delivery method by name
def [](name)
delivery_methods[name.to_sym]
end
diff --git a/lib/bugsnag/delivery/synchronous.rb b/lib/bugsnag/delivery/synchronous.rb
index bd60b96d5..f678673f2 100644
--- a/lib/bugsnag/delivery/synchronous.rb
+++ b/lib/bugsnag/delivery/synchronous.rb
@@ -10,13 +10,13 @@ class << self
def deliver(url, body, configuration)
begin
response = request(url, body, configuration)
- Bugsnag.debug("Notification to #{url} finished, response was #{response.code}, payload was #{body}")
+ configuration.debug("Notification to #{url} finished, response was #{response.code}, payload was #{body}")
rescue StandardError => e
# KLUDGE: Since we don't re-raise http exceptions, this breaks rspec
raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
- Bugsnag.warn("Notification to #{url} failed, #{e.inspect}")
- Bugsnag.warn(e.backtrace)
+ configuration.warn("Notification to #{url} failed, #{e.inspect}")
+ configuration.warn(e.backtrace)
end
end
@@ -36,8 +36,6 @@ def request(url, body, configuration)
if uri.scheme == "https"
http.use_ssl = true
- # the default in 1.9+, but required for 1.8
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = configuration.ca_file if configuration.ca_file
end
diff --git a/lib/bugsnag/delivery/thread_queue.rb b/lib/bugsnag/delivery/thread_queue.rb
index 18bfd5031..52b65f697 100644
--- a/lib/bugsnag/delivery/thread_queue.rb
+++ b/lib/bugsnag/delivery/thread_queue.rb
@@ -9,10 +9,12 @@ class ThreadQueue < Synchronous
class << self
def deliver(url, body, configuration)
+ @configuration = configuration
+
start_once!
if @queue.length > MAX_OUTSTANDING_REQUESTS
- Bugsnag.warn("Dropping notification, #{@queue.length} outstanding requests")
+ @configuration.warn("Dropping notification, #{@queue.length} outstanding requests")
return
end
@@ -38,7 +40,7 @@ def start_once!
end
at_exit do
- Bugsnag.warn("Waiting for #{@queue.length} outstanding request(s)") unless @queue.empty?
+ @configuration.warn("Waiting for #{@queue.length} outstanding request(s)") unless @queue.empty?
@queue.push STOP
worker_thread.join
end
diff --git a/lib/bugsnag/deploy.rb b/lib/bugsnag/deploy.rb
deleted file mode 100644
index 8c5fb6d58..000000000
--- a/lib/bugsnag/deploy.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require "json"
-
-module Bugsnag
- class Deploy
- def self.notify(opts = {})
-
- configuration = Bugsnag.configuration.dup
-
- # update configuration based on parameters passed in
- [:api_key, :app_version, :release_stage, :endpoint, :use_ssl,
- :proxy_host, :proxy_port, :proxy_user, :proxy_password].each do |param|
- unless opts[param].nil?
- configuration.send :"#{param}=", opts[param]
- end
- end
-
- endpoint = (configuration.use_ssl ? "https://" : "http://") + configuration.endpoint + "/deploy"
-
- parameters = {
- "apiKey" => configuration.api_key,
- "releaseStage" => configuration.release_stage,
- "appVersion" => configuration.app_version,
- "revision" => opts[:revision],
- "repository" => opts[:repository],
- "branch" => opts[:branch],
- "provider" => opts[:provider]
- }.reject {|k,v| v == nil}
-
- raise RuntimeError.new("No API key found when notifying of deploy") if !parameters["apiKey"] || parameters["apiKey"].empty?
-
- payload_string = ::JSON.dump(parameters)
- Bugsnag::Delivery::Synchronous.deliver(endpoint, payload_string, configuration)
- end
- end
-end
diff --git a/lib/bugsnag/helpers.rb b/lib/bugsnag/helpers.rb
index daeb7a4b5..5c12f8df2 100644
--- a/lib/bugsnag/helpers.rb
+++ b/lib/bugsnag/helpers.rb
@@ -1,12 +1,12 @@
require 'uri'
-require 'set' unless defined?(Set)
-require 'json' unless defined?(JSON)
+require 'set'
+require 'json'
module Bugsnag
module Helpers
MAX_STRING_LENGTH = 3072
- MAX_PAYLOAD_LENGTH = 128000
+ MAX_PAYLOAD_LENGTH = 256000
MAX_ARRAY_LENGTH = 40
RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass]
@@ -23,16 +23,9 @@ def self.trim_if_needed(value)
remove_metadata_from_events(reduced_value)
end
- def self.flatten_meta_data(overrides)
- return nil unless overrides
+ private
- meta_data = overrides.delete(:meta_data)
- if meta_data.is_a?(Hash)
- overrides.merge(meta_data)
- else
- overrides
- end
- end
+ TRUNCATION_INFO = '[TRUNCATED]'
# Check if a value is a raw type which should not be trimmed, truncated
# or converted to a string
@@ -40,10 +33,6 @@ def self.is_json_raw_type?(value)
RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil
end
- private
-
- TRUNCATION_INFO = '[TRUNCATED]'
-
# Shorten array until it fits within the payload size limit when serialized
def self.truncate_array(array)
return [] unless array.respond_to?(:slice)
diff --git a/lib/bugsnag/delayed_job.rb b/lib/bugsnag/integrations/delayed_job.rb
similarity index 81%
rename from lib/bugsnag/delayed_job.rb
rename to lib/bugsnag/integrations/delayed_job.rb
index 5c4def190..3b771431d 100644
--- a/lib/bugsnag/delayed_job.rb
+++ b/lib/bugsnag/integrations/delayed_job.rb
@@ -8,9 +8,12 @@
unless defined? Delayed::Plugins::Bugsnag
module Delayed
module Plugins
+ class Bugsnag < Plugin
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "DelayedJob"
+ }
- class Bugsnag < Plugin
module Notify
def error(job, error)
overrides = {
@@ -19,11 +22,9 @@ def error(job, error)
:id => job.id,
},
:severity_reason => {
- :type => ::Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "DelayedJob"
- }
- }
+ :type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES,
+ },
}
if job.respond_to?(:queue) && (queue = job.queue)
overrides[:job][:queue] = queue
@@ -50,7 +51,14 @@ def error(job, error)
overrides[:job][:payload] = p
end
- ::Bugsnag.auto_notify(error, overrides)
+ ::Bugsnag.notify(error, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
+ }
+ report.meta_data.merge! overrides
+ end
super if defined?(super)
end
diff --git a/lib/bugsnag/mailman.rb b/lib/bugsnag/integrations/mailman.rb
similarity index 55%
rename from lib/bugsnag/mailman.rb
rename to lib/bugsnag/integrations/mailman.rb
index 92515b8f5..78eeecfd9 100644
--- a/lib/bugsnag/mailman.rb
+++ b/lib/bugsnag/integrations/mailman.rb
@@ -2,6 +2,11 @@
module Bugsnag
class Mailman
+
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Mailman"
+ }
+
def initialize
Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Mailman)
Bugsnag.configuration.app_type = "mailman"
@@ -9,23 +14,20 @@ def initialize
def call(mail)
begin
-
- Bugsnag.set_request_data :mailman_msg, mail.to_s
-
+ Bugsnag.configuration.set_request_data :mailman_msg, mail.to_s
yield
rescue Exception => ex
raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
- Bugsnag.auto_notify(ex, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Mailman"
- }
+ Bugsnag.notify(ex, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ end
raise
ensure
- Bugsnag.clear_request_data
+ Bugsnag.configuration.clear_request_data
end
end
end
diff --git a/lib/bugsnag/que.rb b/lib/bugsnag/integrations/que.rb
similarity index 75%
rename from lib/bugsnag/que.rb
rename to lib/bugsnag/integrations/que.rb
index 5cdba68f6..ff088570a 100644
--- a/lib/bugsnag/que.rb
+++ b/lib/bugsnag/integrations/que.rb
@@ -3,14 +3,7 @@
begin
job = job.dup # Make sure the original job object is not mutated.
- Bugsnag.auto_notify(error, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Que"
- }
- }
- }) do |notification|
+ Bugsnag.notify(error, true) do |report|
job[:error_count] += 1
# If the job was scheduled using ActiveJob then unwrap the job details for clarity:
@@ -25,11 +18,18 @@
job.merge!(wrapper_job_class: job[:job_class], wrapper_job_id: job[:job_id]).merge!(wrapped_job)
end
- notification.add_tab(:job, job)
+ report.add_tab(:job, job)
+ report.severity = 'error'
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => {
+ :framework => 'Que'
+ }
+ }
end
rescue => e
# Que supresses errors raised by its error handler to avoid killing the worker. Log them somewhere:
- Bugsnag.warn("Failed to notify Bugsnag of error in Que job (#{e.class}): #{e.message} \n#{e.backtrace[0..9].join("\n")}")
+ Bugsnag.configuration.warn("Failed to notify Bugsnag of error in Que job (#{e.class}): #{e.message} \n#{e.backtrace[0..9].join("\n")}")
raise
end
end
@@ -41,4 +41,4 @@
Bugsnag.configuration.app_type ||= "que"
Que.error_handler = handler
end
-end
+end
\ No newline at end of file
diff --git a/lib/bugsnag/rack.rb b/lib/bugsnag/integrations/rack.rb
similarity index 58%
rename from lib/bugsnag/rack.rb
rename to lib/bugsnag/integrations/rack.rb
index 41bd15231..491663f44 100644
--- a/lib/bugsnag/rack.rb
+++ b/lib/bugsnag/integrations/rack.rb
@@ -1,11 +1,8 @@
module Bugsnag
class Rack
- SEVERITY_REASON = {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Rack"
- }
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Rack"
}
def initialize(app)
@@ -14,37 +11,41 @@ def initialize(app)
# Configure bugsnag rack defaults
Bugsnag.configure do |config|
# Try to set the release_stage automatically if it hasn't already been set
- config.release_stage ||= release_stage
+ config.release_stage ||= ENV["RACK_ENV"] if ENV["RACK_ENV"]
# Try to set the project_root if it hasn't already been set, or show a warning if we can't
unless config.project_root && !config.project_root.to_s.empty?
if defined?(settings)
config.project_root = settings.root
else
- Bugsnag.warn("You should set your app's project_root (see https://bugsnag.com/docs/notifiers/ruby#project_root).")
+ config.warn("You should set your app's project_root (see https://bugsnag.com/docs/notifiers/ruby#project_root).")
end
end
# Hook up rack-based notification middlewares
config.middleware.insert_before([Bugsnag::Middleware::Rails3Request,Bugsnag::Middleware::Callbacks], Bugsnag::Middleware::RackRequest) if defined?(::Rack)
config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::WardenUser) if defined?(Warden)
- config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
+ config.middleware.insert_before(Bugsnag::Middleware::Callbkacs, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
- Bugsnag.configuration.app_type ||= "rack"
+ config.app_type ||= "rack"
end
end
def call(env)
# Set the request data for bugsnag middleware to use
- Bugsnag.set_request_data(:rack_env, env)
+ Bugsnag.configuration.set_request_data(:rack_env, env)
begin
response = @app.call(env)
rescue Exception => raised
# Notify bugsnag of rack exceptions
- Bugsnag.auto_notify(raised, {
- :severity_reason => SEVERITY_REASON
- })
+ Bugsnag.notify(raised, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => Bugsnag::Rack::FRAMEWORK_ATTRIBUTES
+ }
+ end
# Re-raise the exception
raise
@@ -52,21 +53,19 @@ def call(env)
# Notify bugsnag of rack exceptions
if env["rack.exception"]
- Bugsnag.auto_notify(env["rack.exception"], {
- :severity_reason => SEVERITY_REASON
- })
+ Bugsnag.notify(env["rack.exception"], true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
+ }
+ end
end
response
ensure
# Clear per-request data after processing the each request
- Bugsnag.clear_request_data
- end
-
- private
-
- def release_stage
- ENV["BUGSNAG_RELEASE_STAGE"] || ENV["RACK_ENV"]
+ Bugsnag.configuration.clear_request_data
end
end
end
diff --git a/lib/bugsnag/rails/active_record_rescue.rb b/lib/bugsnag/integrations/rails/active_record_rescue.rb
similarity index 57%
rename from lib/bugsnag/rails/active_record_rescue.rb
rename to lib/bugsnag/integrations/rails/active_record_rescue.rb
index 24c6516e0..65843e872 100644
--- a/lib/bugsnag/rails/active_record_rescue.rb
+++ b/lib/bugsnag/integrations/rails/active_record_rescue.rb
@@ -1,6 +1,9 @@
module Bugsnag::Rails
module ActiveRecordRescue
KINDS = [:commit, :rollback].freeze
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Rails"
+ }
def run_callbacks(kind, *args, &block)
if KINDS.include?(kind)
@@ -8,14 +11,13 @@ def run_callbacks(kind, *args, &block)
super
rescue StandardError => exception
# This exception will NOT be escalated, so notify it here.
- Bugsnag.auto_notify(exception, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Rails"
- }
+ Bugsnag.notify(exception, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ end
raise
end
else
diff --git a/lib/bugsnag/rails/controller_methods.rb b/lib/bugsnag/integrations/rails/controller_methods.rb
similarity index 82%
rename from lib/bugsnag/rails/controller_methods.rb
rename to lib/bugsnag/integrations/rails/controller_methods.rb
index e4ce339f4..76739daa0 100644
--- a/lib/bugsnag/rails/controller_methods.rb
+++ b/lib/bugsnag/integrations/rails/controller_methods.rb
@@ -10,10 +10,6 @@ def before_bugsnag_notify(*methods, &block)
_add_bugsnag_notify_callback(:before_callbacks, *methods, &block)
end
- def after_bugsnag_notify(*methods, &block)
- _add_bugsnag_notify_callback(:after_callbacks, *methods, &block)
- end
-
def _add_bugsnag_notify_callback(callback_key, *methods, &block)
options = methods.last.is_a?(Hash) ? methods.pop : {}
@@ -40,10 +36,5 @@ def _add_bugsnag_notify_callback(callback_key, *methods, &block)
end
end
end
-
- private
- def notify_bugsnag(exception, custom_data=nil)
- Bugsnag.notify(exception, custom_data)
- end
end
end
diff --git a/lib/bugsnag/railtie.rb b/lib/bugsnag/integrations/railtie.rb
similarity index 56%
rename from lib/bugsnag/railtie.rb
rename to lib/bugsnag/integrations/railtie.rb
index 481a433a1..126a4495b 100644
--- a/lib/bugsnag/railtie.rb
+++ b/lib/bugsnag/integrations/railtie.rb
@@ -7,8 +7,13 @@
module Bugsnag
class Railtie < Rails::Railtie
+
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Rails"
+ }
+
rake_tasks do
- require "bugsnag/rake"
+ require "bugsnag/integrations/rake"
load "bugsnag/tasks/bugsnag.rake"
end
@@ -17,14 +22,13 @@ class Railtie < Rails::Railtie
runner do
at_exit do
if $!
- Bugsnag.auto_notify($!, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Rails"
- }
+ Bugsnag.notify($!, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ end
end
end
end
@@ -39,28 +43,27 @@ class Railtie < Rails::Railtie
config.middleware.insert_before Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::Rails3Request
end
- # Auto-load configuration settings from config/bugsnag.yml if it exists
- config_file = ::Rails.root.join("config", "bugsnag.yml")
- config = YAML.load_file(config_file) if File.exist?(config_file)
- Bugsnag.configure(config[::Rails.env] ? config[::Rails.env] : config) if config
-
- ActiveSupport.on_load(:action_controller) do
- require "bugsnag/rails/controller_methods"
- include Bugsnag::Rails::ControllerMethods
+ if defined?(::ActionController::Base)
+ require "bugsnag/integrations/rails/controller_methods"
+ ::ActionController::Base.send(:include, Bugsnag::Rails::ControllerMethods)
+ end
+ if defined?(ActionController::API)
+ require "bugsnag/integrations/rails/controller_methods"
+ ActionController::API.send(:include, Bugsnag::Rails::ControllerMethods)
end
- ActiveSupport.on_load(:active_record) do
- require "bugsnag/rails/active_record_rescue"
- include Bugsnag::Rails::ActiveRecordRescue
+ if defined?(ActiveRecord::Base)
+ require "bugsnag/integrations/rails/active_record_rescue"
+ ActiveRecord::Base.send(:include, Bugsnag::Rails::ActiveRecordRescue)
end
Bugsnag.configuration.app_type = "rails"
end
- # Configure params_filters after initialization, so that rails initializers
+ # Configure meta_data_filters after initialization, so that rails initializers
# may set filter_parameters which will be picked up by Bugsnag.
config.after_initialize do
Bugsnag.configure do |config|
- config.params_filters += ::Rails.configuration.filter_parameters.map do |filter|
+ config.meta_data_filters += ::Rails.configuration.filter_parameters.map do |filter|
case filter
when String, Symbol
/\A#{filter}\z/
diff --git a/lib/bugsnag/rake.rb b/lib/bugsnag/integrations/rake.rb
similarity index 54%
rename from lib/bugsnag/rake.rb
rename to lib/bugsnag/integrations/rake.rb
index 74261a977..f8cb4626f 100644
--- a/lib/bugsnag/rake.rb
+++ b/lib/bugsnag/integrations/rake.rb
@@ -4,25 +4,28 @@
class Rake::Task
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Rake"
+ }
+
def execute_with_bugsnag(args=nil)
Bugsnag.configuration.app_type ||= "rake"
old_task = Bugsnag.configuration.request_data[:bugsnag_running_task]
- Bugsnag.set_request_data :bugsnag_running_task, self
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, self
execute_without_bugsnag(args)
rescue Exception => ex
- Bugsnag.auto_notify(ex, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Rake"
- }
+ Bugsnag.notify(ex, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ end
raise
ensure
- Bugsnag.set_request_data :bugsnag_running_task, old_task
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task
end
alias_method :execute_without_bugsnag, :execute
diff --git a/lib/bugsnag/resque.rb b/lib/bugsnag/integrations/resque.rb
similarity index 76%
rename from lib/bugsnag/resque.rb
rename to lib/bugsnag/integrations/resque.rb
index c93d7c48c..28be34ba5 100644
--- a/lib/bugsnag/resque.rb
+++ b/lib/bugsnag/integrations/resque.rb
@@ -3,6 +3,11 @@
module Bugsnag
class Resque < ::Resque::Failure::Base
+
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Resque"
+ }
+
def self.configure(&block)
add_failure_backend
Bugsnag.configure(&block)
@@ -26,16 +31,14 @@ def self.add_failure_backend
end
def save
- Bugsnag.auto_notify(exception, {
- :context => "#{payload['class']}@#{queue}",
- :payload => payload,
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Resque"
- }
+ Bugsnag.notify(exception, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload})
+ end
end
end
end
diff --git a/lib/bugsnag/integrations/shoryuken.rb b/lib/bugsnag/integrations/shoryuken.rb
new file mode 100644
index 000000000..921da5be9
--- /dev/null
+++ b/lib/bugsnag/integrations/shoryuken.rb
@@ -0,0 +1,49 @@
+require 'shoryuken'
+
+module Bugsnag
+ class Shoryuken
+
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Shoryuken"
+ }
+
+ def initialize
+ Bugsnag.configure do |config|
+ config.app_type ||= "shoryuken"
+ config.default_delivery_method = :synchronous
+ end
+ end
+
+ def call(_, queue, _, body)
+ begin
+ Bugsnag.before_notify_callbacks << lambda {|report|
+ report.add_tab(:shoryuken, {
+ queue: queue,
+ body: body
+ })
+ }
+
+ yield
+ rescue Exception => ex
+ unless [Interrupt, SystemExit, SignalException].include?(ex.class)
+ Bugsnag.auto_notify(ex, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => Bugsnag::Shoryuken::FRAMEWORK_ATTRIBUTES
+ }
+ end
+ end
+ raise
+ ensure
+ Bugsnag.configuration.clear_request_data
+ end
+ end
+ end
+end
+
+::Shoryuken.configure_server do |config|
+ config.server_middleware do |chain|
+ chain.add ::Bugsnag::Shoryuken
+ end
+end
\ No newline at end of file
diff --git a/lib/bugsnag/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb
similarity index 65%
rename from lib/bugsnag/sidekiq.rb
rename to lib/bugsnag/integrations/sidekiq.rb
index c411db3d8..dbfbe057c 100644
--- a/lib/bugsnag/sidekiq.rb
+++ b/lib/bugsnag/integrations/sidekiq.rb
@@ -2,6 +2,11 @@
module Bugsnag
class Sidekiq
+
+ FRAMEWORK_ATTRIBUTES = {
+ :framework => "Sidekiq"
+ }
+
def initialize
Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq)
Bugsnag.configuration.app_type = "sidekiq"
@@ -11,22 +16,21 @@ def initialize
def call(worker, msg, queue)
begin
# store msg/queue in thread local state to be read by Bugsnag::Middleware::Sidekiq
- Bugsnag.set_request_data :sidekiq, { :msg => msg, :queue => queue }
+ Bugsnag.configuration.set_request_data :sidekiq, { :msg => msg, :queue => queue }
yield
rescue Exception => ex
raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
- Bugsnag.auto_notify(ex, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Sidekiq"
- }
+ Bugsnag.notify(ex, true) do |report|
+ report.severity = "error"
+ report.severity_reason = {
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
+ :attributes => FRAMEWORK_ATTRIBUTES
}
- })
+ end
raise
ensure
- Bugsnag.clear_request_data
+ Bugsnag.configuration.clear_request_data
end
end
end
diff --git a/lib/bugsnag/middleware/callbacks.rb b/lib/bugsnag/middleware/callbacks.rb
index 20e4c1d35..be7c87d93 100644
--- a/lib/bugsnag/middleware/callbacks.rb
+++ b/lib/bugsnag/middleware/callbacks.rb
@@ -4,16 +4,12 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- if notification.request_data[:before_callbacks]
- notification.request_data[:before_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
+ def call(report)
+ if report.request_data[:before_callbacks]
+ report.request_data[:before_callbacks].each {|c| c.call(*[report][0...c.arity]) }
end
- @bugsnag.call(notification)
-
- if notification.request_data[:after_callbacks]
- notification.request_data[:after_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
- end
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/classify_error.rb b/lib/bugsnag/middleware/classify_error.rb
index e6a553110..f42bea595 100644
--- a/lib/bugsnag/middleware/classify_error.rb
+++ b/lib/bugsnag/middleware/classify_error.rb
@@ -18,36 +18,30 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- notification.exceptions.each do |ex|
-
- outer_break = false
+ def call(report)
+ report.raw_exceptions.each do |ex|
ancestor_chain = ex.class.ancestors.select {
- |ancestor| ancestor.is_a?(Class)
+ |ancestor| ancestor.is_a?(Class)
}.map {
|ancestor| ancestor.to_s
}
INFO_CLASSES.each do |info_class|
if ancestor_chain.include?(info_class)
- notification.severity_reason = {
- :type => Bugsnag::Notification::ERROR_CLASS,
+ report.severity_reason = {
+ :type => Bugsnag::Report::ERROR_CLASS,
:attributes => {
:errorClass => info_class
}
}
- notification.severity = 'info'
- outer_break = true
+ report.severity = 'info'
break
end
end
-
- break if outer_break
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
-
\ No newline at end of file
diff --git a/lib/bugsnag/middleware/clearance_user.rb b/lib/bugsnag/middleware/clearance_user.rb
index 1cba19d61..67c8326fd 100644
--- a/lib/bugsnag/middleware/clearance_user.rb
+++ b/lib/bugsnag/middleware/clearance_user.rb
@@ -6,15 +6,15 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- if notification.request_data[:rack_env] &&
- notification.request_data[:rack_env][:clearance] &&
- notification.request_data[:rack_env][:clearance].signed_in? &&
- notification.request_data[:rack_env][:clearance].current_user
+ def call(report)
+ if report.request_data[:rack_env] &&
+ report.request_data[:rack_env]["clearance"] &&
+ report.request_data[:rack_env]["clearance"].signed_in? &&
+ report.request_data[:rack_env]["clearance"].current_user
# Extract useful user information
user = {}
- user_object = notification.request_data[:rack_env][:clearance].current_user
+ user_object = report.request_data[:rack_env]["clearance"].current_user
if user_object
# Build the bugsnag user info from the current user record
COMMON_USER_FIELDS.each do |field|
@@ -22,10 +22,10 @@ def call(notification)
end
end
- notification.user = user unless user.empty?
+ report.user = user unless user.empty?
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/exception_meta_data.rb b/lib/bugsnag/middleware/exception_meta_data.rb
new file mode 100644
index 000000000..4df478860
--- /dev/null
+++ b/lib/bugsnag/middleware/exception_meta_data.rb
@@ -0,0 +1,34 @@
+module Bugsnag::Middleware
+ class ExceptionMetaData
+ def initialize(bugsnag)
+ @bugsnag = bugsnag
+ end
+
+ def call(report)
+ # Apply the user's information attached to the exceptions
+ report.raw_exceptions.each do |exception|
+ if exception.class.include?(Bugsnag::MetaData)
+ if exception.bugsnag_user_id.is_a?(String)
+ report.user = {id: exception.bugsnag_user_id}
+ end
+
+ if exception.bugsnag_context.is_a?(String)
+ report.context = exception.bugsnag_context
+ end
+
+ if exception.bugsnag_grouping_hash.is_a?(String)
+ report.grouping_hash = exception.bugsnag_grouping_hash
+ end
+
+ if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data
+ exception.bugsnag_meta_data.each do |key, value|
+ report.add_tab key, value
+ end
+ end
+ end
+ end
+
+ @bugsnag.call(report)
+ end
+ end
+end
diff --git a/lib/bugsnag/middleware/ignore_error_class.rb b/lib/bugsnag/middleware/ignore_error_class.rb
new file mode 100644
index 000000000..c6e3110a8
--- /dev/null
+++ b/lib/bugsnag/middleware/ignore_error_class.rb
@@ -0,0 +1,21 @@
+module Bugsnag::Middleware
+ class IgnoreErrorClass
+ def initialize(bugsnag)
+ @bugsnag = bugsnag
+ end
+
+ def call(report)
+ ignore_error_class = report.raw_exceptions.any? do |ex|
+ ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.to_set
+
+ report.configuration.ignore_classes.any? do |to_ignore|
+ to_ignore.is_a?(Proc) ? to_ignore.call(ex) : ancestor_chain.include?(to_ignore)
+ end
+ end
+
+ report.ignore! if ignore_error_class
+
+ @bugsnag.call(report)
+ end
+ end
+end
diff --git a/lib/bugsnag/middleware/mailman.rb b/lib/bugsnag/middleware/mailman.rb
index 64d4c814e..65d4ce7db 100644
--- a/lib/bugsnag/middleware/mailman.rb
+++ b/lib/bugsnag/middleware/mailman.rb
@@ -4,10 +4,10 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- mailman_msg = notification.request_data[:mailman_msg]
- notification.add_tab(:mailman, {"message" => mailman_msg}) if mailman_msg
- @bugsnag.call(notification)
+ def call(report)
+ mailman_msg = report.request_data[:mailman_msg]
+ report.add_tab(:mailman, {"message" => mailman_msg}) if mailman_msg
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/rack_request.rb b/lib/bugsnag/middleware/rack_request.rb
index 080c2adcb..031194235 100644
--- a/lib/bugsnag/middleware/rack_request.rb
+++ b/lib/bugsnag/middleware/rack_request.rb
@@ -6,9 +6,9 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- if notification.request_data[:rack_env]
- env = notification.request_data[:rack_env]
+ def call(report)
+ if report.request_data[:rack_env]
+ env = report.request_data[:rack_env]
request = ::Rack::Request.new(env)
@@ -17,10 +17,10 @@ def call(notification)
session = env["rack.session"]
# Set the context
- notification.context = "#{request.request_method} #{request.path}"
+ report.context = "#{request.request_method} #{request.path}"
# Set a sensible default for user_id
- notification.user_id = request.ip
+ report.user["id"] = request.ip
# Build the clean url (hide the port if it is obvious)
url = "#{request.scheme}://#{request.host}"
@@ -28,9 +28,9 @@ def call(notification)
# If app is passed a bad URL, this code will crash attempting to clean it
begin
- url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
+ url << Bugsnag::Cleaner.new(report.configuration.meta_data_filters).clean_url(request.fullpath)
rescue StandardError => stde
- Bugsnag.log "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
+ Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
end
headers = {}
@@ -48,7 +48,7 @@ def call(notification)
end
# Add a request tab
- notification.add_tab(:request, {
+ report.add_tab(:request, {
:url => url,
:httpMethod => request.request_method,
:params => params.to_hash,
@@ -58,23 +58,23 @@ def call(notification)
})
# Add an environment tab
- if notification.configuration.send_environment
- notification.add_tab(:environment, env)
+ if report.configuration.send_environment
+ report.add_tab(:environment, env)
end
# Add a session tab
if session
if session.is_a?(Hash)
# Rails 3
- notification.add_tab(:session, session)
+ report.add_tab(:session, session)
elsif session.respond_to?(:to_hash)
# Rails 4
- notification.add_tab(:session, session.to_hash)
+ report.add_tab(:session, session.to_hash)
end
end
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/rails2_request.rb b/lib/bugsnag/middleware/rails2_request.rb
deleted file mode 100644
index aadd2f212..000000000
--- a/lib/bugsnag/middleware/rails2_request.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module Bugsnag::Middleware
- class Rails2Request
- def initialize(bugsnag)
- @bugsnag = bugsnag
- end
-
- def call(notification)
- if notification.request_data[:rails2_request]
- request = notification.request_data[:rails2_request]
- params = request.parameters || {}
- session_data = request.session.respond_to?(:to_hash) ? request.session.to_hash : request.session.data
-
- # Set the context
- notification.context = "#{params[:controller]}##{params[:action]}"
-
- # Set a sensible default for user_id
- notification.user_id = request.remote_ip if request.respond_to?(:remote_ip)
-
- # Build the clean url
- url = "#{request.protocol}#{request.host}"
- url << ":#{request.port}" unless [80, 443].include?(request.port)
- url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
-
- # Add a request tab
- notification.add_tab(:request, {
- :url => url,
- :params => params.to_hash,
- :controller => params[:controller],
- :action => params[:action]
- })
-
- # Add an environment tab
- if request.env && notification.configuration.send_environment
- notification.add_tab(:environment, request.env)
- end
-
- # Add a session tab
- notification.add_tab(:session, session_data) if session_data
-
- # Add a cookies tab
- notification.add_tab(:cookies, request.cookies) if request.cookies
-
- # Add the rails version
- notification.add_tab(:environment, {
- :railsVersion => Rails::VERSION::STRING
- })
- end
-
- @bugsnag.call(notification)
- end
- end
-end
diff --git a/lib/bugsnag/middleware/rails3_request.rb b/lib/bugsnag/middleware/rails3_request.rb
index 61e7858fd..ac56feede 100644
--- a/lib/bugsnag/middleware/rails3_request.rb
+++ b/lib/bugsnag/middleware/rails3_request.rb
@@ -6,40 +6,40 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- if notification.request_data[:rack_env]
- env = notification.request_data[:rack_env]
+ def call(report)
+ if report.request_data[:rack_env]
+ env = report.request_data[:rack_env]
params = env["action_dispatch.request.parameters"]
client_ip = env["action_dispatch.remote_ip"].to_s rescue SPOOF
if params
# Set the context
- notification.context = "#{params[:controller]}##{params[:action]}"
+ report.context = "#{params[:controller]}##{params[:action]}"
# Augment the request tab
- notification.add_tab(:request, {
+ report.add_tab(:request, {
:railsAction => "#{params[:controller]}##{params[:action]}",
:params => params
})
end
# Use action_dispatch.remote_ip for IP address fields and send request id
- notification.add_tab(:request, {
+ report.add_tab(:request, {
:clientIp => client_ip,
:requestId => env["action_dispatch.request_id"]
})
- notification.user_id = client_ip
+ report.user["id"] = client_ip
# Add the rails version
- if notification.configuration.send_environment
- notification.add_tab(:environment, {
+ if report.configuration.send_environment
+ report.add_tab(:environment, {
:railsVersion => Rails::VERSION::STRING
})
end
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/rake.rb b/lib/bugsnag/middleware/rake.rb
index 2ce641fa3..74c3d68f2 100644
--- a/lib/bugsnag/middleware/rake.rb
+++ b/lib/bugsnag/middleware/rake.rb
@@ -4,20 +4,20 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- task = notification.request_data[:bugsnag_running_task]
+ def call(report)
+ task = report.request_data[:bugsnag_running_task]
if task
- notification.add_tab(:rake_task, {
+ report.add_tab(:rake_task, {
:name => task.name,
:description => task.full_comment,
:arguments => task.arg_description
})
- notification.context ||= task.name
+ report.context ||= task.name
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/sidekiq.rb b/lib/bugsnag/middleware/sidekiq.rb
index 0f37f142c..b35e78b64 100644
--- a/lib/bugsnag/middleware/sidekiq.rb
+++ b/lib/bugsnag/middleware/sidekiq.rb
@@ -4,13 +4,13 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- sidekiq = notification.request_data[:sidekiq]
+ def call(report)
+ sidekiq = report.request_data[:sidekiq]
if sidekiq
- notification.add_tab(:sidekiq, sidekiq)
- notification.context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
+ report.add_tab(:sidekiq, sidekiq)
+ report.context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
end
diff --git a/lib/bugsnag/middleware/suggestion_data.rb b/lib/bugsnag/middleware/suggestion_data.rb
new file mode 100644
index 000000000..696af1171
--- /dev/null
+++ b/lib/bugsnag/middleware/suggestion_data.rb
@@ -0,0 +1,30 @@
+module Bugsnag::Middleware
+ class SuggestionData
+
+ CAPTURE_REGEX = /Did you mean\?([\s\S]+)$/
+ DELIMITER = "\n"
+
+ def initialize(bugsnag)
+ @bugsnag = bugsnag
+ end
+
+ def call(report)
+ matches = []
+ report.raw_exceptions.each do |exception|
+ match = CAPTURE_REGEX.match(exception.message)
+ next unless match
+
+ suggestions = match.captures[0].split(DELIMITER)
+ matches.concat suggestions.map{ |suggestion| suggestion.strip }
+ end
+
+ if matches.size == 1
+ report.add_tab(:error, {:suggestion => matches.first})
+ elsif matches.size > 1
+ report.add_tab(:error, {:suggestions => matches})
+ end
+
+ @bugsnag.call(report)
+ end
+ end
+end
diff --git a/lib/bugsnag/middleware/warden_user.rb b/lib/bugsnag/middleware/warden_user.rb
index 4fd4d0633..1c4ed59b8 100644
--- a/lib/bugsnag/middleware/warden_user.rb
+++ b/lib/bugsnag/middleware/warden_user.rb
@@ -7,9 +7,9 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- if notification.request_data[:rack_env] && notification.request_data[:rack_env]["warden"]
- env = notification.request_data[:rack_env]
+ def call(report)
+ if report.request_data[:rack_env] && report.request_data[:rack_env]["warden"]
+ env = report.request_data[:rack_env]
session = env["rack.session"] || {}
# Find all warden user scopes
@@ -29,11 +29,11 @@ def call(notification)
end
# We merge the first warden scope down, so that it is the main "user" for the request
- notification.user = user unless user.empty?
+ report.user = user unless user.empty?
end
end
- @bugsnag.call(notification)
+ @bugsnag.call(report)
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/bugsnag/middleware_stack.rb b/lib/bugsnag/middleware_stack.rb
index 0f788f744..61bec481e 100644
--- a/lib/bugsnag/middleware_stack.rb
+++ b/lib/bugsnag/middleware_stack.rb
@@ -63,7 +63,7 @@ def method_missing(method, *args, &block)
end
# Runs the middleware stack and calls
- def run(notification)
+ def run(report)
# The final lambda is the termination of the middleware stack. It calls deliver on the notification
lambda_has_run = false
notify_lambda = lambda do |notif|
@@ -73,19 +73,19 @@ def run(notification)
begin
# We reverse them, so we can call "call" on the first middleware
- middleware_procs.reverse.inject(notify_lambda) { |n,e| e.call(n) }.call(notification)
+ middleware_procs.reverse.inject(notify_lambda) { |n,e| e.call(n) }.call(report)
rescue StandardError => e
# KLUDGE: Since we don't re-raise middleware exceptions, this breaks rspec
raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
# We dont notify, as we dont want to loop forever in the case of really broken middleware, we will
# still send this notify
- Bugsnag.warn "Bugsnag middleware error: #{e}"
- Bugsnag.log "Middleware error stacktrace: #{e.backtrace.inspect}"
+ Bugsnag.configuration.warn "Bugsnag middleware error: #{e}"
+ Bugsnag.configuration.warn "Middleware error stacktrace: #{e.backtrace.inspect}"
end
# Ensure that the deliver has been performed, and no middleware has botched it
- notify_lambda.call(notification) unless lambda_has_run
+ notify_lambda.call(report) unless lambda_has_run
end
private
diff --git a/lib/bugsnag/notification.rb b/lib/bugsnag/notification.rb
deleted file mode 100644
index 5290ab988..000000000
--- a/lib/bugsnag/notification.rb
+++ /dev/null
@@ -1,506 +0,0 @@
-require "json"
-
-if RUBY_VERSION =~ /^1\.8/
- begin
- require "iconv"
- rescue LoadError
- end
-end
-
-require "pathname"
-
-module Bugsnag
- class Notification
- NOTIFIER_NAME = "Ruby Bugsnag Notifier"
- NOTIFIER_VERSION = Bugsnag::VERSION
- NOTIFIER_URL = "http://www.bugsnag.com"
-
- HANDLED_EXCEPTION = "handledException"
- UNHANDLED_EXCEPTION = "unhandledException"
- UNHANDLED_EXCEPTION_MIDDLEWARE = "unhandledExceptionMiddleware"
- ERROR_CLASS = "errorClass"
- USER_SPECIFIED_SEVERITY = "userSpecifiedSeverity"
- USER_CALLBACK_SET_SEVERITY = "userCallbackSetSeverity"
-
- API_KEY_REGEX = /[0-9a-f]{32}/i
-
- # e.g. "org/jruby/RubyKernel.java:1264:in `catch'"
- BACKTRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/
-
- # e.g. "org.jruby.Ruby.runScript(Ruby.java:807)"
- JAVA_BACKTRACE_REGEX = /^(.*)\((.*)(?::([0-9]+))?\)$/
-
- MAX_EXCEPTIONS_TO_UNWRAP = 5
-
- SUPPORTED_SEVERITIES = ["error", "warning", "info"]
-
- CURRENT_PAYLOAD_VERSION = "2"
-
- attr_accessor :context
- attr_reader :user
- attr_reader :severity
- attr_accessor :severity_reason
- attr_accessor :configuration
- attr_accessor :meta_data
-
- class << self
- def deliver_exception_payload(url, payload, configuration=Bugsnag.configuration, delivery_method=nil)
- payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(payload))
- delivery_method = delivery_method || configuration.delivery_method
- Bugsnag::Delivery[delivery_method].deliver(url, payload_string, configuration)
- end
- end
-
- def initialize(exception, configuration, overrides = nil, request_data = nil)
- @configuration = configuration
- @overrides = Bugsnag::Helpers.flatten_meta_data(overrides) || {}
- @request_data = request_data
- @meta_data = {}
- @user = {}
- @should_ignore = false
- @severity = nil
- @unhandled = false
- @severity_reason = nil
- @grouping_hash = nil
- @delivery_method = nil
-
- if @overrides.key? :unhandled
- @unhandled = @overrides[:unhandled]
- @overrides.delete :unhandled
- end
-
- valid_severity = @overrides.key?(:severity) && SUPPORTED_SEVERITIES.include?(@overrides[:severity])
- has_reason = @overrides.key? :severity_reason
-
- if valid_severity && has_reason
- @severity = @overrides[:severity]
- @severity_reason = @overrides[:severity_reason]
- elsif valid_severity
- @severity = @overrides[:severity]
- @severity_reason = {
- :type => USER_SPECIFIED_SEVERITY
- }
- elsif has_reason
- @severity_reason = @overrides[:severity_reason]
- else
- @severity_reason = {
- :type => HANDLED_EXCEPTION
- }
- end
-
- @overrides.delete :severity_reason
- @overrides.delete :severity
-
- if @overrides.key? :grouping_hash
- self.grouping_hash = @overrides[:grouping_hash]
- @overrides.delete :grouping_hash
- end
-
- if @overrides.key? :api_key
- self.api_key = @overrides[:api_key]
- @overrides.delete :api_key
- end
-
- if @overrides.key? :delivery_method
- @delivery_method = @overrides[:delivery_method]
- @overrides.delete :delivery_method
- end
-
- # Unwrap exceptions
- @exceptions = []
-
- ex = exception
- while ex != nil && !@exceptions.include?(ex) && @exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP
-
- unless ex.is_a? Exception
- if ex.respond_to?(:to_exception)
- ex = ex.to_exception
- elsif ex.respond_to?(:exception)
- ex = ex.exception
- end
- end
-
- unless ex.is_a?(Exception) || (defined?(Java::JavaLang::Throwable) && ex.is_a?(Java::JavaLang::Throwable))
- Bugsnag.warn("Converting non-Exception to RuntimeError: #{ex.inspect}")
- ex = RuntimeError.new(ex.to_s)
- ex.set_backtrace caller
- end
-
- @exceptions << ex
-
- if ex.respond_to?(:cause) && ex.cause
- ex = ex.cause
- elsif ex.respond_to?(:continued_exception) && ex.continued_exception
- ex = ex.continued_exception
- elsif ex.respond_to?(:original_exception) && ex.original_exception
- ex = ex.original_exception
- else
- ex = nil
- end
- end
- end
-
- # Add a single value as custom data, to this notification
- def add_custom_data(name, value)
- @meta_data[:custom] ||= {}
- @meta_data[:custom][name.to_sym] = value
- end
-
- # Add a new tab to this notification
- def add_tab(name, value)
- return if name.nil?
-
- if value.is_a? Hash
- @meta_data[name.to_sym] ||= {}
- @meta_data[name.to_sym].merge! value
- else
- self.add_custom_data(name, value)
- Bugsnag.warn "Adding a tab requires a hash, adding to custom tab instead (name=#{name})"
- end
- end
-
- # Remove a tab from this notification
- def remove_tab(name)
- return if name.nil?
-
- @meta_data.delete(name.to_sym)
- end
-
- def user_id=(user_id)
- @user[:id] = user_id
- end
-
- def user_id
- @user[:id]
- end
-
- def user=(user = {})
- return unless user.is_a? Hash
- @user.merge!(user).delete_if{|k,v| v == nil}
- end
-
- def severity=(severity)
- @severity = severity if SUPPORTED_SEVERITIES.include?(severity)
- end
-
- def severity
- @severity || "warning"
- end
-
- def payload_version
- CURRENT_PAYLOAD_VERSION
- end
-
- def grouping_hash=(grouping_hash)
- @grouping_hash = grouping_hash
- end
-
- def grouping_hash
- @grouping_hash || nil
- end
-
- def api_key=(api_key)
- @api_key = api_key
- end
-
- def api_key
- @api_key ||= @configuration.api_key
- end
-
- # Deliver this notification to bugsnag.com Also runs through the middleware as required.
- def deliver
- return unless @configuration.should_notify?
-
- # Check we have at least an api_key
- if api_key.nil?
- Bugsnag.warn "No API key configured, couldn't notify"
- return
- elsif api_key !~ API_KEY_REGEX
- Bugsnag.warn "Your API key (#{api_key}) is not valid, couldn't notify"
- return
- end
-
- # Warn if no release_stage is set
- Bugsnag.warn "You should set your app's release_stage (see https://bugsnag.com/docs/notifiers/ruby#release_stage)." unless @configuration.release_stage
-
- @configuration.internal_middleware.run(self)
-
- exceptions.each do |exception|
- if exception.class.include?(Bugsnag::MetaData)
- if exception.bugsnag_user_id.is_a?(String)
- self.user_id = exception.bugsnag_user_id
- end
- if exception.bugsnag_context.is_a?(String)
- self.context = exception.bugsnag_context
- end
- if exception.bugsnag_grouping_hash.is_a?(String)
- self.grouping_hash = exception.bugsnag_grouping_hash
- end
- end
- end
-
- [:user_id, :context, :user, :grouping_hash].each do |symbol|
- if @overrides[symbol]
- self.send("#{symbol}=", @overrides[symbol])
- @overrides.delete symbol
- end
- end
-
- # make meta_data available to public middleware
- @meta_data = generate_meta_data(@exceptions, @overrides)
-
- initial_severity = self.severity
-
- # Run the middleware here (including Bugsnag::Middleware::Callbacks)
- # at the end of the middleware stack, execute the actual notification delivery
- @configuration.middleware.run(self) do
- # This supports self.ignore! for before_notify_callbacks.
- return if @should_ignore
-
- # Check to see if the severity has been changed
- if initial_severity != self.severity
-
- end
-
- # Build the endpoint url
- endpoint = (@configuration.use_ssl ? "https://" : "http://") + @configuration.endpoint
- Bugsnag.log("Notifying #{endpoint} of #{@exceptions.last.class}")
-
- # Deliver the payload
- self.class.deliver_exception_payload(endpoint, build_exception_payload, @configuration, @delivery_method)
- end
- end
-
- # Build an exception payload
- def build_exception_payload
- # Build the payload's exception event
- payload_event = {
- :app => {
- :version => @configuration.app_version,
- :releaseStage => @configuration.release_stage,
- :type => @configuration.app_type
- },
- :context => self.context,
- :user => @user,
- :payloadVersion => payload_version,
- :exceptions => exception_list,
- :severity => self.severity,
- :unhandled => @unhandled,
- :severityReason => @severity_reason,
- :groupingHash => self.grouping_hash,
- }
-
- payload_event[:device] = {:hostname => @configuration.hostname} if @configuration.hostname
-
- # cleanup character encodings
- payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event)
-
- # filter out sensitive values in (and cleanup encodings) metaData
- payload_event[:metaData] = Bugsnag::Cleaner.new(@configuration.params_filters).clean_object(@meta_data)
- payload_event.reject! {|k,v| v.nil? }
-
- # return the payload hash
- {
- :apiKey => api_key,
- :notifier => {
- :name => NOTIFIER_NAME,
- :version => NOTIFIER_VERSION,
- :url => NOTIFIER_URL
- },
- :events => [payload_event]
- }
- end
-
- def ignore?
- @should_ignore || ignore_exception_class? || ignore_user_agent?
- end
-
- def request_data
- @request_data || Bugsnag.configuration.request_data
- end
-
- def exceptions
- @exceptions
- end
-
- def ignore!
- @should_ignore = true
- end
-
- private
-
- def ignore_exception_class?
- @exceptions.any? do |ex|
- ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.map { |ancestor| error_class(ancestor) }.to_set
-
- @configuration.ignore_classes.any? do |to_ignore|
- to_ignore.is_a?(Proc) ? to_ignore.call(ex) : ancestor_chain.include?(to_ignore)
- end
- end
- end
-
- def ignore_user_agent?
- if @configuration.request_data && @configuration.request_data[:rack_env] && (agent = @configuration.request_data[:rack_env]["HTTP_USER_AGENT"])
- @configuration.ignore_user_agents.any? do |to_ignore|
- agent =~ to_ignore
- end
- end
- end
-
- # Generate the meta data from both the request configuration, the overrides and the exceptions for this notification
- def generate_meta_data(exceptions, overrides)
- # Copy the request meta data so we dont edit it by mistake
- meta_data = @meta_data.dup
-
- exceptions.each do |exception|
- if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data
- exception.bugsnag_meta_data.each do |key, value|
- add_to_meta_data key, value, meta_data
- end
- end
- end
-
- overrides.each do |key, value|
- add_to_meta_data key, value, meta_data
- end
-
- meta_data
- end
-
- def add_to_meta_data(key, value, meta_data)
- # If its a hash, its a tab so we can just add it providing its not reserved
- if value.is_a? Hash
- key = key.to_sym
-
- if meta_data[key]
- # If its a clash, merge with the existing data
- meta_data[key].merge! value
- else
- # Add it as is if its not special
- meta_data[key] = value
- end
- else
- meta_data[:custom] ||= {}
- meta_data[:custom][key] = value
- end
- end
-
- def exception_list
- @exceptions.map do |exception|
- {
- :errorClass => error_class(exception),
- :message => exception.message,
- :stacktrace => stacktrace(exception.backtrace)
- }
- end
- end
-
- def error_class(exception)
- # The "Class" check is for some strange exceptions like Timeout::Error
- # which throw the error class instead of an instance
- (exception.is_a? Class) ? exception.name : exception.class.name
- end
-
- def stacktrace(backtrace)
- backtrace = caller if !backtrace || backtrace.empty?
- backtrace.map do |trace|
- if trace.match(BACKTRACE_LINE_REGEX)
- file, line_str, method = [$1, $2, $3]
- elsif trace.match(JAVA_BACKTRACE_REGEX)
- method, file, line_str = [$1, $2, $3]
- end
-
- # Parse the stacktrace line
-
- # Skip stacktrace lines inside lib/bugsnag
- next(nil) if file.nil? || file =~ %r{lib/bugsnag(/|\.rb)}
-
- # Expand relative paths
- p = Pathname.new(file)
- if p.relative?
- file = p.realpath.to_s rescue file
- end
-
- # Generate the stacktrace line hash
- trace_hash = {}
- trace_hash[:inProject] = true if in_project?(file)
- trace_hash[:lineNumber] = line_str.to_i
-
- if @configuration.send_code
- trace_hash[:code] = code(file, trace_hash[:lineNumber])
- end
-
- # Clean up the file path in the stacktrace
- if defined?(Bugsnag.configuration.project_root) && Bugsnag.configuration.project_root.to_s != ''
- file.sub!(/#{Bugsnag.configuration.project_root}\//, "")
- end
-
- # Strip common gem path prefixes
- if defined?(Gem)
- file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
- end
-
- trace_hash[:file] = file
-
- # Add a method if we have it
- trace_hash[:method] = method if method && (method =~ /^__bind/).nil?
-
- if trace_hash[:file] && !trace_hash[:file].empty?
- trace_hash
- else
- nil
- end
- end.compact
- end
-
- def in_project?(line)
- return false if @configuration.vendor_paths && @configuration.vendor_paths.any? do |vendor_path|
- if vendor_path.is_a?(String)
- line.include?(vendor_path)
- else
- line =~ vendor_path
- end
- end
- @configuration.project_root && line.start_with?(@configuration.project_root.to_s)
- end
-
- def code(file, line_number, num_lines = 7)
- code_hash = {}
-
- from_line = [line_number - num_lines, 1].max
-
- # don't try and open '(irb)' or '-e'
- return unless File.exist?(file)
-
- # Populate code hash with line numbers and code lines
- File.open(file) do |f|
- current_line_number = 0
- f.each_line do |line|
- current_line_number += 1
-
- next if current_line_number < from_line
-
- code_hash[current_line_number] = line[0...200].rstrip
-
- break if code_hash.length >= ( num_lines * 1.5 ).ceil
- end
- end
-
- while code_hash.length > num_lines
- last_line = code_hash.keys.max
- first_line = code_hash.keys.min
-
- if (last_line - line_number) > (line_number - first_line)
- code_hash.delete(last_line)
- else
- code_hash.delete(first_line)
- end
- end
-
- code_hash
- rescue
- Bugsnag.warn("Error fetching code: #{$!.inspect}")
- nil
- end
- end
-end
diff --git a/lib/bugsnag/rails.rb b/lib/bugsnag/rails.rb
deleted file mode 100644
index 451788476..000000000
--- a/lib/bugsnag/rails.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-# Rails 2.x hooks
-# For Rails 3+ hooks, see railtie.rb
-
-require "bugsnag"
-require "bugsnag/rails/controller_methods"
-require "bugsnag/rails/action_controller_rescue"
-require "bugsnag/rails/active_record_rescue"
-require "bugsnag/middleware/rails2_request"
-require "bugsnag/middleware/callbacks"
-
-module Bugsnag
- module Rails
- def self.initialize
- if defined?(ActionController::Base)
- ActionController::Base.send(:include, Bugsnag::Rails::ActionControllerRescue)
- ActionController::Base.send(:include, Bugsnag::Rails::ControllerMethods)
- end
- if defined?(ActiveRecord::Base) && Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("4.3")
- unless ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks) && ActiveRecord::Base.raise_in_transactional_callbacks
- ActiveRecord::Base.send(:include, Bugsnag::Rails::ActiveRecordRescue)
- end
- end
-
- Bugsnag.configure do |config|
- config.logger ||= rails_logger
- config.release_stage = release_stage if release_stage
- config.project_root = rails_root if rails_root
-
- config.middleware.insert_before(Bugsnag::Middleware::Callbacks,Bugsnag::Middleware::Rails2Request)
- end
-
- # Auto-load configuration settings from config/bugsnag.yml if it exists
- config_file = File.join(rails_root, "config", "bugsnag.yml")
- config = YAML.load_file(config_file) if File.exist?(config_file)
- Bugsnag.configure(config[rails_env] ? config[rails_env] : config) if config
-
- Bugsnag.configuration.app_type = "rails"
- end
-
- def self.rails_logger
- if defined?(::Rails.logger)
- rails_logger = ::Rails.logger
- elsif defined?(RAILS_DEFAULT_LOGGER)
- rails_logger = RAILS_DEFAULT_LOGGER
- end
- end
-
- def self.release_stage
- ENV["BUGSNAG_RELEASE_STAGE"] || rails_env
- end
-
- def self.rails_env
- if defined?(::Rails.env)
- ::Rails.env
- elsif defined?(RAILS_ENV)
- RAILS_ENV
- end
- end
-
- def self.rails_root
- if defined?(::Rails.root)
- ::Rails.root
- elsif defined?(RAILS_ROOT)
- RAILS_ROOT
- end
- end
- end
-end
-
-Bugsnag::Rails.initialize
diff --git a/lib/bugsnag/rails/action_controller_rescue.rb b/lib/bugsnag/rails/action_controller_rescue.rb
deleted file mode 100644
index a0c0649fc..000000000
--- a/lib/bugsnag/rails/action_controller_rescue.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-# Rails 2.x only
-module Bugsnag::Rails
- module ActionControllerRescue
-
- SEVERITY_REASON = {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Rails"
- }
- }
-
- def self.included(base)
- base.extend(ClassMethods)
-
- # Hook into rails exception rescue stack
- base.send(:alias_method, :rescue_action_in_public_without_bugsnag, :rescue_action_in_public)
- base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_bugsnag)
-
- base.send(:alias_method, :rescue_action_locally_without_bugsnag, :rescue_action_locally)
- base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_bugsnag)
-
- # Run filters on requests to capture request data
- base.send(:prepend_before_filter, :set_bugsnag_request_data)
- end
-
- private
- def set_bugsnag_request_data
- Bugsnag.clear_request_data
- Bugsnag.set_request_data(:rails2_request, request)
- end
-
- def rescue_action_in_public_with_bugsnag(exception)
- Bugsnag.auto_notify(exception, {
- :severity_reason => SEVERITY_REASON
- })
-
- rescue_action_in_public_without_bugsnag(exception)
- end
-
- def rescue_action_locally_with_bugsnag(exception)
- Bugsnag.auto_notify(exception, {
- :severity_reason => SEVERITY_REASON
- })
-
- rescue_action_locally_without_bugsnag(exception)
- end
-
- module ClassMethods
-
- def self.extended(base)
- base.singleton_class.class_eval do
- alias_method_chain :filter_parameter_logging, :bugsnag
- end
- end
-
- # Rails 2 does parameter filtering via a controller configuration method
- # that dynamically defines a method on the controller, so the configured
- # parameters aren't easily accessible. Intercept these parameters as
- # they're configured so that the Bugsnag configuration can take them
- # into account.
- #
- def filter_parameter_logging_with_bugsnag(*filter_words, &block)
- if filter_words.length > 0
- Bugsnag.configure do |config|
- # Use the same regular expression that Rails parameter filtering uses.
- config.params_filters << Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true)
- end
- end
- filter_parameter_logging_without_bugsnag(*filter_words, &block)
- end
- end
-
- end
-end
\ No newline at end of file
diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb
new file mode 100644
index 000000000..3ed2d78e0
--- /dev/null
+++ b/lib/bugsnag/report.rb
@@ -0,0 +1,187 @@
+require "json"
+require "pathname"
+require "bugsnag/stacktrace"
+
+module Bugsnag
+ class Report
+ NOTIFIER_NAME = "Ruby Bugsnag Notifier"
+ NOTIFIER_VERSION = Bugsnag::VERSION
+ NOTIFIER_URL = "http://www.bugsnag.com"
+
+ UNHANDLED_EXCEPTION = "unhandledException"
+ UNHANDLED_EXCEPTION_MIDDLEWARE = "unhandledExceptionMiddleware"
+ ERROR_CLASS = "errorClass"
+ HANDLED_EXCEPTION = "handledException"
+ USER_SPECIFIED_SEVERITY = "userSpecifiedSeverity"
+ USER_CALLBACK_SET_SEVERITY = "userCallbackSetSeverity"
+
+ MAX_EXCEPTIONS_TO_UNWRAP = 5
+
+ CURRENT_PAYLOAD_VERSION = "2"
+
+ attr_accessor :api_key
+ attr_accessor :app_type
+ attr_accessor :app_version
+ attr_accessor :configuration
+ attr_accessor :context
+ attr_accessor :delivery_method
+ attr_accessor :exceptions
+ attr_accessor :hostname
+ attr_accessor :grouping_hash
+ attr_accessor :meta_data
+ attr_accessor :raw_exceptions
+ attr_accessor :release_stage
+ attr_accessor :severity
+ attr_accessor :severity_reason
+ attr_accessor :user
+
+ def initialize(exception, passed_configuration, auto_notify=false)
+ @should_ignore = false
+ @unhandled = auto_notify
+
+ self.configuration = passed_configuration
+
+ self.raw_exceptions = generate_raw_exceptions(exception)
+ self.exceptions = generate_exception_list
+
+ self.api_key = configuration.api_key
+ self.app_type = configuration.app_type
+ self.app_version = configuration.app_version
+ self.delivery_method = configuration.delivery_method
+ self.hostname = configuration.hostname
+ self.meta_data = {}
+ self.release_stage = configuration.release_stage
+ self.severity = auto_notify ? "error" : "warning"
+ self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION}
+ self.user = {}
+ end
+
+ # Add a new tab to this notification
+ def add_tab(name, value)
+ return if name.nil?
+
+ if value.is_a? Hash
+ meta_data[name] ||= {}
+ meta_data[name].merge! value
+ else
+ meta_data["custom"] = {} unless meta_data["custom"]
+
+ meta_data["custom"][name.to_s] = value
+ end
+ end
+
+ # Remove a tab from this notification
+ def remove_tab(name)
+ return if name.nil?
+
+ meta_data.delete(name)
+ end
+
+ # Build an exception payload
+ def as_json
+ # Build the payload's exception event
+ payload_event = {
+ app: {
+ version: app_version,
+ releaseStage: release_stage,
+ type: app_type
+ },
+ context: context,
+ device: {
+ hostname: hostname
+ },
+ exceptions: exceptions,
+ groupingHash: grouping_hash,
+ payloadVersion: CURRENT_PAYLOAD_VERSION,
+ severity: severity,
+ severityReason: severity_reason,
+ unhandled: @unhandled,
+ user: user
+ }
+
+ # cleanup character encodings
+ payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event)
+
+ # filter out sensitive values in (and cleanup encodings) metaData
+ payload_event[:metaData] = Bugsnag::Cleaner.new(configuration.meta_data_filters).clean_object(meta_data)
+ payload_event.reject! {|k,v| v.nil? }
+
+ # return the payload hash
+ {
+ :apiKey => api_key,
+ :notifier => {
+ :name => NOTIFIER_NAME,
+ :version => NOTIFIER_VERSION,
+ :url => NOTIFIER_URL
+ },
+ :events => [payload_event]
+ }
+ end
+
+ def ignore?
+ @should_ignore
+ end
+
+ def request_data
+ configuration.request_data
+ end
+
+ def ignore!
+ @should_ignore = true
+ end
+
+ private
+
+ def generate_exception_list
+ raw_exceptions.map do |exception|
+ {
+ errorClass: error_class(exception),
+ message: exception.message,
+ stacktrace: Stacktrace.new(exception.backtrace, configuration).to_a
+ }
+ end
+ end
+
+ def error_class(exception)
+ # The "Class" check is for some strange exceptions like Timeout::Error
+ # which throw the error class instead of an instance
+ (exception.is_a? Class) ? exception.name : exception.class.name
+ end
+
+ def generate_raw_exceptions(exception)
+ exceptions = []
+
+ ex = exception
+ while ex != nil && !exceptions.include?(ex) && exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP
+
+ unless ex.is_a? Exception
+ if ex.respond_to?(:to_exception)
+ ex = ex.to_exception
+ elsif ex.respond_to?(:exception)
+ ex = ex.exception
+ end
+ end
+
+ unless ex.is_a?(Exception) || (defined?(Java::JavaLang::Throwable) && ex.is_a?(Java::JavaLang::Throwable))
+ configuration.warn("Converting non-Exception to RuntimeError: #{ex.inspect}")
+ ex = RuntimeError.new(ex.to_s)
+ ex.set_backtrace caller
+ end
+
+ exceptions << ex
+
+ if ex.respond_to?(:cause) && ex.cause
+ ex = ex.cause
+ elsif ex.respond_to?(:continued_exception) && ex.continued_exception
+ ex = ex.continued_exception
+ elsif ex.respond_to?(:original_exception) && ex.original_exception
+ ex = ex.original_exception
+ else
+ ex = nil
+ end
+ end
+
+ exceptions
+ end
+ end
+end
diff --git a/lib/bugsnag/shoryuken.rb b/lib/bugsnag/shoryuken.rb
deleted file mode 100644
index ab5182442..000000000
--- a/lib/bugsnag/shoryuken.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require 'shoryuken'
-
-module Bugsnag
- class Shoryuken
- def initialize
- Bugsnag.configuration.app_type = "shoryuken"
- Bugsnag.configuration.default_delivery_method = :synchronous
- end
-
- def call(_, queue, _, body)
- begin
- Bugsnag.before_notify_callbacks << lambda {|notification|
- notification.add_tab(:shoryuken, {
- queue: queue,
- body: body
- })
- }
-
- yield
- rescue Exception => ex
- Bugsnag.auto_notify(ex, {
- :severity_reason => {
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
- :attributes => {
- :framework => "Shoryuken"
- }
- }
- }) unless [Interrupt, SystemExit, SignalException].include?(ex.class)
- raise
- ensure
- Bugsnag.clear_request_data
- end
- end
- end
-end
-
-::Shoryuken.configure_server do |config|
- config.server_middleware do |chain|
- chain.add ::Bugsnag::Shoryuken
- end
-end
diff --git a/lib/bugsnag/stacktrace.rb b/lib/bugsnag/stacktrace.rb
new file mode 100644
index 000000000..fceabb004
--- /dev/null
+++ b/lib/bugsnag/stacktrace.rb
@@ -0,0 +1,113 @@
+module Bugsnag
+ class Stacktrace
+
+ # e.g. "org/jruby/RubyKernel.java:1264:in `catch'"
+ BACKTRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/
+
+ # e.g. "org.jruby.Ruby.runScript(Ruby.java:807)"
+ JAVA_BACKTRACE_REGEX = /^(.*)\((.*)(?::([0-9]+))?\)$/
+
+ def initialize(backtrace, configuration)
+ @configuration = configuration
+
+ backtrace = caller if !backtrace || backtrace.empty?
+ @processed_backtrace = backtrace.map do |trace|
+ if trace.match(BACKTRACE_LINE_REGEX)
+ file, line_str, method = [$1, $2, $3]
+ elsif trace.match(JAVA_BACKTRACE_REGEX)
+ method, file, line_str = [$1, $2, $3]
+ end
+
+ # Parse the stacktrace line
+
+ # Skip stacktrace lines inside lib/bugsnag
+ next(nil) if file.nil? || file =~ %r{lib/bugsnag(/|\.rb)}
+
+ # Expand relative paths
+ p = Pathname.new(file)
+ if p.relative?
+ file = p.realpath.to_s rescue file
+ end
+
+ # Generate the stacktrace line hash
+ trace_hash = {}
+ trace_hash[:inProject] = true if in_project?(file)
+ trace_hash[:lineNumber] = line_str.to_i
+
+ if configuration.send_code
+ trace_hash[:code] = code(file, trace_hash[:lineNumber])
+ end
+
+ # Clean up the file path in the stacktrace
+ if defined?(@configuration.project_root) && @configuration.project_root.to_s != ''
+ file.sub!(/#{@configuration.project_root}\//, "")
+ end
+
+ # Strip common gem path prefixes
+ if defined?(Gem)
+ file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
+ end
+
+ trace_hash[:file] = file
+
+ # Add a method if we have it
+ trace_hash[:method] = method if method && (method =~ /^__bind/).nil?
+
+ if trace_hash[:file] && !trace_hash[:file].empty?
+ trace_hash
+ else
+ nil
+ end
+ end.compact
+ end
+
+ def to_a
+ @processed_backtrace
+ end
+
+ private
+
+ def in_project?(line)
+ @configuration.project_root && line.start_with?(@configuration.project_root.to_s)
+ end
+
+ def code(file, line_number, num_lines = 7)
+ code_hash = {}
+
+ from_line = [line_number - num_lines, 1].max
+
+ # don't try and open '(irb)' or '-e'
+ return unless File.exist?(file)
+
+ # Populate code hash with line numbers and code lines
+ File.open(file) do |f|
+ current_line_number = 0
+ f.each_line do |line|
+ current_line_number += 1
+
+ next if current_line_number < from_line
+
+ code_hash[current_line_number] = line[0...200].rstrip
+
+ break if code_hash.length >= ( num_lines * 1.5 ).ceil
+ end
+ end
+
+ while code_hash.length > num_lines
+ last_line = code_hash.keys.max
+ first_line = code_hash.keys.min
+
+ if (last_line - line_number) > (line_number - first_line)
+ code_hash.delete(last_line)
+ else
+ code_hash.delete(first_line)
+ end
+ end
+
+ code_hash
+ rescue
+ @configuration.warn("Error fetching code: #{$!.inspect}")
+ nil
+ end
+ end
+end
diff --git a/lib/bugsnag/tasks/bugsnag.cap b/lib/bugsnag/tasks/bugsnag.cap
deleted file mode 100644
index 4a05a7ddd..000000000
--- a/lib/bugsnag/tasks/bugsnag.cap
+++ /dev/null
@@ -1,48 +0,0 @@
-namespace :load do
-
- task :defaults do
-
- set :bugsnag_default_hooks, ->{ true }
-
- end
-
-end
-
-namespace :deploy do
-
- before :starting, :bugsnag_hooks do
- invoke 'bugsnag:add_default_hooks' if fetch(:bugsnag_default_hooks)
- end
-
-end
-
-namespace :bugsnag do
-
- task :add_default_hooks do
- after 'deploy:published', 'bugsnag:deploy'
- end
-
- desc 'Notify Bugsnag that new production code has been deployed'
- task :deploy do
- run_locally do
- begin
- Bugsnag::Deploy.notify({
- :api_key => fetch(:bugsnag_api_key, ENV["BUGSNAG_API_KEY"]),
- :release_stage => fetch(:bugsnag_env) || ENV["BUGSNAG_RELEASE_STAGE"] || fetch(:rails_env) || fetch(:stage) || "production",
- :revision => fetch(:current_revision, ENV["BUGSNAG_REVISION"]),
- :repository => fetch(:repo_url, ENV["BUGSNAG_REPOSITORY"]),
- :branch => fetch(:branch, ENV["BUGSNAG_BRANCH"]),
- :app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]),
- :endpoint => fetch(:bugsnag_endpoint, Bugsnag::Configuration::DEFAULT_ENDPOINT),
- :use_ssl => fetch(:bugsnag_use_ssl, true)
- })
- rescue
- error "Bugsnag deploy notification failed, #{$!.inspect}"
- end
-
- info 'Bugsnag deploy notification complete.'
- end
- end
-
-end
-# vi:ft=ruby
diff --git a/lib/bugsnag/tasks/bugsnag.rake b/lib/bugsnag/tasks/bugsnag.rake
index bad09c133..be214b3da 100644
--- a/lib/bugsnag/tasks/bugsnag.rake
+++ b/lib/bugsnag/tasks/bugsnag.rake
@@ -1,82 +1,14 @@
require "bugsnag"
namespace :bugsnag do
- desc "Notify Bugsnag of a new deploy."
- task :deploy do
- api_key = ENV["BUGSNAG_API_KEY"]
- release_stage = ENV["BUGSNAG_RELEASE_STAGE"]
- app_version = ENV["BUGSNAG_APP_VERSION"]
- revision = ENV["BUGSNAG_REVISION"]
- repository = ENV["BUGSNAG_REPOSITORY"]
- branch = ENV["BUGSNAG_BRANCH"]
-
- Rake::Task["load"].invoke unless api_key
-
- Bugsnag::Deploy.notify({
- :api_key => api_key,
- :release_stage => release_stage,
- :app_version => app_version,
- :revision => revision,
- :repository => repository,
- :branch => branch
- })
- end
-
desc "Send a test exception to Bugsnag."
task :test_exception => :load do
begin
raise RuntimeError.new("Bugsnag test exception")
rescue => e
- Bugsnag.notify(e, {:context => "rake#test_exception"})
- end
- end
-
- desc "Show the bugsnag middleware stack"
- task :middleware => :load do
- Bugsnag.configuration.middleware.each {|m| puts m.to_s}
- end
-
- namespace :heroku do
- desc "Add a heroku deploy hook to notify Bugsnag of deploys"
- task :add_deploy_hook => :load do
- # Wrapper to run command safely even in bundler
- run_command = lambda { |command|
- defined?(Bundler.with_clean_env) ? Bundler.with_clean_env { `#{command}` } : `#{command}`
- }
-
- # Fetch heroku config settings
- config_command = "heroku config --shell"
- config_command += " --app #{ENV["HEROKU_APP"]}" if ENV["HEROKU_APP"]
- heroku_env = run_command.call(config_command).split(/[\n\r]/).each_with_object({}) do |c, obj|
- k,v = c.split("=")
- obj[k] = (v.nil? || v.strip.empty?) ? nil : v
- end
-
- # Check for Bugsnag API key (required)
- api_key = heroku_env["BUGSNAG_API_KEY"] || Bugsnag.configuration.api_key || ENV["BUGSNAG_API_KEY"]
- unless api_key
- puts "Error: No API key found, have you run 'heroku config:set BUGSNAG_API_KEY=your-api-key'?"
- next
+ Bugsnag.notify(e) do |report|
+ report.context = "rake#test_exception"
end
-
- # Build the request, making use of deploy hook variables
- # (https://devcenter.heroku.com/articles/deploy-hooks#customizing-messages)
- params = {
- :apiKey => api_key,
- :branch => "master",
- :revision => "{{head_long}}",
- :releaseStage => heroku_env["RAILS_ENV"] || ENV["RAILS_ENV"] || "production"
- }
- repo = `git config --get remote.origin.url`.strip
- params[:repository] = repo unless repo.empty?
-
- # Add the hook
- url = "https://notify.bugsnag.com/deploy?" + params.map {|k,v| "#{k}=#{v}"}.join("&")
- command = "heroku addons:add deployhooks:http --url=\"#{url}\""
- command += " --app #{ENV["HEROKU_APP"]}" if ENV["HEROKU_APP"]
-
- puts "$ #{command}"
- run_command.call(command)
end
end
end
diff --git a/rails/init.rb b/rails/init.rb
deleted file mode 100644
index 51ddc10af..000000000
--- a/rails/init.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# On Rails 2.x GEM_ROOT/rails/init.rb is auto loaded for all gems
-# so this is the place to initialize Rails 2.x plugin support
-if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("3.0")
- require "bugsnag/rails"
-else
- Bugsnag.warn "Blocked attempt to initialize legacy Rails 2.x extensions"
-end
diff --git a/spec/cleaner_spec.rb b/spec/cleaner_spec.rb
index 3318599ac..4e2a0baff 100644
--- a/spec/cleaner_spec.rb
+++ b/spec/cleaner_spec.rb
@@ -42,6 +42,12 @@
expect(subject.clean_object(obj)).to eq("André")
end
+ it "cleans custom objects" do
+ class Macaron; end
+ a = Macaron.new
+ expect(subject.clean_object(a)).to eq('[OBJECT]')
+ end
+
it "cleans up binary strings properly" do
if RUBY_VERSION > "1.9"
obj = "Andr\xc7\xff"
diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb
index db72a0bca..f1401edb9 100644
--- a/spec/configuration_spec.rb
+++ b/spec/configuration_spec.rb
@@ -4,7 +4,7 @@
describe Bugsnag::Configuration do
describe "delivery_method" do
it "should have the default delivery method" do
- expect(subject.delivery_method).to eq(Bugsnag::Configuration::DEFAULT_DELIVERY_METHOD)
+ expect(subject.delivery_method).to eq(:thread_queue)
end
it "should have the defined delivery_method" do
diff --git a/spec/fixtures/middleware/internal_info_setter.rb b/spec/fixtures/middleware/internal_info_setter.rb
index 5dee13588..8a49ab1bc 100644
--- a/spec/fixtures/middleware/internal_info_setter.rb
+++ b/spec/fixtures/middleware/internal_info_setter.rb
@@ -4,8 +4,8 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- notification.meta_data[:custom][:info] = MESSAGE
- @bugsnag.call(notification)
+ def call(report)
+ report.meta_data.merge!({custom: {info: MESSAGE}})
+ @bugsnag.call(report)
end
end
diff --git a/spec/fixtures/middleware/public_info_setter.rb b/spec/fixtures/middleware/public_info_setter.rb
index 94bca2c1c..8c4bc3a1d 100644
--- a/spec/fixtures/middleware/public_info_setter.rb
+++ b/spec/fixtures/middleware/public_info_setter.rb
@@ -4,8 +4,8 @@ def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- notification.meta_data[:custom][:info] = MESSAGE
- @bugsnag.call(notification)
+ def call(report)
+ report.meta_data.merge!({custom: {info: MESSAGE}})
+ @bugsnag.call(report)
end
end
diff --git a/spec/fixtures/tasks/Rakefile b/spec/fixtures/tasks/Rakefile
index 5da3820ea..385b11049 100644
--- a/spec/fixtures/tasks/Rakefile
+++ b/spec/fixtures/tasks/Rakefile
@@ -1,13 +1,12 @@
-require "bugsnag/rake"
+require "bugsnag/integrations/rake"
namespace :test do
desc "used by integration_spec to test that Bugsnag::Middleware::Rake runs properly"
task :crash do
port = ENV['BUGSNAG_TEST_SERVER_PORT']
Bugsnag.configure do |config|
- config.endpoint = "localhost:#{port}"
+ config.endpoint = "http://localhost:#{port}"
config.api_key = "0" * 32
- config.use_ssl = false
end
raise
diff --git a/spec/integration_spec.rb b/spec/integration_spec.rb
index 333349aef..205026018 100644
--- a/spec/integration_spec.rb
+++ b/spec/integration_spec.rb
@@ -15,6 +15,7 @@
end
Thread.new{ server.start }
end
+
after do
server.stop
queue.clear
@@ -39,8 +40,7 @@
it 'should send notifications over the wire' do
Bugsnag.configure do |config|
- config.endpoint = "localhost:#{server.config[:Port]}"
- config.use_ssl = false
+ config.endpoint = "http://localhost:#{server.config[:Port]}"
end
WebMock.allow_net_connect!
@@ -49,22 +49,9 @@
expect(request['events'][0]['exceptions'][0]['message']).to eq('yo')
end
- it 'should send deploys over the wire' do
- Bugsnag.configure do |config|
- config.endpoint = "localhost:#{server.config[:Port]}"
- config.use_ssl = false
- end
- WebMock.allow_net_connect!
-
- Bugsnag::Deploy.notify :app_version => '1.1.1'
-
- expect(request['appVersion']).to eq('1.1.1')
- end
-
it 'should work with threadpool delivery' do
Bugsnag.configure do |config|
- config.endpoint = "localhost:#{server.config[:Port]}"
- config.use_ssl = false
+ config.endpoint = "http://localhost:#{server.config[:Port]}"
config.delivery_method = :thread_queue
end
WebMock.allow_net_connect!
@@ -78,8 +65,7 @@
is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
unless is_jruby #jruby doesn't support fork, so this test doesn't apply
Bugsnag.configure do |config|
- config.endpoint = "localhost:#{server.config[:Port]}"
- config.use_ssl = false
+ config.endpoint = "http://localhost:#{server.config[:Port]}"
config.delivery_method = :thread_queue
end
WebMock.allow_net_connect!
@@ -117,8 +103,7 @@
it 'should use a proxy when configured' do
Bugsnag.configure do |config|
- config.endpoint = "localhost:#{server.config[:Port]}"
- config.use_ssl = false
+ config.endpoint = "http://localhost:#{server.config[:Port]}"
config.proxy_host = 'localhost'
config.proxy_port = proxy.config[:Port]
diff --git a/spec/delayed_job_spec.rb b/spec/integrations/delayed_job_spec.rb
similarity index 100%
rename from spec/delayed_job_spec.rb
rename to spec/integrations/delayed_job_spec.rb
diff --git a/spec/integrations/sidekiq_spec.rb b/spec/integrations/sidekiq_spec.rb
new file mode 100644
index 000000000..f4993dd9a
--- /dev/null
+++ b/spec/integrations/sidekiq_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'sidekiq/testing'
+
+class FailingWorker
+ include Sidekiq::Worker
+ def perform(value)
+ puts "Work: #{100/value}"
+ end
+end
+
+describe Bugsnag::Sidekiq do
+ before do
+ Sidekiq::Testing.inline!
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.add Bugsnag::Sidekiq
+ end
+ end
+
+ it "works" do
+ begin
+ FailingWorker.perform_async(-0)
+ fail("shouldn't be here")
+ rescue
+ end
+
+ expect(Bugsnag).to have_sent_notification {|payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]["sidekiq"]["msg"]["class"]).to eq("FailingWorker")
+ expect(event["metaData"]["sidekiq"]["msg"]["args"]).to eq([-0])
+ expect(event["metaData"]["sidekiq"]["msg"]["queue"]).to eq("default")
+ expect(event["severity"]).to eq("error")
+ }
+ end
+end
diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb
index e94a17c55..e8b3d46fe 100644
--- a/spec/middleware_spec.rb
+++ b/spec/middleware_spec.rb
@@ -28,8 +28,8 @@
it "runs before_bugsnag_notify callbacks, adding custom data" do
callback_run_count = 0
Bugsnag.before_notify_callbacks << lambda {|notif|
- notif.add_custom_data(:info, "here")
- notif.add_custom_data(:data, "also here")
+ notif.add_tab(:custom, {info: "here"})
+ notif.add_tab(:custom, {data: "also here"})
callback_run_count += 1
}
@@ -67,34 +67,35 @@
end
- it "allows overrides to override values set by internal middleware" do
+ it "allows block to override values set by internal middleware" do
Bugsnag.configuration.internal_middleware.use(InternalInfoSetter)
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:info => "overridden"})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({custom: {info: 'overridden'}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
expect(event["metaData"]["custom"]).not_to be_nil
- expect(event["metaData"]["custom"]["info"]).not_to eq(InternalInfoSetter::MESSAGE)
expect(event["metaData"]["custom"]["info"]).to eq("overridden")
}
end
- it "doesn't allow overrides to override public middleware" do
+ it "allows block to override public middleware" do
Bugsnag.configuration.middleware.use(PublicInfoSetter)
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:info => "overridden"})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({custom: {info: 'overridden'}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
expect(event["metaData"]["custom"]).not_to be_nil
- expect(event["metaData"]["custom"]["info"]).not_to eq("overridden")
- expect(event["metaData"]["custom"]["info"]).to eq(PublicInfoSetter::MESSAGE)
+ expect(event["metaData"]["custom"]["info"]).to eq("overridden")
}
end
- it "does not have have before or after callbacks by default" do
+ it "does not have have before callbacks by default" do
expect(Bugsnag.before_notify_callbacks.size).to eq(0)
- expect(Bugsnag.after_notify_callbacks.size).to eq(0)
Bugsnag.notify(BugsnagTestException.new("It crashed"))
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -102,18 +103,6 @@
}
end
- it "runs after_bugsnag_notify callbacks" do
- callback_run_count = 0
- Bugsnag.after_notify_callbacks << lambda {|notif|
- callback_run_count += 1
- }
-
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
- expect(callback_run_count).to eq(1)
- expect(Bugsnag::Notification).to have_sent_notification { }
- end
-
it "does not execute disabled bugsnag middleware" do
callback_run_count = 0
Bugsnag.configure do |config|
@@ -133,7 +122,7 @@
notif.ignore!
end
Bugsnag.notify(BugsnagTestException.new("It crashed"))
- expect(Bugsnag::Notification).not_to have_sent_notification { }
+ expect(Bugsnag).not_to have_sent_notification
end
it "allows inspection of meta_data before ignoring exception" do
@@ -148,34 +137,118 @@
end
Bugsnag.notify(BugsnagTestException.new("It crashed"))
- expect(Bugsnag::Notification).not_to have_sent_notification
+ expect(Bugsnag).not_to have_sent_notification
end
it "allows meta_data to be modified in a middleware" do
+ MetaDataAdder = Class.new do
+ def initialize(bugsnag)
+ @bugsnag = bugsnag
+ end
+
+ def call(report)
+ report.meta_data = {test: {value: "abcdef123456abcdef123456abcdef123456"}}
+ @bugsnag.call(report)
+ end
+ end
+
MetaDataMunger = Class.new do
def initialize(bugsnag)
@bugsnag = bugsnag
end
- def call(notification)
- token = notification.meta_data[:sidekiq][:args].first
- notification.meta_data[:sidekiq][:args] = ["#{token[0...6]}*****#{token[-4..-1]}"]
- @bugsnag.call(notification)
+ def call(report)
+ token = report.meta_data[:test][:value]
+ report.meta_data[:test][:value] = "#{token[0...6]}*****#{token[-4..-1]}"
+ @bugsnag.call(report)
end
end
Bugsnag.configure do |c|
+ c.middleware.use MetaDataAdder
c.middleware.use MetaDataMunger
end
- notification = Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :sidekiq => {
- :args => ["abcdef123456abcdef123456abcdef123456"]
- }
- })
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]['test']['value']).to eq("abcdef*****3456")
+ }
+ end
- expect(notification.meta_data[:sidekiq][:args]).to eq(["abcdef*****3456"])
+ if ruby_version_greater_equal?("2.3.0")
+ context "with a ruby version >= 2.3.0" do
+ it "attaches did you mean metadata when necessary" do
+ begin
+ "Test".prepnd "T"
+ rescue Exception => e
+ Bugsnag.notify(e)
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]["error"]).to_not be_nil
+ expect(event["metaData"]["error"]).to eq({"suggestion" => "prepend"})
+ }
+ end
+ end
+ end
+
+ context "with a ruby version < 2.3.0" do
+ if !ruby_version_greater_equal?("2.3.0")
+ it "doesn't attach did you mean metadata" do
+ begin
+ "Test".prepnd "T"
+ rescue Exception => e
+ Bugsnag.notify(e)
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]["error"]).to be_nil
+ }
+ end
+ end
end
+ it "doesn't allow handledState properties to be changed in middleware" do
+ HandledStateChanger = Class.new do
+ def initialize(bugsnag)
+ @bugsnag = bugsnag
+ end
+
+ def call(report)
+ report.severity_reason = {
+ :test => "test"
+ }
+ @bugsnag.call(report)
+ end
+ end
+
+ Bugsnag.configure do |c|
+ c.middleware.use HandledStateChanger
+ end
+
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report|
+ report.severity_reason = {
+ :type => "middleware_handler",
+ :attributes => {
+ :name => "middleware_test"
+ }
+ }
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["unhandled"]).to be true
+ expect(event["severityReason"]).to eq({
+ "type" => "middleware_handler",
+ "attributes" => {
+ "name" => "middleware_test"
+ }
+ })
+ }
+ end
end
diff --git a/spec/rack_spec.rb b/spec/rack_spec.rb
index 353505a74..3f72e399e 100644
--- a/spec/rack_spec.rb
+++ b/spec/rack_spec.rb
@@ -54,7 +54,7 @@
rack_stack.call(rack_env) rescue nil
- expect(Bugsnag::Notification).not_to have_sent_notification
+ expect(Bugsnag).not_to have_sent_notification
end
end
diff --git a/spec/notification_spec.rb b/spec/report_spec.rb
similarity index 78%
rename from spec/notification_spec.rb
rename to spec/report_spec.rb
index a7580673d..1a1f995c7 100644
--- a/spec/notification_spec.rb
+++ b/spec/report_spec.rb
@@ -27,7 +27,7 @@ def gloops
end
end
-describe Bugsnag::Notification do
+describe Bugsnag::Report do
it "should contain an api_key if one is set" do
Bugsnag.notify(BugsnagTestException.new("It crashed"))
@@ -53,7 +53,9 @@ def gloops
end
it "lets you override the api_key" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), :api_key => "9d84383f9be2ca94902e45c756a9979d")
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.api_key = "9d84383f9be2ca94902e45c756a9979d"
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
expect(payload["apiKey"]).to eq("9d84383f9be2ca94902e45c756a9979d")
@@ -62,7 +64,9 @@ def gloops
it "lets you override the groupingHash" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:grouping_hash => "this is my grouping hash"})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.grouping_hash = "this is my grouping hash"
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -115,7 +119,7 @@ def gloops
it "uses correct unhandled defaults" do
Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
+
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
expect(event["unhandled"]).to be false
@@ -126,19 +130,6 @@ def gloops
}
end
- it "sets correct severityReason if severity is modified" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:severity => "info"})
-
- expect(Bugsnag).to have_sent_notification{ |payload|
- event = get_event_from_payload(payload)
- expect(event["unhandled"]).to be false
- expect(event["severity"]).to eq("info")
- expect(event["severityReason"]).to eq({
- "type" => "userSpecifiedSeverity"
- })
- }
- end
-
it "sets correct severityReason if severity is modified in a block" do
Bugsnag.notify(BugsnagTestException.new("It crashed")) do |notification|
notification.severity = "info"
@@ -153,28 +144,6 @@ def gloops
}
end
- it "sets unhandled and severityReasons through auto_notify" do
- Bugsnag.auto_notify(BugsnagTestException.new("It crashed"), {
- :severity_reason => {
- :type => "unhandledExceptionMiddleware",
- :attributes => {
- :framework => "ruby test"
- }
- }
- })
- expect(Bugsnag).to have_sent_notification{ |payload|
- event = get_event_from_payload(payload)
- expect(event["unhandled"]).to be true
- expect(event["severity"]).to eq("error")
- expect(event["severityReason"]).to eq({
- "type" => "unhandledExceptionMiddleware",
- "attributes" => {
- "framework" => "ruby test"
- }
- })
- }
- end
-
it "sets correct severity and reason for specific error classes" do
Bugsnag.notify(SignalException.new("TERM"))
expect(Bugsnag).to have_sent_notification{ |payload|
@@ -193,12 +162,14 @@ def gloops
# TODO: nested context
it "accepts tabs in overrides and adds them to metaData" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :some_tab => {
- :info => "here",
- :data => "also here"
- }
- })
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({
+ some_tab: {
+ info: "here",
+ data: "also here"
+ }
+ })
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -209,22 +180,27 @@ def gloops
}
end
- it "accepts non-hash overrides and adds them to the custom tab in metaData" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :info => "here",
- :data => "also here"
- })
+ it "accepts meta data from an exception that mixes in Bugsnag::MetaData" do
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
+ exception.bugsnag_meta_data = {
+ some_tab: {
+ info: "here",
+ data: "also here"
+ }
+ }
+
+ Bugsnag.notify(exception)
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
- expect(event["metaData"]["custom"]).to eq(
+ expect(event["metaData"]["some_tab"]).to eq(
"info" => "here",
"data" => "also here"
)
}
end
- it "accepts meta data from an exception that mixes in Bugsnag::MetaData" do
+ it "removes tabs" do
exception = BugsnagTestExceptionWithMetaData.new("It crashed")
exception.bugsnag_meta_data = {
:some_tab => {
@@ -233,7 +209,28 @@ def gloops
}
}
- Bugsnag.notify(exception)
+ Bugsnag.notify(exception) do |report|
+ report.remove_tab(:some_tab)
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]["some_tab"]).to be_nil
+ }
+ end
+
+ it "ignores removing nil tabs" do
+ exception = BugsnagTestExceptionWithMetaData.new("It crashed")
+ exception.bugsnag_meta_data = {
+ :some_tab => {
+ :info => "here",
+ :data => "also here"
+ }
+ }
+
+ Bugsnag.notify(exception) do |report|
+ report.remove_tab(nil)
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -244,6 +241,21 @@ def gloops
}
end
+ it "Creates a custom tab for metadata which is not a Hash" do
+ exception = Exception.new("It crashed")
+
+ Bugsnag.notify(exception) do |report|
+ report.add_tab(:some_tab, "added")
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
+ expect(event["metaData"]["custom"]).to eq(
+ "some_tab" => "added",
+ )
+ }
+ end
+
it "accepts meta data from an exception that mixes in Bugsnag::MetaData, but override using the overrides" do
exception = BugsnagTestExceptionWithMetaData.new("It crashed")
exception.bugsnag_meta_data = {
@@ -253,7 +265,9 @@ def gloops
}
}
- Bugsnag.notify(exception, {:some_tab => {:info => "overridden"}})
+ Bugsnag.notify(exception) do |report|
+ report.add_tab(:some_tab, {:info => "overridden"})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -280,7 +294,9 @@ def gloops
exception = BugsnagTestExceptionWithMetaData.new("It crashed")
exception.bugsnag_user_id = "exception_user_id"
- Bugsnag.notify(exception, {:user_id => "override_user_id"})
+ Bugsnag.notify(exception) do |report|
+ report.user.merge!({:id => "override_user_id"})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -317,7 +333,9 @@ def gloops
exception = BugsnagTestExceptionWithMetaData.new("It crashed")
exception.bugsnag_context = "exception_context"
- Bugsnag.notify(exception, {:context => "override_context"})
+ Bugsnag.notify(exception) do |report|
+ report.context = "override_context"
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -326,14 +344,14 @@ def gloops
end
it "accepts meta_data in overrides (for backwards compatibility) and merge it into metaData" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :meta_data => {
- :some_tab => {
- :info => "here",
- :data => "also here"
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({
+ some_tab: {
+ info: "here",
+ data: "also here"
}
- }
- })
+ })
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -345,14 +363,14 @@ def gloops
end
it "truncates large meta_data before sending" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :meta_data => {
- :some_tab => {
- :giant => SecureRandom.hex(500_000/2),
- :mega => SecureRandom.hex(500_000/2)
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({
+ some_tab: {
+ giant: SecureRandom.hex(500_000/2),
+ mega: SecureRandom.hex(500_000/2)
}
- }
- })
+ })
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
# Truncated body should be no bigger than
@@ -362,14 +380,14 @@ def gloops
end
it "truncates large messages before sending" do
- Bugsnag.notify(BugsnagTestException.new(SecureRandom.hex(500_000)), {
- :meta_data => {
- :some_tab => {
- :giant => SecureRandom.hex(500_000/2),
- :mega => SecureRandom.hex(500_000/2)
+ Bugsnag.notify(BugsnagTestException.new(SecureRandom.hex(500_000))) do |report|
+ report.meta_data.merge!({
+ some_tab: {
+ giant: SecureRandom.hex(500_000/2),
+ mega: SecureRandom.hex(500_000/2)
}
- }
- })
+ })
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
# Truncated body should be no bigger than
@@ -393,9 +411,9 @@ def gloops
end
it "accepts a severity in overrides" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :severity => "info"
- })
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.severity = "info"
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -413,42 +431,10 @@ def gloops
}
end
- it "does not accept a bad severity in overrides" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :severity => "fatal"
- })
-
- expect(Bugsnag).to have_sent_notification{ |payload|
- event = get_event_from_payload(payload)
- expect(event["severity"]).to eq("warning")
- }
- end
-
- it "lets you override severity using block syntax" do
- Bugsnag.notify(BugsnagTestException.new("It crashed")) do |notification|
- notification.severity = "info"
- end
-
- expect(Bugsnag).to have_sent_notification{ |payload|
- event = get_event_from_payload(payload)
- expect(event["severity"]).to eq("info")
- }
- end
-
- it "autonotifies errors" do
- Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
-
- expect(Bugsnag).to have_sent_notification{ |payload|
- event = get_event_from_payload(payload)
- expect(event["severity"]).to eq("error")
- }
- end
-
-
it "accepts a context in overrides" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :context => "test_context"
- })
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.context = 'test_context'
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -457,9 +443,9 @@ def gloops
end
it "accepts a user_id in overrides" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {
- :user_id => "test_user"
- })
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.user = {id: 'test_user'}
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -467,12 +453,12 @@ def gloops
}
end
- it "does not send a notification if auto_notify is false" do
+ it "does not send an automatic notification if auto_notify is false" do
Bugsnag.configure do |config|
config.auto_notify = false
end
- Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), true)
expect(Bugsnag).not_to have_sent_notification
end
@@ -482,7 +468,7 @@ def gloops
config.release_stage = "production"
end
- Bugsnag.auto_notify(BugsnagTestException.new("It crashed"))
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -516,27 +502,6 @@ def gloops
expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
end
- it "uses ssl when use_ssl is true" do
- Bugsnag.configuration.use_ssl = true
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
- expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
- end
-
- it "does not use ssl when use_ssl is false" do
- stub_request(:post, "http://notify.bugsnag.com/")
- Bugsnag.configuration.use_ssl = false
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
- expect(WebMock).to have_requested(:post, "http://notify.bugsnag.com")
- end
-
- it "uses ssl when use_ssl is unset" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
- expect(WebMock).to have_requested(:post, "https://notify.bugsnag.com")
- end
-
it "does not mark the top-most stacktrace line as inProject if out of project" do
Bugsnag.configuration.project_root = "/Random/location/here"
Bugsnag.notify(BugsnagTestException.new("It crashed"))
@@ -548,19 +513,6 @@ def gloops
}
end
- it "does not mark the top-most stacktrace line as inProject if it matches a vendor path" do
- Bugsnag.configuration.project_root = File.expand_path('../../', __FILE__)
- Bugsnag.configuration.vendor_paths = [File.expand_path('../', __FILE__)]
-
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
-
- expect(Bugsnag).to have_sent_notification{ |payload|
- exception = get_exception_from_payload(payload)
- expect(exception["stacktrace"].size).to be >= 1
- expect(exception["stacktrace"].first["inProject"]).to be_nil
- }
- end
-
it "marks the top-most stacktrace line as inProject if necessary" do
Bugsnag.configuration.project_root = File.expand_path File.dirname(__FILE__)
Bugsnag.notify(BugsnagTestException.new("It crashed"))
@@ -582,9 +534,11 @@ def gloops
}
end
- it "filters params from all payload hashes if they are set in default params_filters" do
+ it "filters params from all payload hashes if they are set in default meta_data_filters" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "12345", :other_data => "123456"}}})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "12345", :other_data => "123456"}}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -597,10 +551,12 @@ def gloops
}
end
- it "filters params from all payload hashes if they are added to params_filters" do
+ it "filters params from all payload hashes if they are added to meta_data_filters" do
- Bugsnag.configuration.params_filters << "other_data"
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ Bugsnag.configuration.meta_data_filters << "other_data"
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -613,10 +569,12 @@ def gloops
}
end
- it "filters params from all payload hashes if they are added to params_filters as regex" do
+ it "filters params from all payload hashes if they are added to meta_data_filters as regex" do
- Bugsnag.configuration.params_filters << /other_data/
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ Bugsnag.configuration.meta_data_filters << /other_data/
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -629,10 +587,12 @@ def gloops
}
end
- it "filters params from all payload hashes if they are added to params_filters as partial regex" do
+ it "filters params from all payload hashes if they are added to meta_data_filters as partial regex" do
- Bugsnag.configuration.params_filters << /r_data/
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ Bugsnag.configuration.meta_data_filters << /r_data/
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -646,7 +606,9 @@ def gloops
end
it "does not filter params from payload hashes if their values are nil" do
- Bugsnag.notify(BugsnagTestException.new("It crashed"), {:request => {:params => {:nil_param => nil}}})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({:request => {:params => {:nil_param => nil}}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -657,49 +619,46 @@ def gloops
}
end
- it "does not notify if the non-default exception class is added to the ignore_classes" do
- Bugsnag.configuration.ignore_classes << "BugsnagTestException"
-
- Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
+ it "does not notify if report ignored" do
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.ignore!
+ end
expect(Bugsnag).not_to have_sent_notification
end
- it "does not notify if exception's ancestor is an ignored class" do
- Bugsnag.configuration.ignore_classes << "BugsnagTestException"
-
- Bugsnag.notify_or_ignore(BugsnagSubclassTestException.new("It crashed"))
+ it "does not notify if the exception class is in the default ignore_classes list" do
+ Bugsnag.configuration.ignore_classes << ActiveRecord::RecordNotFound
+ Bugsnag.notify(ActiveRecord::RecordNotFound.new("It crashed"))
expect(Bugsnag).not_to have_sent_notification
end
- it "does not notify if any caused exception is an ignored class" do
- Bugsnag.configuration.ignore_classes << "NestedException"
-
- ex = NestedException.new("Self-referential exception")
- ex.original_exception = BugsnagTestException.new("It crashed")
+ it "does not notify if the non-default exception class is added to the ignore_classes" do
+ Bugsnag.configuration.ignore_classes << BugsnagTestException
- Bugsnag.notify_or_ignore(ex)
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
expect(Bugsnag).not_to have_sent_notification
end
- it "accepts both String and Class instances as an ignored class" do
+ it "does not notify if exception's ancestor is an ignored class" do
Bugsnag.configuration.ignore_classes << BugsnagTestException
- Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
+ Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
expect(Bugsnag).not_to have_sent_notification
end
- it "does not notify if the user agent is present and matches a regex in ignore_user_agents" do
- Bugsnag.configuration.ignore_user_agents << %r{BugsnagUserAgent}
+ it "does not notify if any caused exception is an ignored class" do
+ Bugsnag.configuration.ignore_classes << NestedException
- ((Thread.current["bugsnag_req_data"] ||= {})[:rack_env] ||= {})["HTTP_USER_AGENT"] = "BugsnagUserAgent"
+ ex = NestedException.new("Self-referential exception")
+ ex.original_exception = BugsnagTestException.new("It crashed")
- Bugsnag.notify_or_ignore(BugsnagTestException.new("It crashed"))
+ Bugsnag.notify(ex)
- expect(Bugsnag::Notification).not_to have_sent_notification
+ expect(Bugsnag).not_to have_sent_notification
end
it "sends the cause of the exception" do
@@ -723,7 +682,7 @@ def gloops
ex = NestedException.new("Self-referential exception")
ex.original_exception = ex
- Bugsnag.notify_or_ignore(ex)
+ Bugsnag.notify(ex)
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
@@ -738,7 +697,7 @@ def gloops
ex = ex.original_exception = NestedException.new("Deep exception #{idx}")
end
- Bugsnag.notify_or_ignore(first_ex)
+ Bugsnag.notify(first_ex)
expect(Bugsnag).to have_sent_notification{ |payload|
event = get_event_from_payload(payload)
expect(event["exceptions"].size).to eq(5)
@@ -819,34 +778,39 @@ def gloops
invalid_data = "fl\xc3ff"
invalid_data.force_encoding('BINARY') if invalid_data.respond_to?(:force_encoding)
- notify_test_exception(:fluff => {:fluff => invalid_data})
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.meta_data.merge!({fluff: {fluff: invalid_data}})
+ end
expect(Bugsnag).to have_sent_notification{ |payload|
+ event = get_event_from_payload(payload)
if defined?(Encoding::UTF_8)
- expect(payload.to_json).to match(/fl�ff/)
+ expect(event['metaData']['fluff']['fluff']).to match(/fl�ff/)
else
- expect(payload.to_json).to match(/flff/)
+ expect(event['metaData']['fluff']['fluff']).to match(/flff/)
end
}
end
- it "should handle utf8 encoding errors in exceptions_list" do
- invalid_data = "\"foo\xEBbar\""
- invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
+ if RUBY_VERSION < '2.3.0'
+ it "should handle utf8 encoding errors in exceptions_list" do
+ invalid_data = "\"foo\xEBbar\""
+ invalid_data = invalid_data.force_encoding("utf-8") if invalid_data.respond_to?(:force_encoding)
- begin
- JSON.parse(invalid_data)
- rescue
- Bugsnag.notify $!
- end
-
- expect(Bugsnag).to have_sent_notification { |payload|
- if defined?(Encoding::UTF_8)
- expect(payload.to_json).to match(/foo�bar/)
- else
- expect(payload.to_json).to match(/foobar/)
+ begin
+ JSON.parse(invalid_data)
+ rescue
+ Bugsnag.notify $!
end
- }
+
+ expect(Bugsnag).to have_sent_notification { |payload|
+ if defined?(Encoding::UTF_8)
+ expect(payload.to_json).to match(/foo�bar/)
+ else
+ expect(payload.to_json).to match(/foobar/)
+ end
+ }
+ end
end
it "should handle utf8 encoding errors in notification context" do
@@ -856,7 +820,9 @@ def gloops
begin
raise
rescue
- Bugsnag.notify($!, { :context => invalid_data })
+ Bugsnag.notify($!) do |report|
+ report.context = invalid_data
+ end
end
expect(Bugsnag).to have_sent_notification { |payload|
@@ -955,6 +921,57 @@ def gloops
}
end
+ it 'should use defaults when notify is called' do
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = payload["events"][0]
+ expect(event["unhandled"]).to be false
+ expect(event["severityReason"]).to eq({"type" => "handledException"})
+ }
+ end
+
+ it 'should attach severity reason through a block when auto_notify is true' do
+ Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report|
+ report.severity_reason = {
+ :type => "middleware_handler",
+ :attributes => {
+ :name => "middleware_test"
+ }
+ }
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = payload["events"][0]
+ expect(event["severityReason"]).to eq(
+ {
+ "type" => "middleware_handler",
+ "attributes" => {
+ "name" => "middleware_test"
+ }
+ }
+ )
+ expect(event["unhandled"]).to be true
+ }
+ end
+
+ it 'should not attach severity reason from callback when auto_notify is false' do
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
+ report.severity_reason = {
+ :type => "middleware_handler",
+ :attributes => {
+ :name => "middleware_test"
+ }
+ }
+ end
+
+ expect(Bugsnag).to have_sent_notification{ |payload|
+ event = payload["events"][0]
+ expect(event["unhandled"]).to be false
+ expect(event["severityReason"]).to eq({"type" => "handledException"})
+ }
+ end
+
if defined?(JRUBY_VERSION)
it "should work with java.lang.Throwables" do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 5e735b113..7e91198fb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,3 +1,13 @@
+if ARGV.include? "--coverage"
+ require 'simplecov'
+ require 'coveralls'
+
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
+ SimpleCov.start do
+ add_filter 'spec'
+ end
+end
+
require 'bugsnag'
require 'webmock/rspec'
@@ -20,6 +30,14 @@ def notify_test_exception(*args)
Bugsnag.notify(RuntimeError.new("test message"), *args)
end
+def ruby_version_greater_equal?(version)
+ current_version = RUBY_VERSION.split "."
+ target_version = version.split "."
+ (Integer(current_version[0]) >= Integer(target_version[0])) &&
+ (Integer(current_version[1]) >= Integer(target_version[1])) &&
+ (Integer(current_version[2]) >= Integer(target_version[2]))
+end
+
RSpec.configure do |config|
config.order = "random"
diff --git a/spec/code_spec.rb b/spec/stacktrace_spec.rb
similarity index 98%
rename from spec/code_spec.rb
rename to spec/stacktrace_spec.rb
index bbd38546d..b9b1e5134 100644
--- a/spec/code_spec.rb
+++ b/spec/stacktrace_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Bugsnag::Notification do
+describe Bugsnag::Stacktrace do
it "includes code in the stack trace" do
_a = 1
_b = 2