Hlavní navigace

Síťování v Javě: Logování

Martin Majer

Dnes mírně odbočíme od zaměření seriálu. Podíváme se na téma, které se síťováním úzce souvisí - logování. Ukážeme si, jaké jsou možnosti vytváření logů. Od výpisu na konzoli, až po java.util.logging API.

Výpis na konzoli

Výstup na konzoli lze považovat za nejprimitivnější formu vytváření logu. V případě potřeby prostě zavoláme System.out.prin­tln() a odešleme zprávu na standardní výstupní proud. Můžeme použít i standardní chybový proud, System.err.

Tato forma logování se pochopitelně pro větší aplikace vůbec nehodí. Proud System.out lze sice přesměrovat do souboru, ovšem pořád existují mnohem vhodnější způsoby.

Zápis do souboru pomocí PrintWriter

Zápis do souboru pomocí java.io je na téměř stejné úrovni jako výpis na konzoli. Přesto si však ukážeme, jak ho naprogramovat:

import java.io.*;

public class FileLogger {
    public static final String FILE = "log.txt";
    public static void main(String[] args) throws IOException {
        PrintWriter logger = null;
        try {
            //otevřít proud pro zápis na konec souboru
            logger = new PrintWriter(new FileOutputStream(FILE, true));
            //zkusíme si logovat
            logger.println("FileLogger test");
        }
        catch(IOException e) {}
        finally {
            if(logger != null) logger.close();
        }
    }
} 

Důležitý je druhý parametr v konstruktoru FileOutputStream. Pokud bychom ho neuvedli nebo nastavili na false, soubor by se přepsal a přišli bychom o celý předcházející log.

Logovací API

Ve drtivé většině případů je nejvhodnější volbou API specializované přímo na logování. V praxi se nejčastěji používají dvě – jednoduché logging API z balíku java.util.logging, které je přímo součástí JDK, a složitější log4j od Apache Software Foundation. Samozřejmě vám nic nebrání napsat si vlastní, ovšem použitím již hotových si ušetříte mnoho práce.

V tomto článku si popíšeme pouze API z balíku java.util.logging. Vybral jsem ho především kvůli jeho jednoduchosti. Log4j je naopak tak rozsáhlé, že by vystačilo na celý další seriál.

java.util.logging API

Toto jednoduché logovací API je umístěno v přímo knihovnách J2SE, a to od verze 1.4. Nebudu se rozepisovat o teorii a jeho architektuře, pojďme se rovnou podívat, jak ho použít.

Logger

Základem je třída Logger z balíku java.util.logging. Její instanci získáme pomocí statického továrního konstruktoru getLogger(). Jako parametr uvedeme jméno loggeru. Pokud již nějaký logger se stejným jménem existuje, getLogger() nebude vytvářet nový, ale vrátí ten stávající.

Logger logger = Logger.getLogger("test"); 

Handler

Dalším krokem je nastavení handleru. Handler je objekt, který má na starosti zpracování příchozích zpráv od loggeru, např. jejich uložení do souboru.

Handlery se dělí do dvou základních skupin – MemoryHandlery a StreamHandlery. MemoryHandler nejdříve ukládá zprávy do vyrovnávací paměti. Pak je přeposílá jinému handleru. StreamHandlery zase zapisují log do nějakého výstupního proudu. Patří mezi ně ConsoleHandler, FileHandler a SocketHandler.

K přidání nového handleru slouží metoda loggeru addHandler(). Odebrání provádíme metodou removeHandler() a getHandlers() vrátí pole všech handlerů příslušného loggeru.

Handler handler = new FileHandler("log.txt", true);
logger.addHandler(handler); 

Tímto kódem vytvoříme nový handler pro zápis do souboru (FileHandler). První parametr jeho konstruktoru určuje jméno souboru s logem, podle druhého handler pozná, jestli má soubor přepsat nebo pokračovat na jeho konci.

