Ist es möglich, den Code vollständig außerhalb der Geschäftslogik zu protokollieren?

12

Mit Hilfe von AOP kann ich den Protokollierungscode aus meiner Geschäftslogik entfernen. Aber ich denke, es kann nur verwendet werden, um einfache Dinge zu protokollieren (dh Protokollierungsmethodenein- / -ausgang und Parameterwerte).

Was ist jedoch, wenn ich etwas in meiner Geschäftslogik protokollieren muss? z.B

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

Die obige Beispielmethode ist möglicherweise nicht klar genug. Ich möchte hier zeigen, dass die Methode aus Sicht der Domäne als kleinste Einheit behandelt werden sollte. Es sollte nicht in kleinere Stücke geteilt werden.

Ist es möglich, mehr als 3 Protokollierungscodes aus der Methode zu entfernen? Was ist die beste Vorgehensweise für eine solche Situation?

Charlie
quelle
Ich bin mir ziemlich sicher, dass die "Step1" - und "Step2" -Protokolle Ihres Beispiels Teil eines Business Logic Audit Trails und das erste eine technische Protokollierung sein sollten. Ich würde das zuerst sortieren ...
tofro

Antworten:

1

Sicher!

Nach meiner Erfahrung gibt es jedoch zwei allgemeine Arten der nützlichen Protokollierung:

Alles protokolliert: Protokolle, die über Profiling-APIs erstellt wurden. Gut zum Erkennen von Leistungsproblemen und zum Melden von Ausnahmen. Sehr laut.

Geschäftsereignisprotokolle : In der Geschäftslogik aufgerufene Protokolle. Alles, was das Geschäft interessieren könnte. Minimales Rauschen. Nur bemerkenswerte, logische "geschäftliche" Ereignisse. Gut für Audits und KPIs ...

Daher würde ich zwei Dinge sehr empfehlen. Führen Sie zunächst die Aufgaben anderer Überwachungstools wie New Relic aus und verwenden Sie die .NET-Profilierungs-API 1 . Zweitens protokollieren Sie logische Geschäftsereignisse in Ihrer Geschäftslogik . Das Aufzeichnen bestimmter Ereignisse ist Geschäftslogik.

Und normalerweise würde ich AOP für keine der beiden Protokollierungsarten 2 vorschlagen . Nach meiner Erfahrung möchten Sie entweder alles , was bedeutet, dass Sie einen Profiler verwenden, oder Sie möchten logische / geschäftliche Ereignisse. Und im letzteren Fall ist es meiner Meinung nach einfacher, den Logger einfach in der Geschäftslogik aufzurufen .


1. Aber im Ernst, sparen Sie sich Tausende von Stunden Mühe und verwenden Sie einfach ein vorhandenes Profiler-Tool ...

2. Dies setzt natürlich voraus, dass Sie meine Meinung teilen, dass ein Aspekt kein großartiger Ort ist, um Geschäftsregeln zu verbergen!

svidgen
quelle
Ich bin mit den "Geschäftsereignisprotokollen" ziemlich einverstanden, und genau wie die Antworten anderer werde ich den Protokollcode in der Geschäftslogik behalten. Und für den Teil "Alles protokolliert" bevorzuge ich die Verwendung einer AOP-Lösung, da diese dem SRP folgt und meine Geschäftslogik nicht verschmutzt. Wie auch immer, ich werde zuerst einen Blick auf die Profiling-API werfen.
Charlie
10

Natürlich können Sie dafür problemlos AOP verwenden. Einfach die Teile umgestalten

  • Benutzer anhand der ID abrufen
  • Schritt 1
  • Schritt 2

in separate Methoden (wie Sie es hätten tun sollen, um Ihren Code sauberer zu machen). Jetzt können Sie Ihr AOP-Framework einfach so konfigurieren, dass die Methodenaufrufe Ihrer Wahl protokolliert werden ( wie hier gezeigt ). Die Ausnahme kann direkt vom Anrufer protokolliert werden, ohne dass AOP verwendet werden muss, um dies aus der Geschäftslogik herauszuholen.

