Newsgroup: it.comp.java
Titolo: stackTrace

Come e' possibile ottenere lo stack trace in un determinato punto?
Mi spiego meglio. Per ragioni di debug vorrei stampare sullo stderr,
in determinati punti dell'esecuzione, dei messaggi seguiti dallo stack
trace. Per il momento creo un'eccezione fittizia e poi uso il metodo
printStackTrace(). Esiste un metodo piu' ortodosso per fare cio'?
Grazie
Ciao
Franco

sicuramente devi lanciare un eccezione e traccarla in modo tale che tu possa 
lanciare il printStacKTrace()



Piu' che un'eccezione (immagino intendessi un oggetto della classe
Exception o qualche sua derivata) sarebbe piu' opportuno costruire un
oggetto della classe Throwable, da cui deriva anche Exception e che
implementa la gestione dello stack trace.
Se poi ti disturba creare ogni volta un nuovo oggetto Throwable, e'
sufficiente crearne uno solo (memorizzandone il reference da qualche parte
in un campo statico) e usare il metodo fillInStackTrace(). 
Ad esempio:
Throwable t = new Throwable();
t.printStackTrace(); 
//o t.getStackTrace()se vuoi manipolare lo stack trace da programma
// aggiorna lo stack trace
t.fillInStackTrace();
t.printStackTrace();
-- 
Lucio Benfante - Java User Group Padova
Brainbench MVP for Java 1
http://www.brainbench.com
MyTranscript: 
http://www.brainbench.com/transcript.jsp?pid=167064

Grazie a tutti. Ho fatto cosi' (che ne pensate)
public class MyTools  {
 private static Throwable throwableForStackTrace = null;
 public static void stampaStackTrace(String messaggio) {
  System.err.println(messaggio);
  stampaStackTrace();
 }
 public static void stampaStackTrace() {
  if (throwableForStackTrace == null)  {
   throwableForStackTrace = new Throwable();
  } else {
   throwableForStackTrace.fillInStackTrace();
  }
  throwableForStackTrace.printStackTrace();
 }
}
Ciao
Franco


Thread.dumpStack();
Marco

 }
A me non piace molto per il semplice motivo che cosi' "sporchi" lo stack
trace con le chiamate ai tuoi metodi stampaStackTrace(). Dato che immagino
serva principalmente a scopo di debug forse non e' forse cosi'
importante...ma non mi piace lo stesso. :-)
Inoltre in questo caso mi sembra inutile il meccanismo che hai usato per
istanziare l'oggetto Throwable (controllo di nullita' dell'attributo): la
creazione di uno di questi oggetti non e' cosi' dispendiosa da doverla
rinviare fino a quando non e' assolutamente necessario. Inoltre in questo
modo rallenti i metodi stampaStackTrace (poco, ma inutilmente). Molto
meglio una inizializzazione statica, che elimina anche qualche problema di
concorrenza in inizializzazione.
Se intendi usare questa classe in un applicazione multithread,
ovviamente non funziona: devi sincronizzare i tuoi metodi.
Dato che comprende solo membri statici potresti anche impedire che
possano essere create inutilmente istanze della tua classe MyTools,
definendo un costruttore privato.
Provo a migliorare un po' il tuo codice:
public class StackTracer {
    private static Throwable throwable = new Throwable();
    private StackTracer() {}
 
    public static synchronized void printStackTrace(String messaggio) {
	System.err.println(messaggio);
	printStackTrace();
    }
    public static synchronized void printStackTrace() {
	printStackTrace(System.err);
    }
 
    public static synchronized void printStackTrace(java.io.PrintStream stream) {
	throwable.fillInStackTrace();
	// remove the first StackTracer-orginated StackTraceElements
	StackTraceElement[] elements = throwable.getStackTrace();
	int i = 0;
	while (elements[i].getClassName().equals(StackTracer.class.getName())) {
	    i++;
	}
	if (i > 0) {
	    StackTraceElement[] reduced = new StackTraceElement[elements.length - i];
	    System.arraycopy(elements, i, reduced, 0, reduced.length);
	    throwable.setStackTrace(reduced);
	}
	throwable.printStackTrace(stream);
    }
}
...ovviamente raffinamenti sono ancora possibili.  


 Thread.dumpStack();
