Preservando o Stacktrace/Linenumbers originais em exceções .NET
-
22-09-2019 - |
Pergunta
Entender a diferença entre jogue ex e lançar, por que o Stacktrace original é preservado neste exemplo:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
SomethingThatThrowsException(x);
}
catch (Exception)
{
throw;
}
}
static void SomethingThatThrowsException(int x)
{
int y = x / (x - x);
}
Mas não neste:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception)
{
throw;
}
}
O segundo cenário está produzindo a mesma saída que jogue ex gostaria?
Nos dois casos, espera -se ver o número da linha em que Y é inicializado.
Solução
Não tenho certeza se essa limitação está dentro da linguagem C#, a CLI ou a implementação da Microsoft, mas seu segundo exemplo é um caso em que uma chamada explícita para Exception.InternalPreserveStackTrace
é necessário conforme documentado na postagem a seguir. Uma vez que este método é internal
, geralmente deve ser chamado através da reflexão. As questões de desempenho envolvidas nisso podem ser quase completamente aliviadas, criando um Action<Exception>
para a chamada, como mostrado no final desta resposta.
Referência: Retilando exceções e preservação do rastreamento completo da pilha de chamadas
Editar: Depois de reexaminar a partição da ECMA-335 I §12.4.2 (manuseio de exceção) e Partição III §4.24 (Rethrow), agora acredito que o comportamento que você está vendo é um erro semântico no CLR (implementação da Microsoft da CLI). A única referência específica ao comportamento é "a rethrow
não muda o rastreamento da pilha no objeto. "No caso descrito aqui, o Rethrow está de fato alterando o rastreamento da pilha, fazendo o PreserveStackTrace
Hackear uma solução alternativa para uma falha de Clr Know.
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
PreserveStackTrace(ex); // <-- add this line
throw;
}
}
PreserveStackTrace
Aqui está uma otimização da entrada da entrada do blog:
private static readonly Action<Exception> _internalPreserveStackTrace =
(Action<Exception>)Delegate.CreateDelegate(
typeof(Action<Exception>),
typeof(Exception).GetMethod(
"InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic));
public static void PreserveStackTrace(Exception e)
{
_internalPreserveStackTrace(e);
}
Outras dicas
Porque no segundo exemplo, você está retirando a exceção do mesmo método. Primeiro, é jogado de um método diferente, é por que. Em um escopo de método, o Stack Trace pode ser apenas um.
Faça o seguinte, a melhor maneira é sempre envolver uma exceção dentro de uma nova exceção, para que você verá a profundidade da exceção.
"Se Rethrow foi emitido no mesmo método (o rastreamento da pilha de exceção possui apenas uma informação de número de linha por método, você nunca vê rastreamento de pilha que no método A, na linha 2, a exceção foi lançada e depois no mesmo método A, foi RETHROWN da linha número 17, ele conterá apenas o número da última linha de onde a exceção era Rethrown "
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
// do something here.. like log or something
throw new Exception("Internal Exception", ex);
}
Estou surpreso com tantos comentários que não leem meu comentário !! Eu escrevi em comentário que você provavelmente deveria registrar isso com segurança, existem várias razões, se o código de nível superior consumir uma exceção e você não sabe qual e onde a exceção foi lançada, o registro ajudará você a cruzar a exceção !!!
Se você não precisar registrar, não pegue a exceção.