Die erste Vernissage – Behaviour Monitoring mit EBC

Neulich hatte ich das Problem, dass sich die Applikation komisch verhielt. In Gesprächen mit den Entwicklern  hieß es, dass das die anderen Schichten sein müssen, weil man ja selbst alles richtig gemacht hat. In diesem Moment hätte ich gerne zu altmodischen Erziehungsmaßnahmen gegriffen.

Teleskop_RefraktorMir fehlte schlichtweg die Möglichkeit mal eben in die Applikation “hineinzublicken” und festzustellen, wo die Probleme sind. Statt dessen wurden diverse Termine und Maßnahmen festgelegt, wie jede einzelne Schicht Informationen über ihr Verhalten preis geben.
Zur Information: Es gab bis dato kein vernünftigen Tracer, Profiler oder andere Monitoring Tools.
Nach Durchführung eines Monitoringtages, mit erheblichem Aufwand, durften sich die Einzelnen Teams hinsetzen und analysieren, was mit ihrem Code zur Laufzeit passiert. Die Ergebnisse zeigten aber immer noch nicht die von mir gewünschten Daten, oder zumindest nicht ausreichend. Als wurden weitere Termine festgelegt.

Ich finde, dass dies keine gute Herangehensweise ist, das Verhalten einer Applikation zu ermitteln. Ich stelle mir eher ein System vor, dem man zu einem Zeitpunkt x sagt, dass es nun das Verhalten mit aufzeichnen soll. In Teilen klappt das ja schon recht gut, wenn man im Code Tracing-Informationen hinterlässt und mit dem Syinternals Debug Viewer das Tracing verfolgt. Mit reicht das aber nicht. Ich möchte etwas professionelles, etwas komponentenorientiertes, etwas, was man wie ein Bauteil in einem System benutzt. Ich möchte eine EBC für das Monitoring haben.

Exception Handling als Grund

Es gibt viele Gründe, wissen zu wollen was mit der Applikation gerade geschieht. Einer davon ist die Fehlerbehandlung.
In einem Post zuvor habe ich über eine EBC geschrieben, die das Exception Handling in einem System übernimmt. Diese Komponente möchte ich hier wiederverwenden, erweitern und zu einer Fehlerdiagnoseplatine zusammensetzen.
Alte Variante
  1. public interface IExceptionHandler {
  2.     void In_HandleException(Exception exceptionToHandle);
  3.     event Action<Exception> OutHandledException;
  4. }
  5.  
  6. public class DefaultExceptionHandler : IExceptionHandler {
  7.     public event Action<Exception> OutHandledException;
  8.  
  9.     public void In_HandleException(Exception exceptionToHandle) {                     
  10.         // Tue Dinge mit der Exception            
  11.         this.OnHandledException(exception);             
  12.     }          
  13.    
  14.     private void OnHandledException(Exception exception) {            
  15.         if (this.OutHandledException != null)            
  16.             this.OutHandledException(exception);             
  17.     }
  18. }


Diese EBC entspricht schon einwenig dem, was ich mir unter einem Bauteil (!) zum EceptionHandling vorstelle, aber tatsächlich fehlt noch einiges.
Was passiert beim Exception Handling?
Klassischerweise wird eine Exception im Code per catch gefangen und entweder an Ort und Stelle weiterverarbeitet oder an eine Komponente übergeben, die sich dann um die Verarbeitung kümmert. Auf jeden Fall wird der normale Programmablauf verändert. Die Verarbeitung einer Exception erfolgt nach zwei Gesichtspunkten:
  • Eine Exception wurde erwartet und wird zum Steuern des Programmablaufs verwendet und
  • Eine Exception wurde nicht erwartet
Eine unerwartetete Exception gehört immer in die Kategorie “kritisches Verhalten im System” und muss dokumentiert werden mit den Daten Wer, Wann, Was. Im Falle einer erwarteten Exception wird es etwas schwieriger, denn den Programmablauf zu steuern war im Allgemeinen den if-then-else Codeblöcken überlassen. Wie geht das aber mit den EBCs? Das möchte ich hier nicht lösen, aber ich gebe mal eine Stichwort spezielle Expected-Exception-Pin’s (Wow).

Behaviour Monitoring

(Ich mag das Wort Behaviour, es klingt gut und hat eine schöne Form) :D Mit dem bisherigen Exception Handler konnte nicht wirklich viel getan werden, daher werde ich nun eine Erweiterung vornehmen, die am Ende eine Fehlerdiagnoseplatine darstellt. Zunächst einmal Refaktorisiere ich die Pins des Exception Handlers.
Die neue Variante
  1. public class HandledException : ExceptionMessage {
  2.     public HandledException(object sender, Exception sourceException, ExceptionLevel exceptionLevel)
  3.         : base(sender, sourceException) {
  4.         this.ExceptionLevel = exceptionLevel;
  5.     }
  6.     public ExceptionLevel ExceptionLevel {
  7.         get;
  8.         private set;
  9.     }
  10. }
  11.  
  12. public delegate void OutExceptionPin(HandledException handledException);
  13.  
  14. public interface IExceptionHandler {
  15.     void InHandleException(ExceptionMessage exceptionMessage);
  16.     event OutExceptionPin OutHandledException;
  17. }


