如何从调用者的范围中抛出异常?

时间:2022-10-30 09:00:10

I'd like to create a routine that does some logging, takes some other actions, and then throws an Exception. I'd like this routine to be called from many different locations. However, creating Exceptions in this routine means they will have this routine in their stack trace. I would rather the stack trace not report this utility routine. Is there a way to do this without creating the Exception in the caller and passing it to the utility routine?

我想创建一个例程来执行一些日志记录,执行一些其他操作,然后抛出异常。我希望从许多不同的地方调用这个例程。但是,在此例程中创建异常意味着它们将在其堆栈跟踪中具有此例程。我宁愿堆栈跟踪不报告此实用程序例程。有没有办法在调用者中创建Exception并将其传递给实用程序例程?

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

For programmers who are Perl/Java bilingual: how do I carp in Java?

对于Perl / Java双语的程序员:我如何使用Java进行carp?

9 个解决方案

#1


You can set the stack trace of any exception you want to throw:

您可以设置要抛出的任何异常的堆栈跟踪:

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;
    }
}

This will print the following stacktrace at runtime:

这将在运行时打印以下堆栈跟踪:

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)

Note that as you want the method "carp" does not appear in the stacktrace. However the manipulation of stacktraces shoud only be done with greates care.

请注意,如您所愿,方法“carp”不会出现在堆栈跟踪中。然而,堆栈跟踪的操作只能通过greates care来完成。

#2


If you want to use an Exception to control the flow and what happens afterwards, a good advice it to override the fillInStackTrace() method:

如果你想使用Exception来控制流程以及之后会发生什么,那么建议它覆盖fillInStackTrace()方法:

public Throwable fillInStackTrace() {
   return this;
}

As a result you'll have an Exception without the stacktrace and with a reduced overhead (filling in the stack trace takes time).

因此,您将拥有一个没有堆栈跟踪的Exception并且减少了开销(填充堆栈跟踪需要时间)。

#3


There is no way to removing the throwing function from the stack trace. The whole purpose of the stack trace is to log the exception path so allowing a function to opt out would defeat the purpose.

无法从堆栈跟踪中删除throw函数。堆栈跟踪的整个目的是记录异常路径,因此允许函数退出将失败目的。

The only way you could change this is if you returned the exception instead of throwing it. But that forces you to depend on the caller to know to throw the exception.

唯一可以更改此方法的方法是返回异常而不是抛出异常。但这迫使你依赖调用者来知道抛出异常。

throw die("someReason).fillInStackTrace();

Modified function

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

EDIT

Added the fillInStackTrace() call to ensure the stack is reset to the point of the throw.

添加了fillInStackTrace()调用以确保将堆栈重置为throw的点。

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

#4


Mmm.. you could subclass exception and override all the methods in it, and wrap the original exception. Internally, generate a new stack trace using the getStackTrace() method from the wrapped exception. I haven't looked at the source of Exception, but you may not even have to override that many methods.

嗯..你可以继承异常并覆盖其中的所有方法,并包装原始异常。在内部,使用包装异常中的getStackTrace()方法生成新的堆栈跟踪。我没有查看Exception的来源,但您甚至可能不必覆盖那么多方法。

#5


Maybe you should consider approaching the problem from a different direction. Rather than modify the stack trace, why not just have your exception generator method (die in your example) return the exception rather than throw it? Then your call is throw die();.

也许你应该考虑从不同的方向解决问题。而不是修改堆栈跟踪,为什么不只是让你的异常生成器方法(在你的例子中死)返回异常而不是抛出它?然后你的调用是throw die();.

For example:

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


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

Now, granted, throw die() might seem a little un-aesthetic, so you could rename die() to newException() or something. But the requirement that the exception processing method does not show in the stack trace is met -- die() (or newException()) returns before the exception is thrown and is therefore not part of the stack to be traced.

现在,被授予,throw die()可能看起来有点不美观,所以你可以将die()重命名为newException()或其他东西。但是满足异常处理方法未在堆栈跟踪中显示的要求 - die()(或newException())在抛出异常之前返回,因此不是要跟踪的堆栈的一部分。

Edit: My bad. I've spent so much time working with C# that I forgot that in Java exception stack traces are generated at instantiation, where in C#/.NET exception stack traces are generated at throw-time.

编辑:我的坏。我花了很多时间使用C#,我忘了在Java中异常堆栈跟踪是在实例化时生成的,其中C#/ .NET异常堆栈跟踪是在抛出时生成的。

So this trick would work in C#, but not in Java.

所以这个技巧可以在C#中使用,但在Java中则不行。

#6


Based on what ordnungswidrig said about setting the stack trace, and what unknown (google) said about overriding fillInStackTrace(), I've created a CarpException that does exactly what I want. Note that I found I had to strip out four stack trace frames instead of just one, as I was picking up frames from both Throwable and Exception.

根据ordnungswidrig关于设置堆栈跟踪的说法以及未知(google)所说的覆盖fillInStackTrace()的内容,我创建了一个CarpException,它完全符合我的要求。请注意,我发现我必须删除四个堆栈跟踪帧而不是一个,因为我从Throwable和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;
  }
}

