2009-04-07 12 views
5

Mi piacerebbe creare una routine che faccia qualche registrazione, compia alcune altre azioni, e poi lancia un'eccezione. Mi piacerebbe che questa routine venisse chiamata da molti luoghi diversi. Tuttavia, la creazione di eccezioni in questa routine significa che avranno questa routine nella loro traccia dello stack. Preferirei che la traccia dello stack non riportasse questa routine di utilità. C'è un modo per farlo senza creare l'eccezione nel chiamante e passarlo alla routine di utilità?Come si butta un'eccezione dall'ambito del chiamante?

public static void die(String message) throws MyException { 
    log(message); 
    ... 
    throw new MyException(); 
} 

Per i programmatori che sono Perl/Java bilingue: come faccio carpe in Java?

+0

L'idioma sembra strano da Javaland –

+0

Stai utilizzando lo stacktrace per scopi diversi da quelli di debug? Non riesco davvero a capire perché questo potrebbe importare. –

+0

questa è una cattiva idea – ykaganovich

risposta

10

È possibile impostare la traccia dello stack delle eccezioni si vuole buttare:

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class CarpTest { 
    public static void main(String[] args) { 
     new CarpTest().run(); 
    } 

    public void run() { 
     methodThatCarps(); 
    } 

    private void methodThatCarps() { 
     carp("Message"); 
    } 

    private void carp(String message) { 
     RuntimeException e = new RuntimeException(message); 
     e.fillInStackTrace(); 
     List<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace())); 
     stack.remove(0); 
     e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()])); 
     throw e; 
    } 
} 

Questo stamperà il seguente stacktrace in fase di esecuzione:

Exception in thread "main" java.lang.RuntimeException: Message 
    at CarpTest.methodThatCarps(CarpTest.java:18) 
    at CarpTest.run(CarpTest.java:14) 
    at CarpTest.main(CarpTest.java:10) 

Nota che, come si desidera che il metodo del "carpe "non appare nello stacktrace. Tuttavia la manipolazione degli stacktraces va fatta solo con grande cura.

+0

Ecco fatto; grazie! Penso che potrei anche essere in grado di combinarlo con l'approccio della sottoclassi Exception e di sovrascrivere fillInStackTrace() e inserire il codice per rimuovere il primo StackTraceElement. – skiphoppy

2

Non c'è modo di rimuovere la funzione di lancio dalla traccia dello stack. L'intero scopo della traccia dello stack è di registrare il percorso dell'eccezione in modo che l'attivazione di una funzione possa annullare lo scopo.

L'unico modo per modificarlo è se si restituisce l'eccezione anziché lanciarla. Ma questo ti costringe a dipendere dal chiamante per sapere di lanciare l'eccezione.

throw die("someReason).fillInStackTrace(); 

funzione Modificato

public static Exception die(String message) { 
    log(message); 
    ... 
    return new MyException(); 
} 

EDIT

Aggiunto il fillInStackTrace() chiamata al fine di garantire la pila è riportato al punto del lancio.

http://java.sun.com/j2se/1.3/docs/api/java/lang/Throwable.html#Throwable()

+0

Penso che la traccia dello stack mostri dove è stata creata l'eccezione, non da dove è stata lanciata, quindi non credo che nemmeno restituirlo lo porterebbe a termine. Potrei sbagliarmi. Controllerò.:) – skiphoppy

+0

@skiphoppy, sono abbastanza sicuro che sia dal punto di lancio. In caso contrario, la JVM segnalerebbe false tracce di stack nei casi in cui l'eccezione è stata passata attorno a un bit. – JaredPar

+0

Chiamate di Throwable() fillInStackTrace() - è fatto al momento della creazione –

0

Non può fare ... ho provato a fare qualcosa di simile un po 'indietro (stavo cercando di catturare l'analisi dello stack per registrare le chiamate di metodo prima che esistesse AOP).

La traccia di stack viene compilata quando viene creata l'eccezione e viene eseguita in modo nativo. Per la cosa su cui stavo lavorando, ho finito per leggere la traccia dello stack e guardare il secondo elemento, ma questo non ti aiuterebbe qui ...