Zu Ihrer Bearbeitung:

Ich möchte hier zeigen, dass die Methode aus Sicht der Domäne als kleinste Einheit behandelt werden sollte. Es sollte nicht in kleinere Stücke geteilt werden

Warum sollte es nicht? Wenn Sie in einem "Geschäftslogikkontext" "etwas" protokollieren möchten, das es wert ist, protokolliert zu werden, und wenn diesem "Etwas" ein sinnvoller Name zugewiesen werden kann, ist es in den meisten Fällen sinnvoll, den Code in eine Methode umzugestalten seine eigene. Wenn Sie AOP verwenden möchten, müssen Sie Ihren Code so strukturieren, wie Sie ihn wahrscheinlich hätten strukturieren müssen, unabhängig von den Protokollierungsanforderungen. Sie können dies als Nachteil von AOP oder als Vorteil interpretieren, da Sie ein Feedback erhalten, wo Ihre Codestruktur verbessert werden kann.

Doc Brown
quelle
Es ist schlimm, dass mein Beispiel nicht klar genug ist. Was ich im Beispiel tatsächlich zeigen möchte, ist, dass die Methode aus Sicht der Domäne die kleinste Einheit ist, die nicht in kleinere Teile unterteilt werden sollte.
Charlie
@ Charlie: Das Beispiel ist völlig klar. Ihr Missverständnis hier ist wahrscheinlich, dass Sie denken, es wäre eine gute Idee, größere Methoden als Schritte zu haben. Und das ist meiner Meinung nach falsch, es ist keine gute Idee. Es ist ein klares Zeichen, dass verschiedene Schritte eine Protokollierung wert sind. Diese Schritte sollten eine Abstraktion haben, einen eigenen Namen und daher eine eigene Methode.
Doc Brown
@Charlie nichts hindert Sie daran, 3 private Methoden auszuführen, die von Ihrer Einheit oder Arbeit aufgerufen werden. Auf diese Weise blieb es von außen gleich, aber jetzt haben Sie die erforderliche Abstraktion für Ihre Protokollierung.
Rémi
Dieser Ansatz ist in Ordnung, wenn Sie Ihre Codestruktur durch Protokollieren von Bedenken steuern möchten. Manchmal möchten Sie es jedoch mit etwas anderem fahren.
John Wu
@ JohnWu: Die Codestruktur sollte die verschiedenen Bedenken / Schritte widerspiegeln, unabhängig von der Protokollierungsanforderung. Das ist es, was hier die Codestruktur antreibt. Sobald dieses Problem gelöst ist, kann die Protokollierung von AOP durchgeführt werden. Dies ist eher ein "Nebeneffekt", wenn der Code eine bessere Struktur erhält. Ich denke, es ist nicht das Protokollierungsproblem, das die Codestruktur antreibt, sondern vielmehr, dass die Anforderung, AOP für die Protokollierung zu verwenden, transparenter macht, dass dem Code eine Struktur fehlt, die er auch haben sollte.
Doc Brown
3

Wenn das Protokollieren nicht Teil der Geschäftsanforderungen ist, ist es, wie Sie sagen, am besten, es vollständig aus Ihrem Code herauszuhalten.

Das bedeutet, dass Sie wirklich keine Dinge wie "Schritt 1 abgeschlossen" protokollieren möchten. Obwohl es anfangs zum Debuggen nützlich sein könnte, wird es in der Produktion nur Gigabyte Müll erzeugen, den Sie nie sehen werden.

Wenn es sich bei Step1Complete um eine Art Geschäftsereignis handelt, für das weitere Maßnahmen erforderlich sind, kann es durch ein gutes altmodisches Ereignis angezeigt werden, ohne dass Sie gezwungen sind, einen ILogger oder ähnliches in Ihre Klasse einzufügen

Ewan
quelle
Das ist was ich gedacht habe. Ich kann keinen vernünftigen Fall für die Protokollierung innerhalb eines Domain- / Geschäftsmodell-POCO finden. Die Protokollierung passt natürlich außerhalb der Kerngeschäftsmodelle IMO.
Jleach
2

