Logování ve Flexu II.
Minule (Logovani ve flexu) byla popsána tvorba a princip logovacího API Flexu. Dnes navážu pokračováním v tomto tématu, konkrétně půjde o vytvoření vlastního Log Targetu. Vlastní Log Target umožňuje přizpůsobení výstupu z Logerru k obrazu svému, což je dnešním cílem.
Než bude ukázána praktická část vytvoření a práce s vlastním Log Targetem, bylo by dobré popsat princip spoluprace mezi Loggerem a Log Targetem.
Zprávy, které odesíláme pomocí metod poskytovaných Loggerem (debug(), info(), error(), …), jsou “vysílány” jako Event událost, konkrétně LogEvent. Aby Log Target tyto zprávy mohl přijímat, musí nastavit na daném Loggeru listener na událost LogEvent.LOG. Nastavení listeneru na tuto událost probíhá automaticky a odchycená událost je předána do metody logEvent( event:LogEvent ), která přijatou událost zpracuje, zformátuje a odešle na výstup.
Máme tento kód:
var logTarget:ILoggingTarget = new TraceTarget();
logTarget.includeLevel = true;
logTarget.includeDate = true;
// .....
logTarget.filters = ["my.class.foo.*"];
// samotna registrace Log Targetu do spravce loggeru
Log.addTarget( logTarget );
Při zavolání Log.addTarget() dojde k registraci zadaného Log Targetu. Metoda addTarget() zjistí, jaké kategorie zpráv bude registrovaný Log Target přijímat. Správce Loggerů projde filtry (logTarget.filters = ["mu.class.foo.*"] ) právě registrovaného Log Targetu a porovnává je s registrem kategorií Loggerů. Pokud najde Logger, který poskytuje zprávy jež Log Target chce, zavolá Log Target a předá mu informaci o nalezeném Loggeru. Log Target na předaném loggeru nastaví výše zmíněný listener na událost LogEvent.LOG. Od této chvíle Log Target přijímá zprávy odesílané daným Loggerem.
Vlastní Log Target
Flex poskytuje tyto vestavěné Log Targety:
- TraceTarget – vhodný pro základní logování, jako výstup je použita trace() konzole.
- MiniDebugTarget – umožnuje přesměrovat výstup do jiného SWF souboru, pomocí LocalConnection.
- LineFormattedTarget – základní Log Target, který nikam neposílá výstup a je často používán jako “taťka” pro vlastní loggery.
LineFormattedTarget je základním stavebním kamenem při tvorbě vlastního Log Targetu. Poskytuje základní metody, potřebné pro logování událostí. Sám o sobě vychází z třídy AbstractTarget, která definuje základní metody pro správu a příjem událostí z Loggerů. Rozdíl mezi nimi je ten, že LineFormattedTarget definuje metodu, pro základní formtováný výstup, čímž nám práci ulehčí. Kdežto AbstractTarget žádný výstup nedefinuje a je nutné si výstup formátovat osobně.
Na samotném začátku, při vytváření vlastního Log Targetu, stojíme před rozhodnutím z jaké třídy vycházet. Chceme-li pouze využít možnosti ukládání zpráv do souboru, popř. odesílání pomocí LCDS tak doporučuji vycházet z třídy LineFormattedTarget, protože pak stačí implementovat metodu internalLog(), která provádí samotný formátovaný výstup a přesměrovat na libovolný výstup.
Pokud však máme nároky vyšší, např. chci mít vlastní vlastní výstupní formát (např. datum, čas, aj.) tak již doporučuji sáhnout po metodě AbstractTarget. Bylo by možné použít i LineFormattedTarget, ale bylo by nutné implementovat nejméně dvě metody, jednu navíc z mx_internal namespace. LineFormattedTarget přijme událost do metody logEvent, zformátuje a zavolá metodu internalLog(), která provede samotné odeslání na výstup.
Ukázka použití LineFormattedTarget
Zadání problému:
Mějme AIR aplikaci a zprávy o jejím běhu chceme zaznamenávat do zadaného souboru.
Řešení:
Pro tento případ se hodí použití třídy LineFormattedTarget, protože nemáme žádné požadavky na formát zprávy a je tedy možné použít výchozí formátování.
Naše třída bude vypadat např. takto:
package
{
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import mx.core.mx_internal;
import mx.logging.targets.LineFormattedTarget;
use namespace mx_internal;
public class CustomLogger extends LineFormattedTarget
{
private var _file:File;
private var _fileStream:FileStream;
public function CustomLogger()
{
super();
// chceme logovat dotohoto souboru, musime jej tedy otevrit pro zapis
_file = new File( "C:\\flexLog\\log.txt" );
_fileStream = new FileStream();
_fileStream.open( _file, FileMode.APPEND);
}
override mx_internal function internalLog(message:String):void
{
// prijatou zpravu zapisujeme do souboru
_fileStream.writeMultiByte( message + "\n", 'iso-8859-1' );
}
}
}
Výstup v log souboru bude asi takovýto:
12/6/2008 13:58:37.293 [DEBUG] my.class.logger.Main Button click
12/6/2008 13:58:37.421 [DEBUG] my.class.logger.Main Button click
12/6/2008 13:58:47.852 [DEBUG] my.class.logger.Main Button click
Tímto jsme vytvořili vlastní Log Target, který si ponechává všechny možnosti nastavení a zároveň se nemusíme starat o formátování zprávy.
Ukázka použití AbstractTarget
Problém:
Mějme stejné zadání z předchozí ukázky, avšak tentokrát je požadavek na formát zprávy. Zprávu je nutné zapisovat ve formátu “DATE|TIME|LEVEL|CATEGORY@MESSAGE“, protože log zprávy budou následně zpracovány jiným softwarem. Zároveň je nutné, aby byl vytvořen jedinečný soubor pro každé spuštění aplikace.
Řešení:
V tomto případě bude lepší jako základ použít třídu AbstractTarget, protože máme daný přesný formát zprávy a zároveň je nutné implementovat i vlastní metodu prozápis.
Třída bude vypadat např. takto:
package
{
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import mx.logging.AbstractTarget;
import mx.logging.ILogger;
import mx.logging.LogEvent;
public class CustomLogger2 extends AbstractTarget
{
private var _file:File;
private var _fileStream:FileStream;
public function CustomLogger2()
{
super();
_file = new File( "C:\\flexLog\\log.txt" );
_fileStream = new FileStream();
_fileStream.open( _file, FileMode.APPEND);
}
/**
* metoda, ktera zpracovava prijate LogEvent udalosti
*/
override public function logEvent(event:LogEvent):void
{
var date:Date = new Date();
var message:String = "";
// vlozime datum
message += Number(date.getMonth() + 1).toString() + "." +
date.getDate().toString() + "." +
date.getFullYear().toString();
// separator
message += "|";
// vlozime cas
message += padTime(date.getHours()) + ":" +
padTime(date.getMinutes()) + ":" +
padTime(date.getSeconds()) + "." +
padTime(date.getMilliseconds(), true);
//separator
message += "|";
// vlozime uroven
message += LogEvent.getLevelString( event.level );
// separator
message += "|";
// vlozime kategorii loggeru, ktery poslal zpravu
message += ILogger( event.target ).category;
// separator
message += "@";
// vlozime obsah prijate zpravy
message += event.message;
// zapiseme zformatovanou zpravu do souboru
log( message );
}
/**
* metoda prevzata z LineFormattedTarget, upravuje zobrazeni vterin
*/
private function padTime(num:Number, millis:Boolean = false):String
{
if (millis)
{
if (num < 10)
return "00" + num.toString();
else if (num < 100)
return "0" + num.toString();
else
return num.toString();
}
else
{
return num > 9 ? num.toString() : "0" + num.toString();
}
}
/**
* metoda zajisti vytvoreni jedinecneho jmena souboru
* (pro jednoduchost neresi jakekoli konflikty, ktere by mohly
* vzniknout, pokud by se aplikace spustila 2x v ten samy cas)
*/
private static function _createLogName():String
{
var date:Date = new Date();
return 'log_' + date.getTime() + '.log';
}
/**
* metoda zapisuje zpravu do log souboru
*/
protected function log( message:String ):void
{
_fileStream.writeMultiByte( message + "\n", "iso-8859-1" );
}
}
}
Výstup v log souboru bude vypadat asi takto:
12.6.2008|14:50:51.069|DEBUG|my.class.logger.Main@Button click
12.6.2008|14:50:51.359|DEBUG|my.class.logger.Main@Button click
Vytvořili jsme tedy Log Target, ktery zaznamenavá zprávy dle zadání.
Závěr
Možností jak logovat je spousta, snažil jsem se (snad úspěšně) popsat jak využít kvalitního logovacího API, které poskutuje Flex. Log Targety nejsou omezené pouze na zápis do souboru, lze taktéž data rozesílat pomocí LCDS Messaging protokolů, popř. pomocí DataServices zapisovat do databáze. Možností je spousta a je těžké je všechny obsáhnout, ale i tak doufám, že jsem poskytl základní návod, jak správně logovat.