0

I'm working on a distributed system composed of multiple services (processes) working together orchestrated via message broker (service bus). Some of these service are very time sensitive, so we had the idea of using the bus the propagate exceptions to a service that would be used as a central point to log (to a database/on disk) the exceptions of all other services in order not to create latency in our services because we know sending a message is faster than writing on disk. So using NLog, I create a new target for sending exceptions on bus and redirect all service exception handling to it.

It works fine for basic framework exceptions, but of course some exceptions can be pretty complex objects. When using different libraries, not only are some exception not serializable (ex: IronPython), but more importantly our ExceptionLogger service don't have reference to every external dll used by every other endpoints, so it can't instantiate the typed exception.

Here's a few idea that I had to fix my problem, but none work:

  1. Have the exception logger service reference every dlls used by the other services. In my opinion this is not clean, pretty overkill and still don't handle the case of exceptions that can't even be serialized and passed on the bus.

  2. Creating a custom class that doesn't inherit from exception to pass only the data (as Antony Booth method here: How to serialize an Exception object in C#?). Problem with this is that when I receive an instance of this class on my exception logger endpoint and want to trigger a log, I can't create a new Exception() from it to create a NLog LogEventInfo object.

  3. Transform the specially typed exception to a base native Exception type before sending it on the bus. This way, I lose some information, but I would keep the Message, Stacktrace and the InnerException stack, which is pretty much everything I need. Unfortunately, I did not find a way to do it and don't think it is possible.

That makes me question the relevance of the whole idea, am I simply going into a wrong path?

Community
  • 1
  • 1
Dunge
  • 89
  • 1
  • 11
  • What were the issues you had with option 3? If all you need are the base `Exception` properties, why did this not work for you? – Mikanikal Apr 27 '16 at 00:53
  • Exception constructors only take Message and InnerException as parameter, and the other properties like the stacktrace have getters only, no way to set them. – Dunge Apr 27 '16 at 12:59
  • If you wanted to by able to catch an Exception in a remote process, it would need to be Serializable. But if all you want to do is log it, why don't you just call Exception.ToString() and send the resulting string to the remote process? This string contains any InnerException and StackTrace information. – Joe Apr 27 '16 at 14:33

2 Answers2

0

Option 4. Create an Exception assembly to store all of the custom exception types within your organization and reference that assembly in every other service. It's a centralized store of Exceptions.

Mikanikal
  • 1,082
  • 8
  • 12
  • What about the exceptions thrown by the external libraries we use that we don't have the source (got from nuget packages, referenced as assembly)? I can't include them in this Exception assembly can I? – Dunge Apr 26 '16 at 20:28
  • If it's just for logging, you don't need the original (typed) exception instance anymore - you just need the exception object's state. An option would be to reflect the properties of foreign/cumbersome exception types into a homebrew wrapper class that meets your requirements. – Kai van Lopik Apr 26 '16 at 21:44
  • That's pretty much my question, how to I feed the read only properties like StackTrace on an exception that is either the base type or an homebrew wrapper from the source one. – Dunge Apr 27 '16 at 13:05
  • @Dunge Do you have to use one of the NLog `Logger.Error` methods that takes an exception? There are `Error` overrides that take just strings and format strings. You can format it any way you like. That seems like that is what you will have to do. – Mikanikal Apr 27 '16 at 13:51
0
[Serializable]
public sealed class SerializableException : Exception
{
    public SerializableException()
    {
    }

    public SerializableException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }

    public SerializableException(Exception exception)
        : base(exception.Message, exception.InnerException == null ? null : new SerializableException(exception.InnerException))
    {
        _data = exception.Data;
        HelpLink = exception.HelpLink;
        HResult = exception.HResult;
        Source = exception.Source;
        _stackTrace = exception.StackTrace;
    }

    public override IDictionary Data { get { return _data; } }
    public override string StackTrace { get { return _stackTrace; } }

    private readonly IDictionary _data;
    private readonly string _stackTrace;
}
Dunge
  • 89
  • 1
  • 11