Als erstes habe ich eine neue Klasse angelegt, die aussagt, das dies eine behandelte Exception ist. Dann habe ich einen spezialisierten Out-Pin definiert, weil dies im Code besser lesbar ist. Und zu guter Letzt habe ich das Interface entsprechend angepasst.

Heraus kommt:
Neuer Exception Handler
  1. public class ExceptionHandler : IExceptionHandler {
  2.  
  3.     public event OutExceptionPin OutHandledException;
  4.  
  5.     public ExceptionHandler(String configuration) {    
  6.     }
  7.  
  8.     public void InHandleException(ExceptionMessage exceptionMessage) {
  9.         
  10.         // Spezialitäten aus der Config laden und durchführen
  11.         // z.B. Mapping einer Exception oder anpassen der Exception Message
  12.  
  13.         // Setzen eines Exceptionlevel entsprechend des Schweregrades
  14.         ExceptionLevel execptionLevel = config.GetExceptionLevelByException(exceptionMessage);
  15.  
  16.         HandledException handledException = new HandledException(
  17.             exceptionMessage.Sender,
  18.             exceptionMessage.ExceptionData,
  19.             execptionLevel);
  20.  
  21.         this.OnHandledException(handledException);
  22.     }
  23.  
  24.     private void OnHandledException(HandledException handledException) {
  25.         if (this.OutHandledException != null)
  26.             this.OutHandledException(handledException);
  27.     }
  28.  
  29. }
Das sieht schon mal gar nicht so schlecht aus. Wie kommen nun Monitoring und Exception Handling zusammen? Ganz einfach über eine Platine, die ich anlegen muss und mit der ich die Pins verbinde.

Die Exception Handling Platine
Exception Handling Platine
  1. public class ExceptionHandlerBoard : IExceptionHandlerBoard {
  2.  
  3.     private Action<ExceptionMessage> _handleException;
  4.  
  5.     public ExceptionHandlerBoard(IDiagnosticMonitor diagnosticMonitor, IExceptionHandler exceptionHandler) {
  6.         this._handleException = exceptionHandler.InHandleException;
  7.         exceptionHandler.OutHandledException += e => {
  8.             diagnosticMonitor.In_WriteInputMessage(e.ExceptionData.Message);
  9.         };
  10.     }
  11.  
  12.     public void In_HandleException(object sender, Exception exceptionToHandle) {
  13.         ExceptionMessage exceptionMessage = new ExceptionMessage(sender, exceptionToHandle);
  14.         this._handleException(exceptionMessage);
  15.     }
  16. }

Und der Kontrakt dazu
Der Kontrakt
  1. public interface IExceptionHandlerBoard {
  2.     void In_HandleException(object sender, Exception exceptionToHandle);
  3. }

EBC Exception Handling BoardDas ist denkbar einfach und leicht anzuwenden. Aber was ist mit dem Out Pin, der doch angeblich bei jeder EBC dran sein soll? Tja, ich denke, dass eine Platine, die nur ein Aspekt des Systems ist, als Oneway-Bauteil formuliert werden kann. Ich sehe derzeit keine Notwendigkeit, dass die Platine mit weiteren Bauteilen verdrahtet werden muss.

 
 
 
Der Monitor
Das dokumentieren der Exception wird über den Monitor realisiert, der durch den Kontrakt IDiagnosticMonitor definiert wird.
Monitor Kontrakt
  1. public interface IDiagnosticMonitor {
  2.     void In_WriteInputMessage(String inputMessage);
  3. }
Der In Pin bekommt eine Nachricht (sicher ändert sich der Typ noch), die er dann je nach Impplementierung verarbeiten kann. Ein ConsoleMonitor sieht beispielsweise so aus:
Console Monitor
  1. public class ConsoleMonitor : IDiagnosticMonitor {
  2.  
  3.     public void In_WriteInputMessage(string inputMessage) {
  4.         Console.WriteLine(inputMessage);
  5.     }
  6. }
Ich kann mir noch weitere Monitore vorstellen, z.B. LogMonitor oder SqlMonitor. Die Grenzen sind offen und das Kombinieren von Exception Handler und Monitor macht absolut Sinn, wenn dies auf einer Platine geschieht.

Fazit

Wow, so einfach kann es sein, Komponenten zu entwerfen, die auch noch wichtige Aufgaben übernehmen und dabei den Code nicht beschmutzen. Die ExceptionHandler-Platine kann nun in jeder anderen Platine verwendet werden und je nach Kontext können unterschiedliche Platinen definiert werden.




Jan