#7


No can do... I tried doing something like this a while back (I was trying to capture the stack trace to log method calls before AOP existed).

没有办法......我曾经尝试做过这样的事情(我试图在AOP存在之前捕获堆栈跟踪以记录方法调用)。

The stack trace is filled in when the exception is created, and that's done natively. For the thing I was working on, I ended up reading the stack trace and looking at the second element, but that wouldn't help you here...

创建异常时填充堆栈跟踪,并且本机完成。对于我正在处理的事情,我最终阅读了堆栈跟踪并查看了第二个元素,但这对您没有帮助...

#8


You might consider having your method receive a Logger as a parameter to the method. This would allow you to control the logging output based on the calling class.

您可以考虑让您的方法接收Logger作为方法的参数。这将允许您根据调用类控制日志记录输出。

I would advise against wanting your exception to exclude this part of the stack trace though. When you leave and some new person gets to maintain your code, the are not going to appreciate this non-standard error handling.

我建议不要让你的异常排除这部分堆栈跟踪。当你离开并且一些新人来维护你的代码时,他们不会理解这种非标准的错误处理。

#9


Do you throw the stack trace just to be able to analyze it? In that case you could call the getStackTrace() method on the Exception which returns a StackTraceElement[]. There you can filter the elements you don't want (f.ex. the "die" method).

你是否抛出堆栈跟踪只是为了能够分析它?在这种情况下,您可以在Exception上调用getStackTrace()方法,该方法返回StackTraceElement []。在那里你可以过滤你不想要的元素(例如“die”方法)。

#1


You can set the stack trace of any exception you want to throw:

您可以设置要抛出的任何异常的堆栈跟踪:

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;
    }
}

This will print the following stacktrace at runtime:

这将在运行时打印以下堆栈跟踪:

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)

Note that as you want the method "carp" does not appear in the stacktrace. However the manipulation of stacktraces shoud only be done with greates care.

请注意,如您所愿,方法“carp”不会出现在堆栈跟踪中。然而,堆栈跟踪的操作只能通过greates care来完成。

#2


If you want to use an Exception to control the flow and what happens afterwards, a good advice it to override the fillInStackTrace() method:

如果你想使用Exception来控制流程以及之后会发生什么,那么建议它覆盖fillInStackTrace()方法:

public Throwable fillInStackTrace() {
   return this;
}

As a result you'll have an Exception without the stacktrace and with a reduced overhead (filling in the stack trace takes time).

因此,您将拥有一个没有堆栈跟踪的Exception并且减少了开销(填充堆栈跟踪需要时间)。

#3


There is no way to removing the throwing function from the stack trace. The whole purpose of the stack trace is to log the exception path so allowing a function to opt out would defeat the purpose.

无法从堆栈跟踪中删除throw函数。堆栈跟踪的整个目的是记录异常路径,因此允许函数退出将失败目的。

The only way you could change this is if you returned the exception instead of throwing it. But that forces you to depend on the caller to know to throw the exception.

唯一可以更改此方法的方法是返回异常而不是抛出异常。但这迫使你依赖调用者来知道抛出异常。

