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\Environment::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::createFileLogger` 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?




maX | 26. 2. 2010, 15:24 | bug
vo funkcii parseLevel() by malo byt
Framework::fixNamespace($loggerInterface);
namiesto
fixNamespace($loggerInterface);