C++ Uniwersalny logger

Tworzenie wielopoziomowych logów w programie

Podczas developmentu bardziej rozbudowanych aplikacji niezwykle przydatna jest możliwość logowania danych do różnych strumieni w zunifikowany sposób.Jeszcze w lepszej sytuacji jesteśmy gdy możemy logować jednocześnie do kilku wybranych strumieni.

Bardzo użyteczna przy tym jest możliwość wybierania poziomu logu (info, error, etc) oraz możliwość dodawania timestampu na życzenie. Wszystkie te funkcjonalności można wygodnie (dzięki m.in bibliotece chrono) i bezpiecznie (wykorzystując smart pointery) zaimplementować w modern cpp. Moją implementację (którą można znależźć tutaj: github) opiszę poniżej.

Unifikację obsługi strumieni zapewnia nam jednolity interfejs - wszystkie rodzaje logery dziedziedziczą po virtualnej klasie bazowej.

Interfejs loggera będący abstrakcyjną klasą bazowa.


Aby umożliwić zapis to wielu strumieni jednocześnie wykorzystany został wzorzec kompozyt. Dzięki temu możemy obsługiwać wiele obiektów w sposób nieodróżnialny od obsługi pojedynczego obiektu. Nie ma więc znaczenia, czy logujemy tylko do standarowego wyjścia, czy również do pliku - odbywa się to w dokładnie ten sam sposób. Do tworzenia złożonego obiektu loggera (zawierające w sobie inne logery) został wykorzystany wzorzec fabryka abstrakcyjna.

Loggery stworzone w fabryce abstrakcyjnej wykorzystują smartpointery do zarządzania obiektami

Logowanie można w bardzo elegancki sposób wyłączyć wybierając null logger jako jedyny używany. W ten sposób dokonuje się zmiany w kodzie w tylko jednym miejscu (moment tworzenia loggera) i można w prosty sposób wrócić do poprzedniego sposobu logowania lub wybrać całkiem inny.

Metody w klasie Logger mogą być ze sobą w wygodny sposób łączone, bo zwracają obiekt z którego korzystają. Możliwe jest więc tworzenie łancucha wywołań dodając kolejne ustandaryzowane elementy składowe do loga. Przykłądowo poziom logowania (info, warn, error) wybierany jest podczas logowania za pomocą użycia odpowiedniej metody: withInfo(), withWarn(), withError(). Niezależnie od tego można użyć metody dodającej timestamp - możliwe jest więc wisanie wiadomości z automatycznie dodanym poziomem logu i czasem wiadomości za pomocą następującego wywołania: logger->withTimestamp().withInfo()<<"example info log"<

Przykładowe użycie loggerów o różnej złożoności.

TAGS: c++, wzorce projektowe,