throw die("someReason).fillInStackTrace();

Modified function

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

EDIT

Added the fillInStackTrace() call to ensure the stack is reset to the point of the throw.

添加了fillInStackTrace()调用以确保将堆栈重置为throw的点。

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

#4


Mmm.. you could subclass exception and override all the methods in it, and wrap the original exception. Internally, generate a new stack trace using the getStackTrace() method from the wrapped exception. I haven't looked at the source of Exception, but you may not even have to override that many methods.

嗯..你可以继承异常并覆盖其中的所有方法,并包装原始异常。在内部,使用包装异常中的getStackTrace()方法生成新的堆栈跟踪。我没有查看Exception的来源,但您甚至可能不必覆盖那么多方法。

#5


Maybe you should consider approaching the problem from a different direction. Rather than modify the stack trace, why not just have your exception generator method (die in your example) return the exception rather than throw it? Then your call is throw die();.

也许你应该考虑从不同的方向解决问题。而不是修改堆栈跟踪,为什么不只是让你的异常生成器方法(在你的例子中死)返回异常而不是抛出它?然后你的调用是throw die();.

For example:

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


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

Now, granted, throw die() might seem a little un-aesthetic, so you could rename die() to newException() or something. But the requirement that the exception processing method does not show in the stack trace is met -- die() (or newException()) returns before the exception is thrown and is therefore not part of the stack to be traced.

现在,被授予,throw die()可能看起来有点不美观,所以你可以将die()重命名为newException()或其他东西。但是满足异常处理方法未在堆栈跟踪中显示的要求 - die()(或newException())在抛出异常之前返回,因此不是要跟踪的堆栈的一部分。

Edit: My bad. I've spent so much time working with C# that I forgot that in Java exception stack traces are generated at instantiation, where in C#/.NET exception stack traces are generated at throw-time.

编辑:我的坏。我花了很多时间使用C#,我忘了在Java中异常堆栈跟踪是在实例化时生成的,其中C#/ .NET异常堆栈跟踪是在抛出时生成的。

So this trick would work in C#, but not in Java.

所以这个技巧可以在C#中使用,但在Java中则不行。

#6


Based on what ordnungswidrig said about setting the stack trace, and what unknown (google) said about overriding fillInStackTrace(), I've created a CarpException that does exactly what I want. Note that I found I had to strip out four stack trace frames instead of just one, as I was picking up frames from both Throwable and Exception.

根据ordnungswidrig关于设置堆栈跟踪的说法以及未知(google)所说的覆盖fillInStackTrace()的内容,我创建了一个CarpException,它完全符合我的要求。请注意,我发现我必须删除四个堆栈跟踪帧而不是一个,因为我从Throwable和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;
  }
}

#7


No can do... I tried doing something like this a while back (I was trying to capture the stack trace to log method calls before AOP existed).

没有办法......我曾经尝试做过这样的事情(我试图在AOP存在之前捕获堆栈跟踪以记录方法调用)。

The stack trace is filled in when the exception is created, and that's done natively. For the thing I was working on, I ended up reading the stack trace and looking at the second element, but that wouldn't help you here...

创建异常时填充堆栈跟踪,并且本机完成。对于我正在处理的事情,我最终阅读了堆栈跟踪并查看了第二个元素,但这对您没有帮助...

#8


You might consider having your method receive a Logger as a parameter to the method. This would allow you to control the logging output based on the calling class.

您可以考虑让您的方法接收Logger作为方法的参数。这将允许您根据调用类控制日志记录输出。

I would advise against wanting your exception to exclude this part of the stack trace though. When you leave and some new person gets to maintain your code, the are not going to appreciate this non-standard error handling.

我建议不要让你的异常排除这部分堆栈跟踪。当你离开并且一些新人来维护你的代码时,他们不会理解这种非标准的错误处理。

#9


Do you throw the stack trace just to be able to analyze it? In that case you could call the getStackTrace() method on the Exception which returns a StackTraceElement[]. There you can filter the elements you don't want (f.ex. the "die" method).

你是否抛出堆栈跟踪只是为了能够分析它?在这种情况下,您可以在Exception上调用getStackTrace()方法,该方法返回StackTraceElement []。在那里你可以过滤你不想要的元素(例如“die”方法)。