From c17d01856a5134492c135f7811102d1bcfa39983 Mon Sep 17 00:00:00 2001 From: Kyrylo Silin Date: Thu, 4 May 2017 17:51:20 +0300 Subject: [PATCH] filters/thread_filter: don't serialise IO objects Fixes #204 (Airbrake gem segfaults on Circle CI) The main reason to use `inspect` here was to avoid a bug with IO objects in Rails apps. I stumbled upon an edge case while running the thread filter against a Rails app. The app would stuck because `io_obj.to_json` would never return. The `io_obj` was open. Closed IO objects raise IOError, which we already handle. Given that we stop using `inspect`, I think this also fixes #204. --- lib/airbrake-ruby/filters/thread_filter.rb | 7 +++++-- spec/filters/thread_filter_spec.rb | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/airbrake-ruby/filters/thread_filter.rb b/lib/airbrake-ruby/filters/thread_filter.rb index 5712ca47..ff505889 100644 --- a/lib/airbrake-ruby/filters/thread_filter.rb +++ b/lib/airbrake-ruby/filters/thread_filter.rb @@ -38,12 +38,15 @@ def call(notice) def thread_variables(th) th.thread_variables.map.with_object({}) do |var, h| - h[var] = th.thread_variable_get(var).inspect + h[var] = th.thread_variable_get(var) end end def fiber_variables(th) - th.keys.map.with_object({}) { |key, h| h[key] = th[key].inspect } + th.keys.map.with_object({}) do |key, h| + next if (value = th[key]).is_a?(IO) + h[key] = value + end end def add_thread_info(th, thread_info) diff --git a/spec/filters/thread_filter_spec.rb b/spec/filters/thread_filter_spec.rb index e2f02ea1..ec2cd02c 100644 --- a/spec/filters/thread_filter_spec.rb +++ b/spec/filters/thread_filter_spec.rb @@ -11,7 +11,7 @@ subject.call(notice) th.thread_variable_set(:bingo, nil) - expect(notice[:params][:thread][:thread_variables][:bingo]).to eq(':bango') + expect(notice[:params][:thread][:thread_variables][:bingo]).to eq(:bango) end it "appends fiber variables" do @@ -19,7 +19,7 @@ subject.call(notice) th[:bingo] = nil - expect(notice[:params][:thread][:fiber_variables][:bingo]).to eq(':bango') + expect(notice[:params][:thread][:fiber_variables][:bingo]).to eq(:bango) end it "appends name", skip: !Thread.current.respond_to?(:name) do @@ -49,4 +49,21 @@ subject.call(notice) expect(notice[:params][:thread][:safe_level]).to eq(0) end + + context "when an IO-like object is stored" do + let(:io_obj) do + Class.new(IO) do + def initialize; end + end.new + end + + it "doesn't append the IO object" do + expect(io_obj).to be_is_a(IO) + th.thread_variable_set(:io, io_obj) + subject.call(notice) + th.thread_variable_set(:io, nil) + + expect(notice[:params][:thread][:fiber_variables][:io]).to be_nil + end + end end