0

Potresti considerare il tuo metodo ricevere un Logger come parametro per il metodo. Ciò consentirebbe di controllare l'output di registrazione basato sulla classe chiamante.

Vorrei sconsigliare che la tua eccezione escluda questa parte della traccia dello stack. Quando parti e alcune nuove persone mantengono il tuo codice, non apprezzeranno questa gestione degli errori non standard.

0

Butta la traccia di stack solo per poterla analizzare? In tal caso, è possibile chiamare il metodo getStackTrace() sull'eccezione che restituisce StackTraceElement []. Lì puoi filtrare gli elementi che non vuoi (ad esempio il metodo "die").

1

Mmm .. è possibile creare una sottoclasse di eccezioni e sovrascrivere tutti i metodi al suo interno e avvolgere l'eccezione originale. Internamente, genera una nuova traccia dello stack usando il metodo getStackTrace() dall'eccezione avvolta. Non ho guardato l'origine di Exception, ma potresti anche non dover scavalcare molti metodi.

4

Se si desidera utilizzare un eccezione per il controllo del flusso e ciò che accade in seguito, un buon consiglio per l'override del metodo fillInStackTrace()

public Throwable fillInStackTrace() { 
    return this; 
} 

Come risultato avrete un'eccezione senza la stacktrace e con un overhead ridotto (il completamento della traccia dello stack richiede tempo).

1

Forse dovresti considerare di affrontare il problema da una direzione diversa. Invece di modificare la traccia dello stack, perché non utilizzare il metodo del generatore di eccezioni (die nell'esempio) restituire l'eccezione piuttosto che lanciarla? Quindi la tua chiamata è throw die();.

Ad esempio:

// revised die() method: 
public static MyException die(String message){ 
    log(message); 
    //... 
    return new MyException(); 
} 


// calling code: 
throw die("a-whoopsie daisy!"); 

Ora, scontato, throw die() potrebbe sembrare un po 'non-estetica, così si potrebbe rinominare die() a newException() o qualcosa del genere. Tuttavia, il requisito che il metodo di elaborazione delle eccezioni non viene visualizzato nello stack trace è soddisfatto (die() (o newException()) restituisce prima che venga generata l'eccezione e quindi non fa parte dello stack da tracciare.

Modifica: Il mio male. Ho passato così tanto tempo a lavorare con C# che ho dimenticato che in Java le tracce dello stack di eccezioni sono generate durante l'istanziazione, dove in C# /. NET le tracce dello stack di eccezioni vengono generate al momento del lancio.

Quindi questo trucco funziona in C#, ma non in Java.

+0

Non penso che funzionerà. Lo stacktrace viene popolato quando viene creata un'istanza dell'eccezione, non ciò che viene generato. –

+0

Beh, è ​​bello sapere che funzionerebbe in C#. Potrei averne bisogno un giorno. :) Ho votato a 0 fino a -1. :) – skiphoppy

+0

chiama fillInStackTrace prima di lanciare – ordnungswidrig

1

Sulla base di ciò che ordnungswidrig ha detto sull'impostazione della traccia dello stack e su ciò che sconosciuto (google) ha detto riguardo a overriding fillInStackTrace(), ho creato un CarpException che fa esattamente ciò che voglio. Nota che ho scoperto che dovevo rimuovere quattro frame di traccia stack invece di uno solo, poiché stavo raccogliendo frame sia da Throwable che da Exception.

public class CarpException extends Exception { 
    @Override 
    public Throwable fillInStackTrace() { 
    super.fillInStackTrace(); 
    StackTraceElement[] origStackTrace = getStackTrace(); 
    StackTraceElement[] newStackTrace = new StackTraceElement[origStackTrace.length - 4]; 
    System.arraycopy(origStackTrace, 4, newStackTrace, 0, origStackTrace.length - 4); 
    setStackTrace(newStackTrace); 
    return this; 
    } 
} 
Problemi correlati