Logger-lista wymogów

0

Heja

Z nudów postanowiłem napisać sobie logera przy użyciu qt,chciałem tylko się upewnić,że nie zapomnę o żadnym ficzerze.Zapodam najpierw co sam uważam za potrzebne:
1.Pisanie logów do okna oraz do pliku-można ustawić czy się chce tylko okno/tylko do pliku,czy tu i tu
2.Kolor tekstu w zależności od typu komunikatu,np errory na czerwono,warningi niebiesko,debugi na zielono
3.Okno z logami powinno się dać zrobić jako "above all"

O czym zapomniałem?

1

-wybór formatu pliku np. zwykły tekstowy, csv, xml
-wybór poziomu logowania z możliwością włączenia w danej chwili logowania tylko z danego poziomu tzn. funkcja logująca nie robi nic jeżeli nie jest wybrany dany poziom (coś jak logger z javy)

0

Część zrobiona,loguje do okna,do .txt w postaci:
22-08-2011 1301 | DEBUG | .\main.cpp | 16 | 4 | default
22-08-2011 1301 | DEBUG | .\main.cpp | 18 | 4 | Always on top? true
22-08-2011 1301 | DEBUG | .\main.cpp | 20 | 4 | Always on top? false
22-08-2011 1314 | WARNING | .\main.cpp | 20 | 4 | Added warning log
22-08-2011 1315 | ERROR | .\main.cpp | 24 | 4 | Added error log
te liczby to linia w pliku i poziom logowania (0-4)

Co do .csv,to ja nie widzę użyteczności tego,może ktoś mnie uświadomi?
A co poradzilibyście apropos .xmla?Ja osobiście myślę o takim czymś

<log>
<debug>
<datetime>22-08-2011 13:54:01</datetime>
<filename>main.cpp</filename>
<fileline>16</fileline>
<loglevel>4</loglevel>
<message>some message</message>
</debug>
.....
</log>
0
  • Autorotacja po rozmiarze lub czasie z zachowaniem wybranej liczby archiwalnych plików i ewentualną ich kompresją.
1

Sorry, że odpowiadam tu, a nie w komentarzach, ale chcę dać przykład.
Co to jest autorotacja i jak to działa. Byku_guzio już opisał po krótce w komentarzu, ja tylko uściślę.
Kiedy plik logu przekroczy określony rozmiar, lub nastąpi określona godzina, zmieniasz jego nazwę doklejając ".1", po czym tworzysz i otwierasz nowy plik. Jeśli plik o nazwie zakończonej na ".1" już istnieje, zmieniasz suffiks tego istniejącego na ".2", itd. Użytkownik może określić czy rotacja następuje po wielkości pliku (np. jeśli przekroczy 10MB), czy codziennie o określonej godzinie (np. o północy). Dodatkowo może określić ile plików z suffiksami może być utworzonych (np. 10; nadmiarowe mają być usuwane).
Przykład: rozmiar pliku "server.log" przekroczył 10MB, w katalogu są już pliki archiwalne "server.log.1" oraz "server.log.2". Zmieniasz nazwy: "server.log.2"->"server.log.3", "server.log.1"->"server.log.2", zamykasz plik logu i zmieniasz jego nazwę na "server.log.1", tworzysz nowy plik "server.log" i otwierasz go do zapisu. Jeśli plików archiwalnych jest już 10, usuwasz najpierw plik "server.log.10".
Przy wykonywaniu rotacji (zmiany nazwy "server.log" na "server.log.1") możesz skompresować plik archiwalny, ale lepiej robić to w oddzielnym wątku.

Na serwerach za to odpowiada program logrotate, więc możesz podejrzeć jak on to robi. Niestety, nie zawsze userzy mogą dodać swoje pliki logów do rotowania przez proces systemowy, więc logger który rotuje i troszczy się o archiwizację jest niesamowicie przydatny. W Pythonie jest od tego odpowiednia klasa, w C musiałem sobie sam takowy wyklepać dla loggera w GObject, bo standardowy tylko pluje do pliku i nie wnika co dalej.

0

