Archived

This forum has been archived. Please start a new discussion on GitHub.

Adding Error Handler To The Pipeline Ice RPC

Forgive me if the answer to this is in documentation somewhere, but I search for ages and couldn't find it.

As I said in a previous post I am writing an application that uses both SignalR and Ice RPC.

SignalR has the ability to add an error handler to the server pipeline in order to avoid having to wrap every single call within a try catch, thus saving code. From that error handler I am able to access context and log the error on the server.

I have searched the web for something a similar thing for Ice RPC, but I haven't being about the even find anything about general handling, on the server side.

Can you please help?

I am using C# on the server side.

Comments

  • xdm
    xdm La Coruña, Spain
    edited July 2020

    Hi Andrew,

    You can implement this using a dispatch interceptor, the interceptor will catch any exception throw by servant dispatch log the exception and rethrow it, should be something like

    class Interceptor : Ice.DispatchInterceptor
    {
        private readonly Ice.Object _servant;
    
        internal InterceptorI(Ice.Object servant) => _servant = servant;
    
        public override Task<Ice.OutputStream> dispatch(Ice.Request request)
        {
            Ice.Current current = request.getCurrent();
            Ice.Logger logger = current.adapter.getCommunicator().getLogger();
            try
            {
                return _servant.ice_dispatch(request);
            }
            catch (Exception ex)
            {
                logger.error($"error dispatching operation {current.operation}\n{ex}");
                throw;
            }
        }
    }
    

    And then when creating your servants you wrap them in an interceptor for adding to the adapter

    adapter.add(new Interceptor(new HelloI()), Ice.Util.stringToIdentity("hello"));
    

    see also:

    Cheers,
    Jose

  • Thanks for your help.
    You gave me what I wanted.

    I had to make some changes and I will post them here for the benefit of future readers.

    I am still learning ICE, so if anyone knows better than me please say so.

    internal  class LoggingInterceptor : Ice.DispatchInterceptor
    {
    ...
        public override Task<Ice.OutputStream> dispatch(Ice.Request request)
        {
            Task<Ice.OutputStream> task = _servant.ice_dispatch(request);
    
            task?.ContinueWith(
                t =>
                {
                    if (t.IsFaulted)
                    {
                        LogMessage(t.Exception?.ToString());
                    }
                }
            );
    
            return task;
    
        }
    ...
    }
    

    Under the rules of C# if you return a task but don't await it the exception it won't be caught.

    'ContinueWith' was recommended in the Ice doco in the first link as a way to wait for the task to complete.
    I tried it just like in the code above and it worked.

    Also note I think that there is a typo in this line of the previous answer:

     internal InterceptorI
    

    I think it should be:

     internal Interceptor
    

    Otherwise it is not a valid constructor.

    I do have one burning question. 'ice_dispatch' returns null several times before actually returning a task that can be awaited, hence the '?.' Any ideas?

    Thanks for the help.

  • xdm
    xdm La Coruña, Spain

    You are right, my sample only works with synchronous dispatch, in which case all exceptions are thrown synchronously.

    Maybe a bit simpler

    public override async Task<Ice.OutputStream> dispatch(Ice.Request request)
    {
        Ice.Current current = request.getCurrent();
        Ice.Logger logger = current.adapter.getCommunicator().getLogger();
        try
        {
            return _servant.ice_dispatch(request) is Task<Ice.OutputStream> task ? await task : null;
        }
        catch (Exception ex)
        {
            logger.error(ex.ToString());
            throw;
        }
    }