2011. szeptember 11., vasárnap

Gyárlátogatás: Logging

A logging megintcsak egy érdekes és gubancos kis szál a java történetben. Nagyon szerény elvárásaim ezek lennének:
  • Legfontosabb: Ha a logging el van csűrve, azért az alkalmazás még csak menjen!
  • Lehessen konfigurálni a logging szintet
  • Lehessen konfigurálni azt, hogy hova menjen a log - többnyire file, valami archiválós megoldással vagy méret vagy idő bontásban, fejlesztőknek stdout, had kapjuk az arcunkba :) Vannak egzotikusabb megoldások is
  • Az nagggyon jóóó, ha nem kell újraindítani az appot a logging konfigurálása után. Ugyanakkor belátom, hogy egy évben csak egyszer van karácsony.
Mostanra sikerült odáig is eljutni, hogy a Logger-t kb így deklaráljuk:
private final static Logger logger = ...kakukk...

Ezt várja el a checkstyle, a PMD és a satöbbi. Nem tűnik nagy dolognak, de pl az avalon framework implementációi még bedobálták volna a komponenseknek egy setLogger metóduson keresztül. Persze nem kötelező így használni :-D

Nekem igazából nagyon szimpatikus ez a private final static dolog. A logger legyen a kód számára elérhető mindig. Ne írhassa felül semmi és ne lehessen null se. Amennyiben sikerült az osztályt betölteni, alighanem loggered is van. Innentől már nagy baj ezzel nem lehet.

Mit kellene logolni?

Hát kicsit izlés-codestandard-hitvita tárgya az, hogy mit mikor és hogyan kell logolni. Én nem hiszek a policy-kben, azt hiszem a józan paraszti ész valami ilyesmit diktálna, ha kellene a parasztoknak bármi ilyesmivel foglalkoznia: logolni azt érdemes, ami érdekelni fog hibakeresés közben, és persze csak olyan szinten, amilyen szinten keresni fogsz. Minden más csak a helyet fogja zabálni a szerveren. A logging frameworkok között enyhe eltérés van a szintek tekintetében. Azt hiszem van, ahol nincs trace szint, hanem debugnál kezdődik. Nekem a nagyon sok szint használata nem tetszik, nem tűnik praktikusnak.

Ja igen, bonyolult és anyázásig menő viták tárgya az is, hogy ha esetleg valami stringet hozunk létre a loggernek, amit logolunk, akkor csekkoljuk-e körülötte a debug szintet egy if-fel. Erre gondolok:

if(logger.isDebugEnabled()) {
  logger.debug("Baromira fajt osszerakni ezt a stringet: "+getTerribleString());
}

Szerintem ezzel a legtöbb alkalmazásban nem lehet mérhető teljesítményjavulást elérni. Inkáb csak a kód komplexitásának növekedését. Illetve akinek tényleg már csak ez segített, annak had gratuláljak a kiválló termékhez.

System.[out|err].println()

Na ez a logging elötti logging volt. Már rég megástuk a sírját, már rég ott térdel a szélén, de még mindig nem húztuk meg a ravaszt. Nagyon sokan használnak még ma is ilyesmit. Ezt azt hiszem korrekt tudatlanságnak és/vagy igénytelenségnek nevezni.
Na jó, szóval ez trehányság, de akkor mit kellene használni helyette? A válasz erre kb 2-3 évente változik akár még egyetlen fejlesztő esetében is.

log4j.properties - köszi Bocinak a megosztásért :)
Log4j

Ha jól emlékszem a log4j volt az első komolyan elterjedt logging api.
A log4j-ről tudni érdemes dolgok 99 százalékát tudod, ha tudsz egy log4j.properties file-t írni. Na, ez az a dolog, ahol kiválló alkalmak adódnak a dolgok elcseszésére: illik nem belecsomagolni a log4j.properties file-t a csomagodba. Ezt azért a mainstream opensource libraryk tudják, de enterprise körökben frekventált elcseszés tárgya. Ez még mehetne a multkori konfigurációs kérdéskör boncolgatásához.

Ennyi: Igen, kell a log4j.properties file - pontosabban jó ha van. De ez a konfiguráció része, nem csomagolhatjuk a kódhoz.

Alternatív...

A log4j sikere után sok alternatív logging api jött. Az alternatívok közül legtöbbet a commons-logging projectet lehetett látni. Ha jól tudom a sikerének oka az, hogy az apache projectekben kötelező (volt?). Ez elég sovány ok :-) Mindenesetre a commons-logging már végülis facade jellegű. Azaz az API mögött végülis valamibe logol bele. Valamelyik másik logging frameworkbe.

java.util.logging

A java.util.logging így visszatekintve rá egy szomorú példa a JCP töketlenségére. Aki ragaszkodik a szabványokhoz, az biztosan használja is, de más okot nem is tudnék rá. Az az egy nagyon tetszett benne, hogy a jconsole-n keresztül lehet babrálni a logging szinteket. Még talán akkor veszi az ember használatba ha valamilyen oknál fogva nem akar semmilyen dependency-t. Ultra-minimalista cuccokhoz, aminek nem kell semmi, csak egy JRE, és megy.

slf4j

Simple Logging Facade for Java. Itt tart a történet. Ma kb mindenki slf4j-vel logol, én is. Egyszer valami podcastot hallgattam ennek az előnyeiről, és ilyeneket említett a csákó, hogy végre Loggernek hívják a loggert, nem Log-nak, mint a commons-logging. Hát ez elég sovány ok azért, ebben azt hiszem egyetértünk :)
A slf4j-nél tipikus elba, amikor valakinek az APIja nem csak a slf4j-api-n dependel, hanem berántja valamelyik logging rendszerhez kötő részét (pl tipikusan slf4j-log4j13-at amikor te már berángattad a slefn-log4j12-t). Ugyanis nem, ezek a dolgok nem férnek meg békésen egymással a classpathon. Természetesen ez egy gyógyítható betegség (pl maven dependency exclude), de néhány bosszús pillanatot sikeresen össze tud hozni.

...és végül: log4j :-)

Akárhogy is, de az összes konfigurációban, amit eddig láttam a facadeken keresztül a végén a logging üzeneteidet a log4j kapja meg, és file-be nyomja be. Az üzemeltető csapatok a log4.properties-t keresik, ha debugolni kell valamit. Ennyi.
Szóval ebből a szempontból a történet marha hosszúra nyúlt, de a lényege nagyon keveset változott.

A java elött

Nem tudom mennyire rendelkeznek a java elődei logging apival, amennyire emlékszem amit a CGI scriptekben stderr-re írtunk, na az volt a log, a stdout-ra, na az meg a kimenet. Nem túl kultúrált. A C kultúrám megintcsak félelmetesen alacsony, C/C++-ban úgy logoltunk, hogy precompiler direktívaként megadtuk a log szintet. Nyilván újrakonfignál újra kell fordítani, ezt nem nevezném valami baráti megoldásnak.

Update: átneveztem ezt a postot, hogy kicsit kiemeljem azt hogy nem arról szól, hogyan kell logolni, hanem arról, hogy miket láttam tényleg. Többi ilyen.