Okay,skoro mówcie iż ta autorotacja jest takim użytecznym ficzerem to ją zapodam.Jedynie co do tego zmieniania nazw wszystkich plików mam zastrzeżenia,ja wolę zrobić to tak:
1.Każę logerowi logować do pliku log.txt
2.Po spełnieniu wymogów limitu log.txt jest zamykany a tworzony jest nowy plik log2.txt i teraz logi lecą do niego
3.itd,itp

Podsumujmy jeszcze tylko,wg jakich kryteriów ma się plik logu wypełniać i być zamykany
-czasowe,tj o okreśonej godzinie
-ilością wpisów(wierszy) w pliku
-rozmiarowo,czyli jak plik logu osiągnie zadany rozmiar
-jakieś jeszcze?

A,powiedzcie koledzy czy widzicie potrzebę logowania warningów,debugów i errorów do oddzielnych plików?

0

Co do tego, lepiej zmieniać nazwy wszystkich plików, im nowszy plik tym mniejszy indeks - wyżej na listach plików. Operacja taka będzie rzadko wykonywana, więc czasem czegoś takiego nie ma się co przejmować.

Z tym logowaniem do oddzielnego pliku raczej nie widzę sensu. Ale za to fajnie by było, gdyby można definiować poziomy logowania. Np. standardowo ERROR, WARRNING, INFO i do tego możliwość dodania własnego(np. OKRUTNY_ERROR) i ustawienie mu "ważności"

0

Na chwilę obecną są poziomy 0-4,przy czym domyślny jest 4,oraz typy debug,warning itd.Działają głównie w trybie logowania okienkowego(patrz obrazek) powodując filtrację danych z tabeli.
Niestety,takie coś w trybie konsolowym czy plikowym za bardzo sensu nie ma,stąd póki co do pliku wpada wszystko jak leci.

Hmmm...można by zrobić to tak,że loger miałby funkcję w stylu setLoggingLevel(int maxlevel) która by w trybie okienkowym nastawiała odpowiednio te guziki filtrów,a w trybie plikowym/konsolowym powodowała,że jedynie logi z przedziału <0,maxLevel> były zapisywane

2
MasterBLB napisał(a)

Okay,skoro mówcie iż ta autorotacja jest takim użytecznym ficzerem to ją zapodam.Jedynie co do tego zmieniania nazw wszystkich plików mam zastrzeżenia,ja wolę zrobić to tak:
1.Każę logerowi logować do pliku log.txt
2.Po spełnieniu wymogów limitu log.txt jest zamykany a tworzony jest nowy plik log2.txt i teraz logi lecą do niego
3.itd,itp

To nie jest dobry pomysł.
Po pierwsze, jako admin chcę szybko wyświetlić zawartość pliku logu, bez zastanawiania się i dociekania który indeks jest aktualnie obowiązujący.
Po drugie, różne narzędzia analizujące logi odczytują tylko jeden plik, o bazowej nazwie.
Po trzecie, jak w ten sposób chcesz załatwić sprawę przedawnionych archiwów? Przy numeracji odwrotnej są pliki od ".1" do ".maksimum". Przy Twojej numeracji po kilku miesiącach będą pliki z milionowymi liczbami w nazwie, a są usługi które działają przez lata bez restartu.

MasterBLB napisał(a)

Podsumujmy jeszcze tylko,wg jakich kryteriów ma się plik logu wypełniać i być zamykany
-czasowe,tj o okreśonej godzinie

Tak. W ten sposób można sobie rozdzielić pliki logu według dni.

MasterBLB napisał(a)

-ilością wpisów(wierszy) w pliku

To nie ma zastosowania. Rotowanie po rozmiarze robi się w celu zmniejszenia ryzyka zapchania quoty. Np. ustala się 110MB na logi - 10 plików archiwum po 10MB. Jesli ograniczasz po ilości linii, to nigdy nie wiadomo jakiej długości te linie będą. Wystarczy popatrzeć na logi serwera WWW żeby stwierdzić, że przy próbach ataku te linie mogą być baaaardzo długie.
Kolejny problem to otwarcie już istniejącego pliku logu (np. po restarcie usługi). Musiałbyś przeliczyć wszystkie linie jakie w nim są żeby odpowiednio zainicjować licznik zapisanych już linii. To może być bardzo mało wydajne przy dużych plikach.

