Introdução: por que stack trace não é apenas "texto vermelho"?
Imagine: você inicia seu serviço Java, tudo funciona como um relógio. De repente — bum! — o usuário envia uma captura de tela com um erro, e nos logs só há um seco "Exception occurred". Familiar? Se você já lutou contra bugs em produção, sabe: stack trace é seu principal detetive, que conta quem é o culpado, onde ele se escondeu e como pegá-lo. Mas o que fazer se você precisar não apenas exibir o erro no console, mas salvá-lo em uma string para enviar a um sistema de monitoramento, banco de dados ou simplesmente analisar depois?
A pergunta "How can I convert a stack trace to a string?" é uma das mais populares no Stack Overflow (milhares de visualizações!). E não é por acaso. Converter a pilha de chamadas em string é uma operação básica, mas criticamente importante para qualquer desenvolvedor Java. Neste artigo, não vamos apenas dar uma receita pronta, mas também analisar como fazer isso de forma otimizada, sem vazamentos de memória e com máximo desempenho. Pronto? Vamos lá!
Fundamentos: método clássico com StringWriter e PrintWriter
A maneira mais comum de converter um stack trace em string é usar a combinação de StringWriter e PrintWriter. Este método é simples, confiável e funciona desde o Java 1.1. Vamos ver o código:
import java.io.PrintWriter;import java.io.StringWriter;
public class StackTraceUtil {
public static String stackTraceToString(Throwable e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.flush(); return sw.toString(); }}Como funciona? StringWriter é um fluxo que escreve em uma string, e PrintWriter formata a saída. O método printStackTrace(PrintWriter) faz toda a mágica: ele escreve a pilha de chamadas completa (incluindo exceções aninhadas) neste fluxo. Após chamar flush(), obtemos a string pronta. Simples e elegante, certo?
Vantagens:
- Funciona com qualquer tipo de exceção (Throwable, Exception, Error).
- Suporta exceções aninhadas (caused by).
- Não requer bibliotecas externas.
Desvantagens:
- Cria dois objetos adicionais (StringWriter, PrintWriter) a cada chamada.
- Pode ser excessivo se você precisar apenas do primeiro nível da pilha.
Abordagem alternativa: getStackTrace() e StringBuilder
Se você quiser mais controle sobre o formato da saída ou se preocupar com desempenho (por exemplo, registrando milhares de exceções por segundo), pode usar o método getStackTrace() da classe Throwable. Ele retorna um array StackTraceElement[], que pode ser processado manualmente:
public static String stackTraceToString(Throwable e) { StringBuilder sb = new StringBuilder(); sb.append(e.toString()).append(""); for (StackTraceElement element : e.getStackTrace()) { sb.append("\\tat ").append(element).append(""); } // Adiciona exceções aninhadas (caused by) Throwable cause = e.getCause(); while (cause != null) { sb.append("Caused by: ").append(cause.toString()).append(""); for (StackTraceElement element : cause.getStackTrace()) { sb.append("\\tat ").append(element).append(""); } cause = cause.getCause(); } return sb.toString();}Este método dá a você controle total: você pode filtrar a pilha (por exemplo, remover quadros internos do framework), alterar o formato da data ou adicionar informações extras. Mas tenha cuidado: a formatação manual pode levar a erros se você não considerar todas as nuances (por exemplo, suppressed exceptions no Java 7+).