Si', e' una possibilita'. Pero' questo metodo crea ogni volta un nuovo
oggetto Exception e chiama su di esso printStackTrace() (almeno
nell'implementazione del Sun SDK).
Puo' andare bene a scopo di debug (come e' scritto nella documentazione),
ma se si vogliono degli usi un po' piu' raffinati (ad esempio per dei
messaggi nel log) meglio riscriverselo.


 sicuramente devi lanciare un eccezione
Non e' necessario lanciare l'eccezione.
Basta crearla.


 while (elements[i].getClassName().equals(StackTracer.class.getName())) {
si fa riferimento in modo esplicito al nome della classe che contiene il
metodo.
Se, in futuro, volessi cambiare il nome della classe, dovrei ricordarmi di
cambiare il codice. Esiste un modo per recuperare il nome della classe in un
metodo statico, cioe' un this.class statico?
Ciao
Franco



Per quel che ne so io, non e' possibile.


 Per quel che ne so io, non e' possibile.
Pero' in questo caso e' abbastanza facile riscrivere il metodo in modo da
svincolarsi dal litteral della classe:
public static synchronized void printStackTrace(java.io.PrintStream stream) {
  throwable.fillInStackTrace();
  // remove the first StackTracer-orginated StackTraceElements
  StackTraceElement[] elements = throwable.getStackTrace();
  String thisClass = elements[0].getClassName();
  int i = 1;
  while (elements[i].getClassName().equals(thisClass)) {
      i++;
  }
  if (i > 0) {
      StackTraceElement[] reduced = new StackTraceElement[elements.length - i];
      System.arraycopy(elements, i, reduced, 0, reduced.length);
      throwable.setStackTrace(reduced);
  }
  throwable.printStackTrace(stream);
}
...potrebbe anche essere un buon metodo per sapere in ogni momento in
quale classe ci si trova...se qualcuno non ne conosce uno migliore...


 quale classe ci si trova...se qualcuno non ne conosce uno migliore...
...anzi il metodo potrebbe essere ulteriormente migliorato togliendo il
test su i, che sara' comunque sempre vero (scusate tutte queste
autorisposte):
public static synchronized void printStackTrace(java.io.PrintStream stream) {
  throwable.fillInStackTrace();
  // remove the first StackTracer-orginated StackTraceElements
  StackTraceElement[] elements = throwable.getStackTrace();
  String thisClass = elements[0].getClassName();
  int i = 1;
  while (elements[i].getClassName().equals(thisClass)) {
    i++;
  }
  StackTraceElement[] reduced = new StackTraceElement[elements.length - i];
  System.arraycopy(elements, i, reduced, 0, reduced.length);
  throwable.setStackTrace(reduced);
  throwable.printStackTrace(stream);
}
-- 


Ognuno ha le sue particolari idee...
Io avrei percio' fatto invece cosi':
public class MyTools  {
 private static Throwable throwableForStackTrace = new Throwable();
 public static void stampaStackTrace(String messaggio) {
  System.err.println(messaggio);
  throwableForStackTrace.fillInStackTrace();
  throwableForStackTrace.printStackTrace();


 test su i, che sara' comunque sempre vero
Attenzione che getStackTrace() puo' tornare un array di 0 elementi!
Leggendo 
http://makeashorterlink.com/?Z26A216E4
  Some virtual machines may, under some circumstances,
  omit one or more stack frames from the stack trace.
  In the extreme case, a virtual machine that has no
  stack trace information concerning this throwable
  is permitted to return a zero-length array from this method.
Quindi prima di accedere incondizionatamente allo 0-esimo
elemento io farei un bel check ! [ almemo questo e' quello
che io fatto in progetti Java che usano questa tecnica ]


 
Hai ragione, non avevo dato importanza a quella frase. A qualcuno e' mai
capitata una JVM che si comporti in questa maniera?