MasterBLB napisał(a)

-rozmiarowo,czyli jak plik logu osiągnie zadany rozmiar

Nie "osiągnie". Kiedy "przekroczy". Nie będziesz przecież łamał linii w połowie żeby zmieścić się w rozmiarze pliku. Po zapisaniu każdej linii sprawdzasz czy rozmiar (sumuj długości wpisów, nie odpalaj stat() na pliku za każdym razem) przekroczył zadany i jeśli tak, wykonaj rotację.

MasterBLB napisał(a)

-jakieś jeszcze?

Nic mi już nie przychodzi do głowy.

MasterBLB napisał(a)

A,powiedzcie koledzy czy widzicie potrzebę logowania warningów,debugów i errorów do oddzielnych plików?

Ja nie widze takiej potrzeby. Od tego jest grep ;)

P.S. W razie czego mogę udostępnić moją implementację rotującego file loggera dla GObjectowego systemu rejestracji zdarzeń. Niestety, rotuje tylko po rozmiarze i nie kompresuje (nie było mi to potrzebne).

0

Zacząłbym od przejrzenia listy ficzerów istniejącego softu:
http://log4qt.sourceforge.net/

U mnie (w moim rozwiązaniu) było najważniejsze:

  • rezygnacja z poziomu logowania na rzecz maski logowania (np. ERROR + INFO)
  • nazwy plików unikalne dla każdego uruchomienia (może niezarządzalne automatycznie, ale nie o to mi chodziło)
  • możliwość podpięcia różnych wymyślnych urządzeń (np. osobnego pliku tylko dla TRACE, poza tym standard: konsola, okienko, plik). urządzenie definiuje formatowanie, obsługiwane typy komunikatów oraz miejsce przechowywania danych
  • łatwość użytkowania - singleton:
MojLog::addInfo("Teraz jestem tutaj");
  • auto-odcinanie pliku po przekroczeniu progu rozmiaru (przed zapisem linii - jeśli log jest za duży to zmieniana jest jego nazwa)
0
vpiotr napisał(a)
  • auto-odcinanie pliku po przekroczeniu progu rozmiaru (przed zapisem linii - jeśli log jest za duży to zmieniana jest jego nazwa)

Ojjjj.... Takie rzeczy powinieneś robić po zapisaniu linii. Zmiana nazwy może się nie udać i/lub może spowodować awarię programu, do której jedyny ślad może się znajdować w tej jednej, opuszczonej linii logu. Złota zasada jest taka: najpierw zapisz, potem mieszaj w plikach.

0

-maska już jest,patrz załączony plik logger.png
-nazwy unikalne co uruchomienie rozważę.Jeśli będą,to powstaną ze złożenia userFileName+_+currentDate
-co do tych urządzeń,to poproszę się wypowiedzieć bardziej szczegółowo,mniej więcej tak jak zrobił to Kumashiro
-łatwość użycia jest jeszcze większa niż babranie się singletonami:

#define LOGDEBUG_Full(text,loggerId,level) BLBLogger::logPrint(text,loggerId,__LINE__,__FILE__,level);
#define LOGDEBUG_Level(text,level) LOGDEBUG_Full(text,"default",level)
#define LOGDEBUG_Logger(text,loggerId) LOGDEBUG_Full(text,loggerId,MAXLEVEL)
#define LOGDEBUG(text) LOGDEBUG_Full(text,"default",MAXLEVEL)

moim priorytetem podczas tworzenia było umożliwić użytkownikom proste jak konstrukcja cepa funkcje/makra do logowania bez konieczności chrzanienia się z jakimiś log managerami i podobnymi.Oczywiście,managerem też można się bawić jak jest potrzeba,jednak w zupełności wystarczy napisać LOGDEBUG("tekst debuga") i reszta zostanie załatwiona automatycznie.
-rotacja logu po rozmiarze aktualnie w implementacji wg wytycznych Kumashiro.

0

