Jakie są najlepsze praktyki rejestrowania z perspektywy projektowania? [Zamknięte]

11

Chcę dodać rejestrowanie do aplikacji, nad którą aktualnie pracuję. Wcześniej dodawałem rejestrowanie, to nie jest problem.

Ale z perspektywy projektowania w języku obiektowym, jakie są najlepsze praktyki dotyczące rejestrowania zgodne z OOP i wzorcami?

Uwaga: obecnie robię to w C #, więc przykłady w C # są oczywiście mile widziane. Chciałbym również zobaczyć przykłady w Javie i Ruby.


Edycja: Używam log4net. Po prostu nie wiem, jak najlepiej to podłączyć.

Edgar Gonzalez
źródło

Odpowiedzi:

6

Najlepszą praktyką, którą poleciłbym, jest używanie log4j zamiast tworzenia własnych. (Który został przeniesiony z Java na C # i Ruby, więc dotyczy wszystkich 3 interesujących Cię języków.)

Jeśli przeczytasz tę stronę podręcznika, odkryjesz kilka innych najlepszych praktyk. Na przykład lekkość, możliwość konfiguracji poza aplikacją, możliwość niezależnego włączania i wyłączania rejestrowania różnych części aplikacji i tak dalej.

btilly
źródło
5

Tam, gdzie pracuję, piszemy wiele aplikacji komputerowych .NET. Zwykle implementujemy 2 zdarzenia w naszych komponentach, jedno do rejestrowania informacji, a drugie do rejestrowania wyjątków (chociaż często pozwalamy wyjątkom na bańki zamiast wywoływania osobnego zdarzenia. Zależy to od sytuacji). Korzystając z tej architektury, żadna z naszych bibliotek nie musi wiedzieć, w jaki sposób wdrażane jest rejestrowanie, ani w jaki sposób informacje są wykorzystywane, przechowywane lub przetwarzane. Następnie mamy aplikację do obsługi zdarzeń rejestrowania w sposób odpowiedni dla tej aplikacji. Wiele lat temu ta architektura sprawiła, że ​​przejście z używania rejestrowania MS Enterprise Library do komponentu rejestrującego BitFactory było bardzo prostym przejściem.

Ali
źródło
+1 za użycie wzorca Event / Observer: zmień obserwatora, zmieniłeś rejestrowanie
Matthieu M.
2

Osobiście wybieram środowisko rejestrowania do wyboru (w moim przypadku Entlib, ponieważ pracuję z .NET) i piszę aspekt AOP do rejestrowania.

Następnie możesz przypisać dowolne metody / właściwości / klasy / przestrzenie nazw i dodać do nich rejestrowanie bez zaśmiecania źródła.

Steven Evers
źródło
Brzmi bardzo interesująco, ale mam zastrzeżenia co do tego, co będziesz mógł zalogować i jak informacyjny byłby dziennik (tj. Więcej niż „tylko” instrumentacja metod). Chciałbym zobaczyć działający przykład takiego podejścia, aby zobaczyć, co można, a czego nie można zrobić. Zwłaszcza, że ​​dopiero zaczynam pracę z nową aplikacją i chciałbym zobaczyć, gdzie / jak daleko mogę to zabrać.
Marjan Venema
@marjan Venema: Dokumentacja po ostrym zawiera przykład aspektu, który rejestruje wchodzenie / wychodzenie z metody. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/... W przypadku Post Sharpa w czasie kompilacji kod z atrybutu w źródle, więc nie wpływa to na wydajność, tak jak robią to inni.
Steven Evers
1

System, nad którym obecnie pracuję, wykorzystuje architekturę i komunikację sterowaną zdarzeniami, dzięki czemu większość działań w naszym systemie jest wynikiem polecenia i powoduje zdarzenia (jako wysyłane klasy DTO, a nie standardowe zdarzenie delegowane). Dołączamy procedury obsługi zdarzeń, których jedynym celem jest obsługa rejestrowania. Ten projekt pomaga nam się nie powtarzać, a także nie musi modyfikować istniejącego kodu, aby dodać / zmienić funkcjonalność.

Oto przykład jednej z takich klas rejestrowania, która obsługuje wszystkie zdarzenia, które mają być rejestrowane z wąskiej sekcji naszej aplikacji (dotyczące jednego konkretnego źródła treści, z którego importujemy).

Nie muszę koniecznie mówić, że jest to najlepsza praktyka, ponieważ wydaje mi się, że zmieniam zdanie na temat tego, co i jak często rejestrować - i za każdym razem, gdy muszę używać dziennika do diagnozowania problemu, nieuchronnie znajduję sposoby na ulepszenie informacje, które rejestruję.

Powiem jednak, że najważniejsze jest zapisywanie istotnych informacji (zwłaszcza w Ctrl-F / znajdź wyszukiwalny sposób).

Drugą najważniejszą częścią jest usunięcie kodu logowania z głównej logiki - może to uczynić metodę brzydką, długą i skomplikowaną bardzo szybko.

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));
    }
}
quentin-starin
źródło
1

Jak powiedzieli inni, użyj log4jlub log4netinnego dobrze zbudowanego środowiska rejestrowania.

Naprawdę nie lubię rejestrowania kodu, który przeszkadza logice biznesowej. Dlatego używam Log4PostSharp. Oznacza to, że mogę używać programowania zorientowanego na aspekty do opisywania metod takich jak to:

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

Lub każda metoda w takim zestawie:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]
Peter K.
źródło
0

Nie jestem pewien, czy robi to jakikolwiek framework, ale z punktu widzenia projektowania, modelowałbym informacje, które należy zalogować głównie w trzech kategoriach:

  1. śledzenie na poziomie metody
  2. rejestrowanie wyjątków
  3. twórcy dodatkowych informacji w czasie wykonywania uważają, że ma to zasadnicze znaczenie dla badania w przypadku awarii w czasie wykonywania (lub dowolnego zachowania związanego z sytuacją tylko w czasie wykonywania).

W przypadku pierwszych dwóch kategorii moja idealna struktura rejestrowania powinna obsługiwać je jako proces po kompilacji i przejrzysta dla programistów. Byłoby miło dodać deklaratywne dodawanie rejestrowania do zestawów, podobnie jak poniżej:

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,
}

W przypadku trzeciej kategorii programiści mogą po prostu utworzyć jedną lub więcej dedykowanych metod „rejestrowania” i wykorzystać śledzenie dla pierwszej kategorii. Metody rejestrowania nie służą niczym innym, jak tylko udostępnieniu punktu pośredniego, do którego można zastosować reguły śledzenia.

Codism
źródło