EN | CS | Přihlásit | Registrovat

Logger

Tato třída usnadňuje logování vlastních zpráv. Zatím jediná implementace třídy ILogger, FileLogger, umožňuje zapisovat do souborů s konfigurovatelným automaticky generovaným názvem podle času, filtraci priorit jednotlivých zpráv, podporuje formátovaní zpráv ve stylu sprintf a obsahuje podporu základních událostí.

Verze 0.5.1
Download logger-0.5.1.zip
Autoři Jan Smitka, Martin Pecka

Instalace

Doplněk nepotřebuje žádnou instalaci, stačí jen rozbalit soubory ke své aplikaci a naincludovat. V případě použítí RobotLoaderu se tak navíc děje automaticky.

Příklad použití

Použití třídy je skutečně jednoduché. Pro začátek je vhodné si třídu zaregistrovat jako službu v config.ini:

service.Nette-Logger.factory = FileLogger::createFileLogger

A nyní můžeme službu směle používat! Začneme něčím jednoduchým, například pozdravením světa.

$logger = Environment::getService('Nette\Logger');
$logger->logMessage('Hello, World!');

Nyní po zavolání těchto řádků by se měl ve složce logs objevit nový soubor se jménem log-YYYY-DD-MM.log. Soubor má v názvu datum, takže soubory logů mohou být pěkně přehledné. A nebo v nich může být také pořádný svinčík – podle toho, jak si Logger nastavíme. To si ukážeme později, teď ještě budeme chvilku zkoumat, co jsme vlastně vytvořili. Pokud si soubor otevřeme, uvidíme v něm něco takového:

Fri, 08 Jan 2010 19:39:13 +0100 WARNING: Hello, World!

Na začátku řádku je datum, poté priorita zprávy a na konci samotná zpráva. Výchozí priorita ve výchozím nastavení je WARNING. Pokud bychom chtěli zalogovat zprávu s jinou prioritou, můžeme jí uvést ve volání metody logMessage() před samotnou zprávou:

$logger->logMessage(ILogger::INFO, 'Hello, World!');
Fri, 08 Jan 2010 19:52:14 +0100 INFO: Hello, World!

Jednotlivé priority si vybíráme z konstant v interface ILogger. Priority jsou ve stylu syslogu, takže je opravdu z čeho vybírat a některé pravděpodobně v Nette příliš nevyužijete – chyby Vám laděnka loguje sama. Jejich seznam je následující (popisky jsou převzaté z manuálových stránek syslog(2)):

Hodnota Konstanta Popiska
0 EMERGENCY, EMERG system is unusable
1 ALERT action must be taken immediately
2 CRITICAL, CRIT critical conditions
3 ERROR, ERR error conditions
4 WARNING warning conditions
5 NOTICE normal but significant condition
6 INFO informational
7 DEBUG debug-level messages

Další hezkou věcičkou, kterou naše funkce logMessage umí, je formátování zprávy ve stylu printf/sprintf. Lehce tak můžeme používat následující zápisy:

$logger->logMessage('Hello, World of %s %s!', 'Nette', Framework::VERSION);
$logger->logMessage(ILogger::INFO, 'Hello, World of %s %s!', 'Nette', Framework::VERSION);
$logger->logMessage(ILogger::DEBUG, 'Processed %s chunks of code.', 42);
Fri, 08 Jan 2010 19:54:45 +0100 WARNING: Hello, World of Nette 0.9.3-dev!
Fri, 08 Jan 2010 19:54:45 +0100 INFO: Hello, World of Nette 0.9.3-dev!
Fri, 08 Jan 2010 19:54:45 +0100 DEBUG: Processed 42 chunks of code.

Více informací o formátování naleznete v PHP dokumentaci k funkci sprintf.

Povšimněte si, že nastavení priority je vždy jako první parametr. V případě výchozí priority je jako první parametr uvedena přímo zpráva – funkce se o pořadí parametrů rozhoduje podle toho, zda je první parametr číslo, nebo řetězec. Proto je důležité při vynechání priority zajistit, aby byla zpráva skutečně řetězec:

$logger->logMessage((string) 42);

Konfigurace

Jak jsem již několikrát naznačoval, Logger je poměrně dobře nastavitelný. Uvedu zde vždy jen název property, třída však samozřejmě obsahuje ke každé i příslušný getter a setter.

logDir určuje složku, ve které budou vytvářeny soubory s logy. Je možné zde používat proměnné prostředí Nette, hodnota se zpracovává pomocí Nette\Environ­ment::expand Výchozí hodnotou je "%logDir%", tedy složka APP_DIR . '/log/'.

filenameMask označuje masku názvu souborů s logy. Právě touto maskou lze přizpůsobit název nově vytvořených souborů a období, které v těchto souborech bude zahrnuto. Lze použít identifikátory pro funkci strftime(). Pokud si nastavíme masku "log-%Y-%m.log", budou se nové soubory vytvářet pro každý měsíc. Pokud přitvrdíme a nastavíme si "log-%Y-%m-%d-%H.log", budou se nové logy vytvářet každou hodinu. Tuto volbu nastavujte prosím pečlivě, protože zásadně ovlivňuje množství vytvářených souborů a jejich velikost. Výchozí hodnota je "log-%Y-%m-%d.log".