Mithilfe eines gängigen Musters können Sie Protokollierungscode aus Ihrer Geschäftslogik ziehen. Möglicherweise lohnt es sich jedoch nicht, dies zu tun

Wenn Sie beispielsweise einen Listener verwenden (Handcraft One oder Event Bus usw.), sieht Ihr Code folgendermaßen aus

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

Durch die Implementierung der Protokollierung im Listener befindet sich die Protokollierungslogik nicht mehr in Ihrer Geschäftslogik.

Möglicherweise ist dies jedoch nicht immer realistisch, da Sie möglicherweise nicht immer ein aussagekräftiges Ereignis Ihrer Logik definieren können.

Ein anderer Ansatz ist ein Mechanismus wie Dtrace in Solaris, mit dem Sie in laufende Prozesse einbinden können (ich glaube, es gibt Möglichkeiten, ähnliche Aktionen in C # durchzuführen?), Sodass Protokollierung und statistische Erfassungen zur Laufzeit definiert werden können. Es gibt noch andere Nachteile.

Adrian Shum
quelle
Ein Problem, das AOP zu lösen versucht, ist das Problem, dass Code für nicht geschäftlichen Code (wie das Protokollieren von Anrufen), der mit "Geschäftscode" verwoben ist, unlesbar wird. Das Ersetzen eines "Loggers" durch einen "Listener" löst dies nicht, die Lesbarkeit des Codes wird nicht geändert,
Doc Brown
2

Ein anderer Ansatz besteht darin, die Geschäftsprotokollierung und die technische Protokollierung voneinander zu trennen. Dann können wir die Geschäftsprotokollierung als "Audit" bezeichnen und bestimmte Geschäftsregeln wie Speicherbedingungen und Verarbeitungsregeln wie Business Activity Monitoring anwenden.

Auf der anderen Seite ist die technische Protokollierung oder einfach "Protokollierung" das letzte Mittel, um eine Spur von technischen Problemen zu hinterlassen. Es sollte asynchron, schnell und tolerant sein, wenn die Protokollnachricht nicht beibehalten wird. Außerdem sollten Protokollnachrichten die geringstmögliche Anzahl von Proxys durchlaufen, um nahe an der Ursache des Problems zu sein.

Die Logik der Protokollierung ist sehr variabel und eng mit der Implementierung verbunden. Müssen Sie sie also wirklich vom Code trennen?

Die Logik des Audits sollte als Domänenlogik betrachtet und entsprechend behandelt werden.

In der hexagonalen Architektur gibt es beispielsweise möglicherweise einen Überwachungsport sowie Clients, Speicher und MQ-Ports (und möglicherweise Metriken und Steuerelemente). Dies wäre ein sekundärer Port, dh die Aktivität an diesem Port wird vom Geschäftskern und nicht von externen Systemen ausgelöst.

iTollu
quelle
Ich habe Ihnen sehr zugestimmt, dass es zwei Arten der Protokollierung gibt. Aber ich verstehe nicht, dass die Logik der Protokollierung sehr variabel ist und eng mit der Implementierung verbunden ist. Meinen Sie hier die technische Protokollierung? Ich denke, dass es für die technische Protokollierung verwendet wird, um Methodenein- / -ausgänge und Parameterwerte zu protokollieren, was besser ist, außerhalb der Methode zu sitzen.
Charlie
@Charlie Ja, mit "The Logging" meine ich technische Protokollierung. Die Erfassung von Eingangs- / Ausgangs- / Parameterwerten ist bei reinen Funktionen ausreichend. Dann oder natürlich könnten Sie einen Aspekt oder eine Logger-Monade verwenden. Aber reine Funktionen sind insofern großartig, als sie testbar sind. Probleme, die der Logger verfolgen soll, werden wahrscheinlich während des Entwickelns / Debuggens gelöst. Bei unreinen Funktionen, bei denen die technische Protokollierung am nützlichsten ist, möchten Sie alle nebenwirkenden Aufrufparameter / -ergebnisse und jede Ausnahme protokollieren.
iTollu
1

Möglichkeiten, um zu vermeiden, dass Sie sich direkt in einer Klasse oder Methode anmelden:

  1. Lösen Sie eine Ausnahme aus und melden Sie sich in einem Catch-Block weiter oben im Aufrufbaum an. Wenn Sie eine Protokollebene erfassen müssen, können Sie eine benutzerdefinierte Ausnahme auslösen.

  2. Rufen Sie Methoden auf, die bereits für die Protokollierung instrumentiert sind.

Robert Harvey
quelle
1
Hat sich gezeigt, dass die Protokollierung ein Problem darstellt und es sogar wert ist, "behoben" zu werden?
Whatsisname
1

Ist es wirklich erforderlich, Ihre Protokollierung von Ihrer Geschäftslogik zu trennen? Die Protokollierung entspricht der geschriebenen Geschäftslogik und ist daher sinnvoll, sich in derselben Klasse / Funktion zu befinden. Noch wichtiger ist, dass die Lesbarkeit des Codes erleichtert wird.

Wenn Sie die Protokollierung jedoch wirklich von Ihrer Geschäftslogik trennen möchten, sollten Sie benutzerdefinierte Ausnahmen auslösen und diese Ausnahmen für die Protokollierung übergeben.

user88748
quelle
0

Nein, nicht in c #

OP, die Antwort auf Ihre spezifische Frage lautet nein, nicht in c #. Es mag andere, nativere AOP-Sprachen geben, aber alle Ansätze für AOP in c #, die ich gesehen habe, können nur aspektierte Verhaltensweisen im Kontext eines Verknüpfungspunkts anwenden , was bedeutet, dass zwischen einem Codeblock und ein Kontrollfluss bestehen muss Ein weiterer. Bestimmte Verhaltensweisen werden nicht in der Mitte einer Methode ausgeführt, außer natürlich durch Aufrufen einer anderen Methode.

Sie können bestimmte Teile der Protokollierung "apsect-ize"

Davon abgesehen könnten Sie bestimmte Bedenken in Bezug auf die Protokollierung beseitigen, nur nicht das Schreiben von Protokollen. Beispielsweise könnte ein Schnittpunkt, der beim Eintritt in eine Methode ausgeführt wird, einen Protokollierungskontext einrichten und alle Eingabeparameter ausgeben, und beim Beenden Ausnahmen auslösen oder ein Protokoll für den permanenten Speicher festschreiben.

Das Schreiben von Protokollen ist sowieso kein Aspekt

Ich würde hinzufügen, dass das Schreiben von Protokollen sowieso kein wirkliches Querschnittsthema ist. Zumindest keine Debug-Protokollierung. Mein Beweis dafür ist, dass Sie keine Querschnittsanforderung schreiben konnten, die vollständig erklärt, was dieser Aspekt bewirken würde - sie ist für jeden Fall spezifisch, da der Zweck des Schreibens des Protokolls darin besteht, zu reflektieren, was mit dem los ist Logik, und die Logik in jeder Methode sollte einigermaßen eindeutig sein (siehe DRY ).

Mit anderen Worten, es gibt eine untrennbare logische Abhängigkeit zwischen dem Schreiben von Protokollen und dem Material, über das geschrieben wird. Sie können es nicht verallgemeinern.

Aber Auditing ist

Wenn Sie eine funktionale Protokollierungsanforderung haben (z. B. eine Überwachungsprotokollierung zur Unterstützung einer Nicht-Ablehnungsanforderung ), würden einige argumentieren (und ich würde zustimmen), dass, wenn Sie feststellen, dass Sie diese Protokollschreibvorgänge mitten in einer Methode ausführen müssen, Sie haben Ihren Code nicht so strukturiert, dass er mit dem aspektorientierten Denken übereinstimmt. In diesem Fall sollten Sie Code in separate Methoden extrahieren, bis Sie die erforderliche Granularität erreicht haben.

John Wu
quelle