Jakmile dokončíme práci s handlerem, musíme ho pochopitelně uzavřít metodou close().

Formatter

Formattery mají na starosti formátování příchozích zpráv od loggeru. V balíku java.util.logging jsou pouze dva – SimpleFormatter a XMLFormatter. Jak zprávy formátují, je zcela zřejmé z jejich názvů.

handler.setFormatter(new SimpleFormatter()); 

Je možné, že vám ani jeden ze dvou formatterů nebude vyhovovat. V tomto případě není problém vytvořit si vlastní odvozením z abstraktní třídy Formatter. Informace o jejím API jsou uvedené v dokumentaci.

Logování

Logování probíhá pomocí instance třídy Logger a několika jejich metod:

  • entering(String sourceClass, String sourceMethod) – Zaznamená vstup do metody.
  • exiting(String sourceClass, String sourceMethod) – Informuje o opuštění metody.
  • fine/finer/fi­nest(String msg) – Odešle do logu zprávy typu fine, finer nebo finest (nejméně důležité).
  • log(Level level, String msg) – Pošle do logu zprávu se specifikovanou úrovní závažnosti.
  • severe(String msg) – Pošle do logu zprávu typu severe. Tato úroveň je nejzávažnější, většinou se jedná o kritické chyby.
  • throwing(String sourceClass, String sourceMethod, Throwable thrown) – vloží záznam o vyvolání nějaké výjimky.
  • warning(String msg) – Pošle varování.

Metody entering(), exiting() a log() jsou ještě několikrát přetížené. Více informací o nich se dozvíte v dokumentaci.

Příklad logování

import java.util.logging.*;

public class LoggingTest {
    public static void main(String[] args) throws java.io.IOException {
        Logger logger = Logger.getLogger("test"); //vytvoříme logger s názvem test
        Handler handler = new FileHandler("log.txt", true); //vytvoříme handler pro zápis na konec souboru log.txt
        handler.setFormatter(new SimpleFormatter()); //nastavíme jednoduché formátování (výchozí je XML)
        logger.addHandler(handler); //přidáme handler

        //logujeme
        logger.log(Level.INFO, "zprava");
        logger.log(Level.SEVERE, "chyba");
        logger.finest("v pohodě");

        handler.close(); //uzavřeme handler
    }
} 

Když příklad spustíte, zjistíte že zpráva „v pohodě“ typu finest se do souboru nezapsala. Tyto nevýznamné zprávy jsou totiž loggerem ve výchozí konfiguraci ignorovány. Pokud o ně ale nechcete přijít, nakonfigurujte logger příkazem logger.setLevel(Le­vel.FINEST);

Takto vypadá log s úrovní Level.FINEST:

20.5.2006 19:00:25 LoggingTest main
INFO: zprava
20.5.2006 19:00:25 LoggingTest main
SEVERE: chyba
20.5.2006 19:00:25 LoggingTest main
FINEST: v pohodě 

A takto by vypadal log, pokud bychom nastavili XMLFormatter:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
  <date>2006-05-22T14:42:24</date>

  <millis>1148301744258</millis>
  <sequence>0</sequence>
  <logger>test</logger>
  <level>INFO</level>

  <class>LoggingTest</class>
  <method>main</method>
  <thread>10</thread>
  <message>zprava</message>

</record>
<record>
  <date>2006-05-22T14:42:24</date>
  <millis>1148301744426</millis>
  <sequence>1</sequence>

  <logger>test</logger>
  <level>SEVERE</level>
  <class>LoggingTest</class>
  <method>main</method>

  <thread>10</thread>
  <message>chyba</message>
</record>
</log> 

Závěr

Dnes jsme si řekli o možnostech logování v Javě a ukázali jsme si, jak používat logovací API obsažené v balíku java.util.logging. Příště se zase vrátíme k síťování – naučíme se používat UDP protokol.

Našli jste v článku chybu?