2015-05-27 7 views
5

Supponiamo che io stia gestendo FooException e BarException. Supponiamo che siano entrambe eccezioni non controllate.Come ottenere una traccia di stack correttamente concatenata per le eccezioni generate durante la gestione di altre eccezioni?

Quello che voglio vedere nella stacktrace è:

com.bar.BarException: Bar Message 
    at com.baz.BazCode(BazCode.java:123) 
    ... 
Caused by: com.foo.FooException: Foo Message 
    at com.baz.BazCode(BazCode.java:321) 
    .... 
Caused by: ... 

Tuttavia, per impostazione predefinita, tutti i record di FooException sarà cancellato dal stacktrace. Per esempio:

// In a class written by me 
/** 
    * ... 
    * @throws FooException if foo happens 
    * @throws BarException if bar happens 
    */ 
public void upperFrame() { 
    try { 
     foo.doSomething(); 
    } catch (FooException foo) { 
     bar.doSomethingElse(); 
    } 
} 

// In class Bar (not written by me) 
public void doSomethingElse() { 
    if (someConditionWhichHappensToBeTrueInThisScenario()) { 
     throw new BarException("Hello Bar World"); // At this point, FooException gets erased from the stack trace 
    } 
} 

Se BarException ha un costruttore (message, cause) poi mi può seguire una specie piuttosto grezzo di processo di "manuale di clonazione" per raggiungere il mio obiettivo:

try { 
    foo.doSomething(); 
} catch (FooException foo) { 
    try { 
     bar.doSomethingElse(); 
    } catch (BarException bar) { 
     BarException bar2 = new BarException(bar.getMessage(), foo); 
     bar2.setStackTrace(bar.getStackTrace()); 
     throw bar2; 
    } 
} 

Tuttavia, se BarException non ha tale un costruttore (ad esempio ClassCastException) allora io sono ridotto a fare le cose in questo modo:

try { 
    foo.doSomething(); 
} catch (FooException foo) { 
    try { 
     bar.doSomethingElse(); 
    } catch (BarException bar) { 
     RuntimeException e = new RuntimeException("com.bar.BarException: " + bar.getMessage(), foo); 
     e.setStackTrace(bar.getStackTrace()); 
     throw e; 
    } 
} 

questo è essere pericoloso causa e ha il tipo sbagliato e quindi potrebbe non essere gestito correttamente da frame superiori.

Esiste un modo "best practice" per gestire questa situazione?

+0

La stacktrace di 'FooException' è persa durante la creazione di una' BarException' con una causa Foo? Oppure potresti fare qualcosa come 'bar.getCause(). GetStackTrace()'? –

+0

@KaspervandenBerg Ho modificato la mia domanda per mostrare come si perde 'FooException'. Non è correlato a nessuna creazione di un'eccezione con una causa, è un'eccezione generata dal metodo 'doSomethingElse'. – Kidburla

risposta

4

Una soluzione è quella di utilizzare il metodo Throwable#initCause(Throwable):

bar.initCause(foo); 
+0

Buon punto (non ho notato questo metodo poiché ho cercato solo un metodo 'setCause') .... a condizione che' BarException' non definisca già una causa. Tuttavia, non ho ancora trovato alcuna classe di eccezione (almeno all'interno della specifica java) che definisce una causa senza un costruttore '(message, cause)'. – Kidburla

1

Finché si passa l'eccezione originale a quella nuova come argomento, si crea la "causato da" catena e la traccia dello stack è conservato . I tuoi casi d'uso sembrano un po 'strani. Per me, l'eccezione durante il recupero o la gestione dell'errore con più di qualche registrazione è un altro errore e non realmente "causato da" l'altro errore. Vorrei solo registrare il "foo" e lanciare la "barra".

In alcuni casi, immagino che la tua strada abbia senso. Per fare ciò, potresti passare "foo" come doSomethingElse (foo) e lanciare una nuova BarException (foo) quando qualcosa va storto maneggiando questo. Praticamente tutte le eccezioni standard supportano questo costruttore, e se è necessario crearne uno basta creare un costruttore che lo deleghi a questi.

Personalmente non li userei come sembra. Li uso per lanciare un diverso tipo di eccezione rispetto a quello che ho catturato, se voglio un tipo diverso per qualche motivo. Ad esempio, per tradurre in eccezioni di un tipo specifico alla mia applicazione, o tradurre selezionato in non selezionato nei casi in cui ciò ha senso. In questi casi è comunque opportuno mantenere l'eccezione originale e la traccia completa "causata da .."

+0

"Vorrei solo loggare il foo e lanciare la barra": registrare le eccezioni e quindi scartarle non è una buona pratica. Cosa accadrebbe se un'altra libreria dipendesse dal tuo codice e stesse usando un logger non standard (come la registrazione JMS)?"Quasi tutte le eccezioni standard supportano questo costruttore" non lo fanno, "ClassCastException" è solo un esempio – Kidburla

+0

"Personalmente non le userei come tu sembri, io le uso per lanciare un tipo diverso di eccezione a quello Ho capito "- cosa ti dà l'impressione che questo non è quello che sto cercando di fare? Eccezioni come 'ClassCastException' possono ancora verificarsi quando si tenta di lanciare un diverso tipo di eccezione a quello che ho catturato – Kidburla

+0

" Per fare questo, si potrebbe passare il foo come doSomethingElse (foo) "- Come ho detto nella mia domanda, doSomethingElse non è in una classe scritta da me. E non supporta il passaggio di un'eccezione come argomento. – Kidburla