sobota, marca 01, 2008

Sztuczki tropiciela błędów ;-)

Zwykle tropiąc błąd w kodzie najpierw wymyślamy gdzie problem może być, później zaczynamy go śledzić. Jest to rozwiązanie skuteczne i prawie zawsze działa.
Jednak gdy mowa o aplikacji która pracuje wielowątkowo [a takich jest coraz więcej] nie zawsze jest to podejście najlepsze.
Wtedy przydaje się logowanie.

A w logowaniu, szczególnie gdy tropimy błąd przydaje się taki oto banalny kawałek kodu:

Throwable tr = new Throwable();
tr.printStackTrace();

Niby nic, a cieszy ;-) Wiemy już kto wywołał nasz kawałek kodu.
Czasem przydaje się też wędrówka po stack trace'ie:

public static void logStackTrace() {  
Throwable tr = new Throwable();  
StackTraceElement[] elements = tr.getStackTrace();  
String str = "";  
// zaczynamy od 1 żeby nie było widać wołania tej metody ;-)  
for (int idx=1; idx<elements.length; idx++) {  
str+=elements[idx]+" ";  
}  
System.out.println(str);  
}


Czasem potrzebujemy też wiedzieć jakie wątki odwołują się do naszego kawałka kodu, wtedy używamy równie znanego:

Thread.currentThread();


Możemy go wypisać:

System.out.println(Thread.currentThread();


Albo użyć jako klucza w mapie:

// Używamy Hashtable'a bo jest bezpieczniejsza w przypadku wielu wątków
Map<Thread,Throwable> map = new Hashtable<Thread,Throwable>();
[....]
map.put(Thread.currentThread(), new Throwable());


Wyobraźmy sobie sytuację, że otwieramy połączenie i zamykamy je, ale nie chcemy mając jedno otwarte połączenie z danego wątku otwierać drugiego i kolejnych. W wytropieniu takiego czegoś pomóc nam może:

// Używamy Hashtable'a bo jest bezpieczniejsza w przypadku wielu wątków  
public static Map<Thread,Throwable> map = new Hashtable<Thread,Throwable>();  
  
public void openSomething() {  
Throwable tr = new Throwable();  
// używamy get, bo nie ufam contains, choć tutaj by świetnie zadziałało ;-)  
if (map.get(Thread.currentThread())!=null) {  
System.out.println("PROBLEM!!!!!");  
tr.printStackTrace();  
}  
map.put(Thread.currentThread(), new Throwable());  
[...]  
}  
  
public void closeSomething() {  
map.remove(Thread.currentThread());  
[....]  
}


Jak widać jest to wszystko proste, a na dodatek skuteczne.
Zwykle do logowania nie używamy też System.out a czegoś bardziej sexy ;-) ale to już inna bajka.

Na koniec jeszcze przykład jak można logować w trakcie tropienia błędu nieobsłużone wyjątki:

Runnable r = new Runnable() {  
@Override  
public void run() {  
while (true) {  
Object o = null;  
try {  
o.wait();  
} catch (InterruptedException ie) {  
ie.printStackTrace();  
}  
}  
}  
};  
Thread t = new Thread(r);  
t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {  
@Override  
public void uncaughtException(Thread t, Throwable e) {  
System.out.println(t);  
e.printStackTrace();  
}  
});  
t.start();


Oczywiście takie ustrojstwa mają sens w kodzie w trakcie developmentu, a nie na produkcji, choć to zależy chyba tylko od naszego wyboru :-)

[edit 01/03/2008 11:34: zmieniłem HashMap'e w Hashtable'a, bo walnąłem babola ;-) To Hashtable jest bezpieczne wątkowo, nie HashMap'a]


Podobne postybeta
Sztuczki tropiciela błędów - breakpoint na sterydach ;-)
IE suxx ;-)
Przepływ sterowany danymi - A takie Java'owe coś ;-)
Potfór ;-) czyli generator z yield w Java'ie
Sztuczki tropiciela błędów - breakpoint na sterydach 2 ;-)

3 komentarze:

  1. Ciekawe, ciekawe... :) Dodam więc swoje 0.03f... Zamiast new Throwable()... można też użyć Thread.dumpStack() lub od wersji 5.0 Thread.getStackTrace(). Zawsze to mniej pisania ;) Czasami dobrze też wypisywać debugi poprzez System.err.*, żeby nie kolidowało z normalnymi wynikami pracy programu..

    "Używamy HashMap'y bo jest bezpieczniejsza w przypadku wielu wątków"

    Przecież to Hashtable jest synchronizowane ?

    OdpowiedzUsuń
  2. Z tą HashMap'ą to rzeczywiście jest tak jak piszesz :-) HashMap'a jest niebezpieczna, a Hashtable'jest bezpieczna. Jedyne co mnie tłumaczy to późna godzina :-)
    Już poprawiam :-)
    Rzeczywiście te dumpStack() i getStackTrace() krótsze w zapisie :-)

    OdpowiedzUsuń
  3. I tak najlepsza bylaby ConcurrentHashMap-a ;-)

    OdpowiedzUsuń