smartinez87 / exception_notification

Exception Notifier Plugin for Rails
http://smartinez87.github.io/exception_notification
MIT License
2.18k stars 415 forks source link

ActiveJob::SerializationError: Unsupported argument type: StringIO #319

Open the-teacher opened 8 years ago

the-teacher commented 8 years ago

Hello! I am looking for way how to put sending notifications to the background.

I tried play with exception_notification (4.1.4). I found option for email notifications deliver_with but it looks like allow the just deliver_now value.

Fine, I wrote following monkey patch for my test app:

config/initializers/exception_notifier_patch.rb

module ExceptionNotifier
  class EmailNotifier < BaseNotifier
    def call(exception, options={})
      binding.pry
      # create_email(exception, options).send(deliver_with)
      create_email(exception, options).deliver_later
    end
  end
end

And I have an error message for Sidekiq:

ActiveJob::SerializationError: Unsupported argument type: StringIO

[16] pry(#<ExceptionNotifier::EmailNotifier>)> exception.message
=> "divided by 0"

[17] pry(#<ExceptionNotifier::EmailNotifier>)> exception.class
=> ZeroDivisionError

So, for me it looks like create_email do something incorrect for delayed processors. Maybe DelayedJob can process this, but Sidekiq can't.

I want to ask, is it possible to put exception_notification mailers to sidekiq queue? Or maybe it's impossible for now, and I should stop my attempts?

Thanks for any advice!

juanazam commented 8 years ago

@the-teacher I was able to reproduce the issue.

As you mentioned above, there is no easy way of using deliver_later as exception_notification's code stands right now, but that should be easy to fix.

The real problem is the way the email is created and how ActiveJob serializes the data to pass it to sidekiq.

When the environment section for the notification email is generated, it includes this: * rack.input : #<StringIO:0x007fca9d87a6f8>

which seems to be the culprit.

Anyway I will investigate more and try to find a workaround to send emails in the background since it seems to be a good feature to support.

Axxiss commented 8 years ago

When serializing, ActiveJob only support certain type of objects, according to the SerializationError docs:

Raised when an unsupported argument type is set as a job argument. We currently support NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum, BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record). Raised if you set the key for a Hash something else than a string or a symbol. Also raised when trying to serialize an object which can't be identified with a Global ID - such as an unpersisted Active Record model.

Furthermore rack.input is the first of many "unserializable" objects,

I thinks this leaves a few questions open.

aruprakshit commented 8 years ago

@Axxiss Did you find answers of your question? :) I have similar questions and got stuck.

Axxiss commented 8 years ago

@aruprakshit No, I didn't had a chance to look further into it.

fmluizao commented 8 years ago

This should be a great addition. I was wondering if it would be possible to serialize the data used in the views. I'm not familiar with the source code, but if anyone point me a direction I can give a shoot.

xHire commented 7 years ago

I ran into this exact issue in my application. It turned out the cause is ActiveJob saving parameters to later call the original method. My solution is to use Marshal.dump on all parameters so that ActiveJob is given only a string (plus I put all parameters into an array to make it simpler). To demonstrate it on the code from the first post (untested, but should work):

module ExceptionNotifier
  class EmailNotifier
    def call(exception, options={})
      create_email(Marshal.dump([ exception, options ])).deliver_later
    end

    def create_email(params)
      super(*Marshal.load(params)))
    end
  end
end

Nice thing is that the (un)marshalling can be there even for non-delayed delivery. I hope this helps someone. :c)

aruprakshit commented 7 years ago

@xHire I did that way you did. But while loading sometime I was getting encoding error.

xHire commented 7 years ago

@aruprakshit That’s interesting. Could you be more specific? Is it a normal Ruby encoding error or something special? For which encodings/contents does it happen?

Personally, I didn’t run in any such issue, but that might be because my application only works with ascii-8bit data.

r4mbo7 commented 6 years ago

2018, still looking for a way to have notifications perfomed by sikekiq...

Trying all solutions above, don't find something working yet.

It's gonna end with a custom message base on excpetion and options variables for me :/

kadru commented 4 years ago

Hi, I just got stuck with this. I tried many things, so far this is the best approach I have:

  module EmailNotifier
    def call(exception, options = {})
      options[:env] = options[:env].transform_values do |value|  # this could be in a pre_callback?
        break if ActiveJob::Serializers.serializers.include? value.class
        value.to_s
      end

      super(exception, options)
    end
end

The problem with this approach, is have to write the serializers (see) for the classes that will used later for construct the body of email, (e.g. controller classes) and then add to active job serializers array. And only will work for Rails > 6.

Another approach its serialize to hashes the exception and options just before call deliver_later (maybe the email will have less info about the error).

What do you think?

up_the_irons commented 3 years ago

This also hit me in a new Rails 6.1 app. Being able to deliver exception notifications in the background is important, and we use Sidekiq to back ActiveJob. I haven't found a good solution yet.

herunan commented 3 years ago

Would love to get a fix for this @juanazam! Thanks

bayburin commented 3 years ago

+1 I have the same problem. When I use sidekiq I see An error occurred when sending a notification using 'email' notifier.ActiveJob::SerializationError: Unsupported argument type: IO.

followerstracker commented 3 years ago

A slightly modified @kadru's version that worked for us on rails 6.1

config/initializers/exception_notifier.rb

module ExceptionNotifier

  # ...

  class EmailNotifier < BaseNotifier

    def call_with_patch(exception, options = {})
      options[:env] = options[:env].transform_values do |value|
        break if ActiveJob::Serializers.serializers.include? value.class
        value.to_s
      end

      call_without_patch(exception, options)
    end

    alias_method(:call_without_patch, :call)
    alias_method(:call, :call_with_patch)

   # ...
  end
end