granularity velmi úzce souvisí s filenameMask. Umožňuje zhustit (pozor, opravdu jen zhustit!) záznamy z delšího časového úseku do jednoho souboru. Nejlepší bude vysvětlit tuto volbu na příkladu. Pokud si nastavíme masku názvu souboru na "log-%Y-%m-%d-%H-%M-%S.log", vytvářel by se nám při výchozím nastavení soubor pro každou sekundu. To není zrovna ideální, protože bychom pro jeden den mohli mít až 86 400 malinkatých souborů logů! A právě zde přichází na scénu nastavení granularity. Udává počet sekund, kolik se má zhustit do jednoho souboru. Pokud si nastavíme 3600, bude se nám vytvářet soubor vždy pro každou hodinu, ačkoliv se vždy bude jmenovat přesně podle masky. Pokud si nastavíme 86400, bude to jeden den, pokud nastavíme 604800, bude to jeden týden. Jako název souboru se vždy použije datum ze začátku intervalu, takže při nastavení jedné hodiny a masky "log-%Y-%m-%d-%H-%M-%S.log" se nám vždy soubor bude jmenovat log-%Y-%m-%d-%H-00-00.log, ačkoliv první zápis klidně může přijít klidně i pět minut před začátkem nové hodiny. Výchozím nastavením je 0, tzn. tato funkce se nepoužívá.

dateFormat umožňuje přenastavit formát data používaný uvnitř souboru. Je možné použít veškeré identifikátory funkce date(). Výchozí hodnotou je "r", tedy datum ve formátu RFC 2822.

defaultLogLevel udává výchozí prioritu zprávy. Jak již bylo zmíněno dříve, výchozím nastavením je ILogger::WARNING.

minimumLogLevel nastavuje minimální prioritu zprávy, která se zapíše. Pokud si nastavíme ILogger::ERROR, projdou jen zprávy s prioritou ERROR, CRITICAL, ALERT a EMERGENCY. Výchozí nastavení je závislé na prostředí – v produkčním prostředí je to ILogger::INFO, ve vývojovém ILogger::DEBUG.

Na závěr příklad nastavení:

$logger->filenameMask = 'log-%Y-%m.log';
$logger->defaultLogLevel = ILogger::NOTICE;
$logger->minimumLogLevel = ILogger::INFO;
$logger->dateFormat = 'c';

Líbí se Vám to? Že ne? Tak s tím něco uděláme!

Konfigurace služby v config.ini

Protože jsme si na začátku Logger nastavili jako službu, bylo by fajn jí konfigurovat přímo v config.ini. To naštěstí vyžaduje podporu od továrničky na službu a světe, div se – ` FileLogger::cre­ateFileLogger` jí má. Takže výše uvedenou konfiguraci můžeme zapsat takto:

service.Nette-Logger.factory = FileLogger::createFileLogger
service.Nette-Logger.option.filenameMask = "log-%%Y-%%m.log"
service.Nette-Logger.option.defaultLogLevel = NOTICE
service.Nette-Logger.option.minimumLogLevel = INFO
service.Nette-Logger.option.dateFormat = "c"

Povšimněte si, že jako hodnoty priorit jsme napsali jen NOTICE a INFO. Klidně jsme mohli napsat přímo číselné hodnoty konstant, ale můžeme zadat jen název.

Události

Třída FileLogger podporuje pár událostí. Abych byl konkrétnější, podporuje skutečně pár, tedy dvě. Jejich názvy jsou onMessage a onLogFileCreated.

První jmenovaná, onMessage, se volá po zapsání zprávy do souboru. Callback „navěšený“ na tuto událost by mohl mít následující předpis:

function(FileLogger $logger, int $level, string $message)

Pod parametrem $message se skrývá již zpracovaná zpráva – úplně stejná jako ta, která byla zapsána do souboru. Nemusíte si tedy lámat hlavu s vlastním zpracováním pomocí sprintf. Tuto událost můžete využít například k současnému zápisu zpráv do Firebugu.

Druhá, onLogFileCreated, je o něco praktičtější. Volá se vždy před vytvořením nového souboru se záznamy a callback má následující předpis:

function(FileLogger $logger, string $fullName)

Tuto událost můžete využít například k archivaci starších souborů s logy nebo třídění souborů do složek – jako masku souboru můžete uvést relativní cestu, například %Y/%m/%d.log, požadovanou strukturu je ale potřeba si ručně vytvořit právě v této události. Toto chování se může v budoucnu ještě změnit.

Další vývoj?

Kam dál? Co s tím? Ocením jakékoliv podněty a nápady. Do některé z dalších verzí bych rád zahrnul možnost zápisu do databáze, ale moc se mi nechce řešení vázat na konkrétní databázovou strukturu. Co myslíte?


Komentáře Comments feed

maX | 26. 2. 2010, 15:24 | bug

vo funkcii parseLevel() by malo byt

Framework::fix­Namespace($log­gerInterface);

namiesto

fixNamespace($log­gerInterface);

Panda | 5. 3. 2010, 15:25 | comment

Opraveno, zaspal jsem aktualizaci Nette.

Login to submit a comment