lib/ruote/svc/error_handler.rb in ruote-2.2.0 vs lib/ruote/svc/error_handler.rb in ruote-2.3.0
- old
+ new
@@ -1,7 +1,7 @@
#--
-# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
+# Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
@@ -24,11 +24,25 @@
module Ruote
#
- # A ruote service for turning exceptions into process errors (or letting
+ # For errors occuring when handling errors.
+ #
+ class MetaError < StandardError
+
+ attr_reader :error
+
+ def initialize(message, error)
+
+ super("#{message}: #{error.to_s}")
+ @error = error
+ end
+ end
+
+ #
+ # A ruote service for turning errors into process errors (or letting
# those error fire any potential :on_error attributes in the process
# definition).
#
# This service is used, by the worker, the dispatch pool and some
# receivers (like the one in ruote-beanstalk).
@@ -40,96 +54,138 @@
@context = context
end
# As used by the dispatch pool and the worker.
#
- def msg_handle(msg, exception)
+ def msg_handle(msg, err)
fexp = Ruote::Exp::FlowExpression.fetch(
@context, msg['fei'] || msg['workitem']['fei']
) rescue nil
- handle(msg, fexp, exception)
+ handle(msg, fexp, err)
end
+ # Packages the error in a 'raise' msg and places it in the storage,
+ # for a worker to pick it up.
+ #
+ def msg_raise(msg, err)
+
+ fei = msg['fei']
+ wfid = msg['wfid'] || msg.fetch('fei', {})['wfid']
+
+ @context.storage.put_msg(
+ 'raise',
+ 'fei' => fei,
+ 'wfid' => wfid,
+ 'msg' => msg,
+ 'error' => deflate(err, fei))
+ end
+
# As used by some receivers (see ruote-beanstalk's receiver).
#
- def action_handle(action, fei, exception)
+ # TODO: at some point, merge that with #msg_raise
+ #
+ def action_handle(action, fei, err)
fexp = Ruote::Exp::FlowExpression.fetch(@context, fei)
msg = {
'action' => action,
'fei' => fei,
'participant_name' => fexp.h.participant_name,
- 'workitem' => fexp.h.applied_workitem }
+ 'workitem' => fexp.h.applied_workitem,
+ 'put_at' => Ruote.now_to_utc_s }
- handle(msg, fexp, exception)
+ handle(msg, fexp, err)
end
protected
- # As used by the worker.
+ # Called by msg_handle or action_handle.
#
- def handle(msg, fexp, exception)
+ def handle(msg, fexp, err)
- wfid = msg['wfid'] || (msg['fei']['wfid'] rescue nil)
- fei = msg['fei'] || (fexp.h.fei rescue nil)
+ err = RaisedError.new(err) unless err.respond_to?(:backtrace)
- backtrace = exception.backtrace || []
+ meta = err.is_a?(Ruote::MetaError)
- # debug only
+ fei = msg['fei'] || (fexp.h.fei rescue nil)
+ wfid = msg['wfid'] || (fei || {})['wfid']
- if $DEBUG || ARGV.include?('-d')
-
- puts "\n== worker intercepted error =="
- puts
- p exception
- puts backtrace[0, 20].join("\n")
- puts "..."
- puts
- puts "-- msg --"
- key_length = msg.keys.collect { |k| k.length }.max + 1
- msg.keys.sort.each { |k|
- v = msg[k]
- v = (Ruote.sid(v) rescue nil) if k == 'fei' || k == 'parent_id'
- printf("%*s : %s\n", key_length, k, v.inspect)
- }
- puts "-- . --"
- puts
- end
-
# on_error ?
- return if fexp && fexp.handle_on_error(msg, exception)
+ return if ( ! meta) && fexp && fexp.handle_on_error(msg, err)
# emit 'msg'
#
# (this message might get intercepted by a tracker)
- @context.storage.put_msg(
- 'error_intercepted',
- 'error' => {
- 'fei' => fei,
- 'at' => Ruote.now_to_utc_s,
- 'class' => exception.class.name,
- 'message' => exception.message,
- 'trace' => backtrace
- },
- 'wfid' => wfid,
- 'fei' => fei,
- 'msg' => msg)
+ herr = deflate(err, fei, fexp)
# fill error in the error journal
@context.storage.put(
- 'type' => 'errors',
- '_id' => "err_#{Ruote.to_storage_id(fei)}",
- 'message' => exception.inspect,
- 'trace' => backtrace.join("\n"),
- 'fei' => fei,
- 'msg' => msg
+ herr.merge(
+ 'type' => 'errors',
+ '_id' => "err_#{Ruote.to_storage_id(fei)}",
+ 'message' => err.inspect, # :-(
+ 'trace' => (err.backtrace || []).join("\n"), # :-(
+ 'msg' => msg)
) if fei
+
+ # advertise 'error_intercepted'
+
+ @context.storage.put_msg(
+ 'error_intercepted',
+ 'error' => herr, 'wfid' => wfid, 'fei' => fei, 'msg' => msg)
+
+ rescue => e
+
+ raise e unless @context.worker
+
+ @context.worker.send(
+ :handle_step_error,
+ e,
+ { 'action' => 'error_intercepted',
+ 'error' => deflate(err, fei),
+ 'fei' => fei,
+ 'wfid' => wfid,
+ 'msg' => msg })
+ end
+
+ # Returns a serializable hash with all the details of the error.
+ #
+ def deflate(err, fei, fexp=nil)
+
+ return err unless err.respond_to?(:backtrace)
+
+ fexp ||=
+ Ruote::Exp::FlowExpression.dummy('fei' => fei, 'original_tree' => nil)
+
+ fexp.deflate(err)
+ end
+
+ # The 'raise' action/msg passes deflated errors. This wrapper class
+ # "inflates" them.
+ #
+ class RaisedError
+ def initialize(h)
+ @h = h
+ end
+ def class
+ Ruote.constantize(@h['class'])
+ end
+ def message
+ @h['message']
+ end
+ def backtrace
+ @h['trace']
+ end
+ def to_s
+ "raised: #{@h['class']}: #{@h['message']}"
+ end
+ alias inspect to_s
end
end
end