Moduł Logger

Nagłówek: Logger.hpp
Elementy modułu: Logger Module

Wstęp

Logger to moduł do logowania, czyli zapisywania krótkich komunikatów tekstowych do pliku, na konsolę i w dowolne inne miejsca - typowo przeznaczony do wyprowadzania informacji o zdarzeniach zachodzących w programie, błędach itd.

Właściwości:

Podstawy

Logger to system logowania. Jest tylko jeden. Inicjalizuje się go funkcją common::CreateLogger, a finalizuje - common::DestroyLogger. Między tymi wywołaniami można otrzymać go funkcją common::GetLogger.

Log to obiekt klasy dziedziczącej z interfejsu common::ILog. Reprezentuje miejsce, do którego mogą trafiać logowane komunikaty i odpowiada za ich zapisywanie do tego miejsca. Może to być na przykład plik tekstowy w jakimś formacie, realizowana z użyciem jakiegoś API konsola albo cokolwiek.

Prawidłowa sekwencja wywołań wygląda jak w przykładzie:

// 1. Utworzenie loggera
//    Utworzenie logów
common::CreateLogger(false);
common::Logger & Logger = common::GetLogger();
common::TextFileLog *Log = new common::TextFileLog("Log.txt",
  common::FILE_MODE_NORMAL, common::EOL_CRLF);

// 2. Konfiguracja logów
//    Konfiguracja loggera
//    Zarejestrowanie logów
Logger.AddLogMapping(0xFFFFFFFF, Log);

// 3. Używanie w czasie pracy programu
Logger.Log(1, "Komunikat 1");
Logger.Log(1, "Komunikat 2");
Logger.Log(1, "Komunikat 3");

// 4. Usunięcie loggera
common::DestroyLogger();

// 5. Usunięcie logów
delete Log;

Bardzo ważne jest, aby logi usuwać dopiero po usunięciu loggera. Ważne też, by po rozpoczęciu używania loggera nie dokonywać już żadnej konfiguracji loggera ani logów, nie tworzyć nowych logów itd.

Typy, maski, mapowanie

Komunikat to krótka wiadomość tekstowa, która podlega zalogowaniu. Komunikat składa się z treści (string) oraz typu (uint4). Treść powinna być w kodowaniu Windows-1250/Unicode (jak w całym projekcie). Może zawierać końce wiersza (w dowolnym formacie) - logi mają obowiązek prawidłowo obsługiwać komunikaty wielowierszowe - ale jest to raczej niezalecane, bo powinna być krótka.

Typ komunikatu to maska bitowa. Można jej nadać dowolne znaczenie, np.:

Komunikaty logujemy za pomocą metody common::Logger::Log podając kolejno typ i treść. Logger wysyła go do zera, jednego lub wielu logów zależnie od mapowania. Podczas rejestrowania logów w loggerze podajemy maskę bitową, która będzie łączona operatorem & z typem komunikatu i jeśli wynik będzie niezerowy, komunikat trafi do danego loga. W ten sposób można mapować tylko wybrane typy komunikatów na wybrane logi. Log zarejestrowany z maską 0xFFFFFFFF otrzyma wszystkie komunikaty o niezerowym typie.

Przykład mapowania typów komunikatów na logi:

Logger.AddLogMapping(0xFFFFFFFF, Log1);
Logger.AddLogMapping(1, Log2);

Logger.Log(1, "Komunikat 1");
Logger.Log(2, "Komunikat 2");
Logger.Log(3, "Komunikat 3");

W tym przykładzie do Log1 trafią wszystkie komunikaty, a do Log2 tylko te, których typ ma ustawiony najmłodszy bit, czyli komunikaty 1 i 3.

Nie należy rejestrować logów w loggerze więcej niż raz.

Prefiksy

Prefiks to przedrostek dodawany do treści każdego logowanego komunikatu. Jest składany indywidualnie dla każdego loga na podstawie ustawionego dla niego formatu prefiksu, który jest dowolnym łańcuchem, a specjalne wartości są w nim zastępowane przez:

Format prefiksu można ustawić dla loga metodą common::ILog::SetPrefixFormat(). Można też ustawić na raz format prefiksu dla wszystkich logów zarejestrowanych w loggerze metodą common::Logger::SetPrefixFormat(). Ustawianie formatu prefiksu jest częścią procesu konfiguracji.

Własne informacje prefiksu to trzy łańcuchy, które można zmieniać w czasie działania programu (ich zmiana jest częścią procesu używania loggera) i które mogą być dołączane do prefiksu. Są wspólne dla całego loggera. Do ich zmiany służy metoda common::Logger::SetCustomPrefixInfo(). Można je wykorzystywać np. do wskazania numeru bieżącej klatki gry czy innych tym podobnych rzeczy.

