Was sind aus gestalterischer Sicht die besten Methoden für die Protokollierung? [geschlossen]


11

Ich möchte einer Anwendung, an der ich gerade arbeite, die Protokollierung hinzufügen. Ich habe bereits die Protokollierung hinzugefügt, das ist hier kein Problem.

Was sind aus gestalterischer Sicht in einer objektorientierten Sprache die besten Methoden für die Protokollierung, die OOP und Mustern folgen?

Hinweis: Ich mache dies derzeit in C #, daher sind Beispiele in C # natürlich willkommen. Ich würde auch gerne Beispiele in Java und Ruby sehen.


Edit: Ich benutze log4net. Ich weiß nur nicht, wie ich es am besten anschließen kann.

Antworten:


6

Die beste Vorgehensweise, die ich empfehlen würde, ist die Verwendung von log4j, anstatt Ihre eigenen zu rollen. (Wurde von Java sowohl nach C # als auch nach Ruby portiert, gilt also für alle drei Sprachen, an denen Sie interessiert sind.)

Wenn Sie diese Handbuchseite lesen, werden Sie einige andere bewährte Methoden entdecken. B. leichtgewichtig, außerhalb Ihrer Anwendung konfigurierbar, in der Lage, die Protokollierung für verschiedene Teile Ihrer Anwendung unabhängig voneinander zu aktivieren und zu verringern usw.


5

Wo ich arbeite, schreiben wir viele .NET-Desktop-Apps. Normalerweise implementieren wir zwei Ereignisse in unseren Komponenten, eines zum Protokollieren von Informationen und das andere zum Protokollieren von Ausnahmen (obwohl wir die Ausnahmen häufig in die Luft sprudeln lassen, anstatt das separate Ereignis auszulösen. Dies hängt von der Situation ab). Bei Verwendung dieser Architektur muss keine unserer Bibliotheken wissen, wie die Protokollierung implementiert ist oder wie die Informationen verwendet, gespeichert oder behandelt werden. Wir lassen dann die Anwendung die Protokollierungsereignisse auf eine Weise behandeln, die für diese App angemessen ist. Vor einigen Jahren machte diese Architektur unseren Wechsel von der MS Enterprise Library-Protokollierung zur BitFactory-Protokollierungskomponente zu einem sehr einfachen Übergang.


+1 für die Verwendung eines Ereignis- / Beobachtermusters: Ändern Sie den Beobachter, Sie haben die Protokollierung geändert
Matthieu M.


2

Persönlich nehme ich das Protokollierungsframework Ihrer Wahl (in meinem Fall Entlib, weil ich mit .NET arbeite) und schreibe einen AOP-Aspekt für die Protokollierung.

Sie können dann beliebige Methoden / Eigenschaften / Klassen / Namespaces zuweisen und ihnen Protokollierung hinzufügen, ohne die Quelle zu überladen.


Es klingt sehr interessant, aber ich habe Vorbehalte, was Sie protokollieren könnten und wie informativ das Protokoll wäre (dh mehr als "nur" Instrumentierung von Methoden). Würde gerne ein funktionierendes Beispiel für diesen Ansatz sehen, um zu sehen, was getan werden kann und was nicht. Zumal ich gerade mit einer neuen App anfange und gerne sehen möchte, wo / wie weit ich diese tragen könnte.
Marjan Venema

@marjan Venema: Die postscharfe Dokumentation enthält ein Beispiel für einen Aspekt, der das Eingeben / Verlassen einer Methode protokolliert. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… Im Fall von Post Sharp wird der Code beim Erstellen aus dem Attribut in die Quelle gesponnen , sodass die Leistung nicht beeinträchtigt wird, wie dies bei einigen anderen der Fall ist.
Steven Evers

1

Das System, an dem ich gerade arbeite, verwendet eine ereignisgesteuerte Architektur und Messaging, sodass die meisten Aktionen in unserem System das Ergebnis eines Befehls sind und zu Ereignissen führen (als ausgelöste DTO-Klassen anstelle eines Standarddelegatenereignisses). Wir fügen Ereignishandler hinzu, deren einziger Zweck die Protokollierung ist. Dieses Design hilft uns, uns nicht zu wiederholen und muss auch keinen vorhandenen Code ändern, um Funktionen hinzuzufügen / zu ändern.

Hier ist ein Beispiel für eine solche Protokollierungsklasse, die alle Ereignisse behandelt, die aus einem engen Abschnitt unserer Anwendung protokolliert werden sollen (diejenigen, die sich auf eine bestimmte Inhaltsquelle beziehen, aus der wir importieren).

Ich werde nicht unbedingt sagen, dass dies zweifellos eine bewährte Methode ist, da ich meine Meinung darüber zu ändern scheine, was und wie ich häufig protokolliere - und jedes Mal, wenn ich ein Protokoll zur Diagnose eines Problems verwenden muss, finde ich unweigerlich Möglichkeiten, Verbesserungen am zu vornehmen Informationen, die ich aufzeichne.

Ich werde jedoch sagen, dass das Aufzeichnen der relevanten Informationen (insbesondere in Strg-F / Suche durchsuchbar) der wichtigste Teil ist.

Der zweitwichtigste Teil besteht darin, den Protokollierungscode von Ihrer Hauptlogik zu entfernen - dies kann eine Methode hässlich und lang und sehr schnell verschlungen machen .

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

Wie andere gesagt haben, die Verwendung log4joder log4netoder ein anderes gut ausgebautes Logging - Framework.

Ich mag es nicht, wenn Protokollierungscode der Geschäftslogik im Wege steht. Deshalb benutze ich Log4PostSharp. Das heißt, ich kann aspektorientierte Programmierung verwenden , um Methoden wie diese zu kommentieren:

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

Oder jede Methode in einer Baugruppe wie folgt:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

Ich bin nicht sicher, ob ein Framework dies tut, aber aus gestalterischer Sicht würde ich die Informationen modellieren, die in hauptsächlich drei Kategorien protokolliert werden müssen:

  1. Ablaufverfolgung auf Methodenebene
  2. Ausnahmeprotokollierung
  3. Entwickler von zusätzlichen Laufzeitinformationen sind der Ansicht, dass dies für die Untersuchung im Falle eines Laufzeitfehlers (oder eines Verhaltens, das sich nur auf die Laufzeitsituation bezieht) von entscheidender Bedeutung ist.

Für die ersten beiden Kategorien sollte mein ideales Protokollierungsframework sie als Post-Build-Prozess behandeln und für Entwickler transparent sein. Es wäre schön, Assemblys deklarativ Protokollierung hinzuzufügen, etwa wie folgt:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

Für die 3. Kategorie können Programmierer einfach eine oder mehrere dedizierte "Protokollierungs" -Methoden erstellen und die Ablaufverfolgung für die erste Kategorie nutzen. Die Protokollierungsmethoden dienen lediglich dazu, einen Stub-Punkt bereitzustellen, auf den Ablaufverfolgungsregeln angewendet werden können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.