Makra to masakra. Robisz to w C++ czy w C?
Makra z parametrami mają inne reguły ewaluacji argumentów niż funkcje, w związku z tym są zwodnicze.
Poczytaj dlaczego "macros are evil":
http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.4

Co do urządzeń, to logger główny ma możliwość podpięcia od 1 do n urządzeń (u mnie są specyficznymi logerami).
Każde urządzenie ma to co opisałem, czyli komunikat

MojLog::addInfo("Tekst") 

może iść do iluś urządzeń - w zależności od tego jakie mają maski komunikatów (INFO, WARNING, ERROR, DEBUG, TRACE).
Dzięki temu mogę logować osobno błędy a osobno resztę - wedle uznania.
Poza tym dzięki temu jedna klasa może logować zawsze w ten sam sposób, mimo że raz działa w konsoli a raz w aplikacji okienkowej.
Urządzenia podpinam na starcie aplikacji i/lub okienka logującego.

Aktualnie mam dostępne 4:

  • konsola
  • plik
  • okienko wxWidgets
  • komunikat DebugInfo (Windows - są odpowiednie aplikacje do przechwytywania tego, m.in. MS VS)

Dodatkowo przy logowaniu błędu warto zapisać unikalny kod błędu w logerze - dzięki temu będzie to sformatowane w standardowy sposób (to zamiast stosowania makra LINE, które jest słabe jeśli masz co innego uruchomione niż dostępne jako źródło, albo nie wiesz jak wyglądało źródło dla danego EXE-ca).

0

Ja wiem,jak działają parametry makr.Niestety,innaczej nie idzie a to z uwagi na LINE właśnie które nie mogę dać jako parametr domyślny funkcji bo wyjdą bzdury.Ponadto chcę jak najbardziej uprościć zapis użycia,nie chcę pisać kijWieCoLogger::debug(LINE,FILE,"tekst messaga") tylko po prostu LOGDEBUG("tekst messaga") a reszta ma się zrobić sama.I już.

Klasa logująca w mnie już jest 1 i loguje do okna widocznego z załącznika na poprzedniej stronie,pliku lub konsoli.Ponadto można łączyć logowanie do konsoli lub okna z plikiem.Co do masek to mam debug,warning i error,o info zapomniałem.Acha,powiedz proszę do czego używa się tej trace?Bo ja z logów nie kojarzę takiej

0

Makra to masakra. Robisz to w C++ czy w C?

Trochę głupie pytanie...

Makra z parametrami mają inne reguły ewaluacji argumentów niż funkcje, w związku z tym są zwodnicze.

Ale można je łatwo wyłączyć zmieniając opcje kompilacji. I, jak pisze MasterBLB, rozwijanie makr LINE, FILE etc.

Btw. Logger doskonały: http://nlog-project.org/ (niestety tylko dla .net)

0
MasterBLB napisał(a)

Ja wiem,jak działają parametry makr.Niestety,innaczej nie idzie a to z uwagi na LINE właśnie które nie mogę dać jako parametr domyślny funkcji bo wyjdą bzdury.Ponadto chcę jak najbardziej uprościć zapis użycia,nie chcę pisać kijWieCoLogger::debug(LINE,FILE,"tekst messaga") tylko po prostu LOGDEBUG("tekst messaga") a reszta ma się zrobić sama.I już.

Tu masz rację - jeśli Ci bardzo zależy na LINE to makro będzie niezbędne. Ale to i tak zło w czystej postaci (w kontekście C++) ;)
Poza tym mój logger nie ma 3 składników wywołania - używam globalnego namespace.

MasterBLB napisał(a)

Klasa logująca w mnie już jest 1 i loguje do okna widocznego z załącznika na poprzedniej stronie,pliku lub konsoli.Ponadto można łączyć logowanie do konsoli lub okna z plikiem.Co do masek to mam debug,warning i error,o info zapomniałem.Acha,powiedz proszę do czego używa się tej trace?Bo ja z logów nie kojarzę takiej