Prefiks typu to dodatkowy prefiks dołączany za prefiksem głównym do treści logowanego komunikatu. Jest ustalonym na stałe łańcuchem. Każdy log ma własne mapowanie masek typów na prefiksy typu. Można to mapowanie wykonać metodą common::ILog::AddTypePrefixMapping(). Metoda common::Logger::AddTypePrefixMapping() loggera dokonuje go jednocześnie dla wszystkich zarejestrowanych w nim logów. Jest to część procesu konfiguracji.

Przykład:

Log1->SetPrefixFormat("[%D %T %1] ");
Log1->AddTypePrefixMapping(1, "(!) ");
Logger.SetCustomPrefixInfo(0, "Frame:123");

Logger.Log(1, "Komunikat 1");
Logger.Log(2, "Komunikat 2");
Logger.Log(3, "Komunikat 3");

Da w efekcie mniej więcej takie wyjście:

[2006-08-18 21:52:28 Frame:123] (!) Komunikat 1
[2006-08-18 21:52:28 Frame:123] Komunikat 2
[2006-08-18 21:52:28 Frame:123] (!) Komunikat 3

W przypadku kiedy typ danego komunikatu pasuje do wielu mapowań na prefiks, wykorzystane zostanie pierwsze z nich. Dlatego mapowania należy dodawać w kolejności od najbardziej szczegółowych do najbardziej ogólnych. Na przykład jeśli chcemy dodać mapowanie które dotyczy "wszystkich pozostałych" komunikatów, dodajemy je z maską 0xFFFFFFFF na samym końcu.

Tryby pracy

Logger może pracować w dwóch trybach:

  1. BEZ KOLEJKI Kiedy podamy podczas tworzenia loggera jako parametr common::CreateLogger() wartość false, logger loguje komunikaty natychmiast po ich otrzymaniu.
  2. Z KOLEJKĄ Kiedy podamy podczas tworzenia loggera jako parametr common::CreateLogger() wartość true, logger tworzy osobny wątek zajmujący się logowaniem. Logowane komunikaty trafiają do specjalnej kolejki, a wątek pobiera je i loguje w swoim tempie.

Tryb z kolejką jest szybszy, ale tryb bez kolejki jest bardziej niezawodny w wypadku awarii programu. W trybie z kolejką finalizacja loggera może potrwać dłuższą chwilę, jeśli wymaga poczekania na zalogowanie zalegających w kolejce komunikatów.

Kolejność wywołań logowania komunikatów i zmiany własnych informacji prefiksu jest respektowana także w trybie z kolejką.

Loggery plikowe można otwierać w trzech trybach:

Tryb z kolejką w połączeniu z common::FILE_MODE_NORMAL jest najszybszy.

Tryb bez kolejki w połączeniu z common::FILE_MODE_REOPEN jest najpewniejszy - daje gwarancję, że nawet w przypadku nagłego wysypania się programu wszystko, co było wcześniej logowane trafiło do logów.

Bezpieczeństwo wątkowe

Tworzenie logów, ich konfiguracja, konfiguracja loggera - nie są bezpieczne wątkowo. Dlatego należy ich dokonywać przed rozpoczęciem używania loggera.

Logowanie i zmiana własnych informacji prefiksu są bezpieczne wątkowo. Można ich dokonywać z wielu wątków na raz.

Tworzenie własnych logów

Aby utworzyć własny log, należy napisać własną klasę dziedziczącą z intefejsu common::ILog. Jej jedynym obowiązkiem jest implementacja metody common::ILog::OnLog(), która będzie miała za zadanie zalogować komunikat. Otrzymuje ona już zredagowane do łańcuchów prefiksy (główny i prefiks typu) oraz treść. Przekazywany także typ może wykorzystać do realizacji własnego mapowania, np. na kolor. Jej obowiązkiem jest dostosowanie treści wszystkich otrzymanych łańchów do kodowania znaków i końców wiersza obowiązującego w miejscu docelowym. Wejściowe kodowanie znaków to Windows-1250/Unicode, a końców wiersza - nieokreślone.

Klasa ta nie musi być bezpieczna wątkowo - o bezpieczeństwo wątkowe dba sam logger. Metody tej klasy mogą być wywoływane z różnych wątków, ale nigdy nie będą wywoływane równocześnie.


Generated on Wed Dec 16 20:44:52 2009 for CommonLib by  doxygen 1.6.1