Deklarovat logger jako static?


Často se sám sebe na začátku každého projektu zeptám – budu deklarovat loggery jako statické? Jinak řečeno, budou začátky tříd vypadat takto:

public class Foo {
private static final Log log = LogFactory.getLog(Foo.class);
....
}
A nebo takto:
public class Foo {
private final Log log = LogFactory.getLog(Foo.class);
....
}

Oba přístupy mají svá pro i proti.

prvním případě (statickém) je pro menší využití paměti a program bude o něco rychlejší – není třeba pro každou instanci vytvářet logger. Také nebývá problém při serializaci, kdy načtená třída bez problémů funguje. Ovšem do problému se můžeme dostat, pakliže máme logovací knihovny v J2EE serveru umístěny ve sdíleném prostoru (classloaderu) a na serveru je více instancí stejné aplikace nebo knihovny, které logují. Pak nebude možné od sebe odlišit jednotlivé instance, takže kdybyste chtěli mít logy z jedné aplikace v jiném souboru, než logy z druhé, tak to nebude možné. Situaci musíte řešit na aplikační úrovni.

Druhý případ (nestatický – instanční) tímto problémem netrpí a navíc se s ním lépe pracuje v IoC. Ale pozor – problém s classloaderem je vyřešen pouze v knihovných logback a log4j – nejnovějších verzích. Pokud používáte jinou logovací knihovnu (jul), máte smůlu a můžete použít static. Ovšem přístup má i nevýhody: situaci je třeba explicitně řešit při deserializaci (readResolve), a tento přístup je také pomalejší a bere více procesorového času.

Takže rada zní: pokud jste si opravdu jisti, ale tím myslím na sto procent jisti, že aplikace nebude provozována ve více instancích na stejném aplikačním serveru, tak přesto používejte instanční (pomalejší) přístup. No – teď si dělám srandu. Pochopitelně, že můžete použít statický přístup. Ten instanční použijte jen tehdy, kdy máte jako logující implementaci novější verze knihoven log4j nebo logback (to je nástupce log4j).

Druhá rada je pak prostá: snažte se o jednotný přístup.

A konečně třetí rada: využijte svoje programovací prostředí naplno. Většina IDE nabízí možnost vytvoření šablony pro novou třídu, což lze využít pro nadefinování loggeru i jiným věcem.
Mimochodem, když už se rozhodnete pro instance, pak lze zápis „zkrášlit“ voláním metody getClass() – tím pádem nemusíte logger upravovat, když děláte copy&paste. A ruku na srdce – to děláte často, že?

public class Foo {
private final Log log = LogFactory.getLog(getClass());
....
}

U všech případů platí, že je vhodné deklarovat logger jako privátní. Pokud bychom totiž umožnili potomkům přístup na logger, mohlo by se stát, že by se v logu objevovaly zprávy potomků pod identifikátorem (názvem třídy) předka. Obecně platí jedno pravidlo – nikdy nepoužívejte logger předka. Za každých okolností musí mít každá třída svůj logger, leda byste měli vystavěný nějaký aparát, který by dodával správné identifikátory.

Pozn.: Původně jsem klíčové slovo private chybně neuváděl. Je potřeba jej ale z výše uvedených důvodů uvést. Díky za upozornění v diskusi.




Uvedené příklady jsou pro knihovnu-fasádu commons-logging, v ostatních případech (fasáda slf4j, knihovny log4j, logback, juli a další) bude kód vypadat trošičku jinak, a to jen v některých případech.



A poslední dodatek se týká klíčového slova final. Můžete jej uvádět vždy, když nebudete logger modifikovat (drtivá většina případů), může to pomoct kompilátoru/virtuálnímu stroji. Případ, kdy není vhodné slovo final uvést, již byl uveden výše – například při deserializaci a implementaci metody readResolve().

Další zdroje



Tento zápisek vznikl převodem z mého starého blogu. Ne všechny texty byly takto převedeny, kompletní archiv již není k dispozici.
23 April 2010 | oldblog
twitter.com linkedin.com
google.com/+ facebook.com
flickr.com youtube.com