TRACE się wiąże z urządzeniami. W pewnym momencie chciałem mieć osobną klasę komunikatów którą mogę włączyć lub wyłączyć parametrem wejściowym programu, a komunikaty te były dość liczne.
W związku z tym musiałem zrobić osobne urządzenie - które będzie bardzo szybkie i które będzie przechwytywać tylko te komunikaty - bez zwalniania aplikacji. To taki DEBUG tylko dla wersji "release".
Miałem do tego użyć OutputDebugString, ale w sumie nie pamiętam czy było wystarczająco szybkie.

Proponuję jednak pomyśleć nad OutputDebugString jeśli to Windows. Zaleta - informacje zostają po wywaleniu się aplikacji, można filtrować, stopować itd.
Jego komunikaty przechwytuje m.in.:
http://technet.microsoft.com/en-us/sysinternals/bb896647

0

Hmmm mam pewną zagwozdkę natury koncepcyjnej,której nie mogę wymyśleć satysfakcjonującego rozwiązania :/
Załóżmy,że nastawiłem logerowi wrzucanie wpisów do pliku Logs\log.txt i włączyłem mu autorotację.ALE w tymże folderze znajduje się też jakiś zupełnie nie powiązany z powyższym plikiem log_dupa.txt.
Teraz używając QDir::entryList(QStringList("log*.txt")....) chcę pobrać wszystkie wpisy dotyczące podanej nazwy pliku,niestety,ten log_dupa.txt też się na to log*.txt załapuje :/

Użyłem zatem listy z dokładnymi nazwami log.txt,log1.txt,log2.txt aż do log<tutaj maxLogFiles-1>.txt itd.Jest lepiej,jednak wciąż istnieje ryzyko kolizji nazw na przykład w sytuacji używania 2 różnych obiektów logerów którym niebacznie ktoś podał tą samą nazwę pliku logu.

Jak zatem rozwiązać taką sytuację?Mi przychodzi na myśl,aby logi z automatu wrzucały się do podfolderu o nazwie takiej samej,jak QString z ID logera.A mój system grawantuje,że owe IDki są unikalne.
Co wy na to?

0

Ja to robię tak (da się jeszcze podkręcić):

    for ( rotsuff = logger->max_archives; rotsuff >= 1; rotsuff-- ) {
        g_snprintf(combined_old, PATH_MAX_LENGTH + 1, "%s.%d", logger->logfile, rotsuff);
        if ( !g_file_test(combined_old, G_FILE_TEST_EXISTS) )
            continue;

        if ( rotsuff == logger->max_archives ) {
            if ( g_unlink(combined_old) == -1 )
                return FALSE;
            continue;
        };

        g_snprintf(combined_new, PATH_MAX_LENGTH, "%s.%d", logger->logfile, rotsuff + 1);
        if ( g_file_test(combined_new, G_FILE_TEST_EXISTS) && (g_unlink(combined_new) == -1) )
            return FALSE;

        g_rename(combined_old, combined_new);
    };

    g_snprintf(combined_new, PATH_MAX_LENGTH, "%s.1", logger->logfile);
    g_rename(logger->logfile, combined_new);

    if ( file_logger_open(logger, &lerror) == FALSE ) {
        g_propagate_error(error, lerror);
        return FALSE;
    };

Jeśli w katalogu jest jakiś plik z innej instancji programu, o konfliktowej nazwie, to jest traktowany jako należący do aktualnej. Nie wyobrażam sobie jak dwa loggery z tej samej instancji mogłyby jednocześnie pisać do pliku o tej samej nazwie. To byłoby bez sensu.

0

Hmm kolejna sprawa koncepcyjnej natury.

Napisałem funkcję QList<Logger*> getAllLoggers().Działa świetnie,jest jednak pewne ale-jak po jej wywołaniu pojawi się nowy logger(a może),wówczas lista ta będzie zawierała nieprawdziwy stan systemu.Stąd do Was Bracia(Siostry też ;) ) pytanie-jakiego zachowania oczekujecie od takiej funkcjonalności:
1.Skoro wywołałem/-am getAllLoggers,to na liście mają być wszystkie loggery i już.Jeśli pojawi się nowy,to lista ma się zupdatować i być zawsze aktualna.
2.Lista ma zawierać tylko stan loggerów na chwilę wywołania funkcji gettera.

1 użytkowników online, w tym zalogowanych: 0, gości: 1