如何将堆栈跟踪发送到log4j?

180

假设你捕获了一个异常并在标准输出(比如说控制台)上进行e.printStackTrace()操作,你可能会得到以下输出:

java.io.FileNotFoundException: so.txt
        at java.io.FileInputStream.<init>(FileInputStream.java)
        at ExTest.readMyFile(ExTest.java:19)
        at ExTest.main(ExTest.java:7)

现在我想将这个内容发送给一个像log4j这样的日志记录器,以获取以下结果:

31947 [AWT-EventQueue-0] ERROR Java.io.FileNotFoundException: so.txt
32204 [AWT-EventQueue-0] ERROR    at java.io.FileInputStream.<init>(FileInputStream.java)
32235 [AWT-EventQueue-0] ERROR    at ExTest.readMyFile(ExTest.java:19)
32370 [AWT-EventQueue-0] ERROR    at ExTest.main(ExTest.java:7)

我该怎么做?

try {
   ...
} catch (Exception e) {
    final String s;
    ...  // <-- What goes here?
    log.error( s );
}
11个回答

292

你可以直接将异常传递给日志记录器,例如:

try {
   ...
} catch (Exception e) {
    log.error( "failed!", e );
}

渲染堆栈跟踪是由log4j负责的。


39
记录器的第一个参数是一个对象,将对其调用toString()方法。然而第二个参数必须是Throwable类型,并显示堆栈跟踪信息。 - Peter Lawrey
2
我从来不知道该把什么东西塞进第一个字符串里,通常我只是做log.error(e.getLocalizedMessage(),e),这完全是多余的。它能处理第一个参数为null吗? - Mark Peters
7
@Mark:尝试提供一个比异常信息更相关的消息,例如,在那个时间它实际上是在尝试做什么? - skaffman
3
@Mark Peters,一个很好的例子是将当前方法的参数记录为消息,或者 - 对于FileNotFound异常,记录尝试打开的路径的完整名称。任何有助于查找问题的东西 - 只需想想您无法调试该情况。 - Thorbjørn Ravn Andersen
1
@skaffman:实际上我正在使用log4j-1.2.8.jar,我想要在我的日志文件中打印所有的stackTrace,所以我尝试了你上面提到的方法,但是它只打印了nullPointerException。我在我的代码中使用了e.printStackTrace(),它可以打印出所有的跟踪信息。为什么会这样呢? - Yubaraj
显示剩余3条评论

15

如果您想记录堆栈跟踪而不涉及异常,请按照以下步骤执行:

String message = "";

for(StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {                         
    message = message + System.lineSeparator() + stackTraceElement.toString();
}   
log.warn("Something weird happened. I will print the the complete stacktrace even if we have no exception just to help you find the cause" + message);

3
对于 System.lineSeparator() 给予一个赞,它帮助我打印了来自远程服务的堆栈跟踪信息。 - Stephanie
2
我所搜索的是,但你应该在这里使用 StringBuilder。 - xeruf

13
你可以通过ExceptionUtils.getStackTrace方法将堆栈跟踪信息以字符串形式获取。
参见: ExceptionUtils.java 我只在log.debug中使用它,以使log.error保持简单。

10
skaffman的回答绝对是正确的。所有日志记录器方法,如error()warn()info()debug()都接受Throwable作为第二个参数:
try {
...
 } catch (Exception e) {
logger.error("error: ", e);
}

然而,你也可以将堆栈跟踪提取为字符串。有时,如果您希望利用格式化功能使用“{}”占位符,这可能很有用 - 请参见方法void info(String var1, Object... var2);。在这种情况下,假设您有一个字符串形式的堆栈跟踪,则可以执行以下操作:

try {
...
 } catch (Exception e) {
String stacktrace = TextUtils.getStacktrace(e);
logger.error("error occurred for usename {} and group {}, details: {}",username, group, stacktrace);
}

这将打印参数化信息和堆栈跟踪,就像方法:logger.error("error: ", e);一样。

我实际上编写了一个开源库,其中包含提取堆栈跟踪字符串的实用程序,具有过滤掉一些噪声的选项。例如,如果您指定了感兴趣的包前缀,您提取的堆栈跟踪将过滤掉一些不相关的部分,留下非常简洁的信息。以下是链接到解释该库有哪些实用程序以及在哪里获取它(作为Maven构件和Git源)以及如何使用它的文章链接:Open Source Java library with stack trace filtering, Silent String parsing Unicode converter and Version comparison 请看“Stacktrace noise filter”段落。


1
一个被埋没了但很好的答案。 - payne
谢谢,@payne。我很感激你的好评。 - Michael Gantman
谢谢。我最终看到了这个问题,因为我特别在寻找这个答案。 - axiopisty
@axiopisty - 不用谢。我很高兴我的回答对你有帮助。 - Michael Gantman

7

我只是觉得这件事对我有用,可能对你也有帮助。 如果你这样做。

try {
   ...
} catch (Exception e) {
    log.error( "failed! {}", e );
}

您将获得异常的头部而不是整个堆栈跟踪。这是因为日志记录器会认为您正在传递一个字符串,因此请按照skaffman所说的那样不使用{}


1
在这里,您肯定会得到整个堆栈跟踪,但是 {} 也将逐字打印(不进行任何替换)吗? - k1eran
1
不要那么确定,我的朋友!它对我没用,我只得到了标题。但这可能取决于log4j的版本。 - iberbeu
@iberbeu是正确的,因为Throwable.toString()不会返回堆栈跟踪。(假设您正在使用知道如何处理'{}'的记录器。) - user1531971

4
在Log4j 2中,您可以使用Logger.catching()记录从已捕获的异常中获取的堆栈跟踪。
    try {
        String msg = messages[messages.length];
        logger.error("An exception should have been thrown");
    } catch (Exception ex) {
        logger.catching(ex);
    }

您也可以使用以下代码打印堆栈跟踪,而无需抛出异常:logger.catching(new Throwable()); - Jamie
我们记录堆栈跟踪而没有使用 String message,这是什么意思?为什么不使用 log.error("message", ex) - Geoff Langenderfer
这个想法是你想要记录下捕获并吞噬了一个异常,但没有其他信息。如果你想添加一个错误消息字符串,那么你可以像你说的那样使用logger.error。 - mbonness

2

这个答案可能与所问问题无关,但与问题的标题相关。

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable("Created at main()");
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        createdBy.printStackTrace(pw);
        try {
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger.debug(os.toString());
    }
}

或者

public static String getStackTrace (Throwable t)
{
    StringWriter stringWriter = new StringWriter();
    PrintWriter  printWriter  = new PrintWriter(stringWriter);
    t.printStackTrace(printWriter);
    printWriter.close();    //surprise no IO exception here
    try {
        stringWriter.close();
    }
    catch (IOException e) {
    }
    return stringWriter.toString();
}

或者

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for(StackTraceElement stackTrace: stackTraceElements){
    logger.debug(stackTrace.getClassName()+ "  "+ stackTrace.getMethodName()+" "+stackTrace.getLineNumber());
}

0

这将是良好的log4j错误/异常日志记录 - 可以被splunk/其他日志/监控软件读取。所有内容都是键值对形式。 log4j将从异常对象e获取堆栈跟踪。

    try {
          ---
          ---
    } catch (Exception e) {
        log.error("api_name={} method={} _message=\"error description.\" msg={}", 
                  new Object[]{"api_name", "method_name", e.getMessage(), e});
    }

0
你可以使用下面的代码:
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class LogWriterUtility {
    Logger log;

    public LogWriterUtility(Class<?> clazz) {
        log = LogManager.getLogger(clazz);
    }

public void errorWithAnalysis( Exception exception) {

        String message="No Message on error";
        StackTraceElement[] stackTrace = exception.getStackTrace();
        if(stackTrace!=null && stackTrace.length>0) {
            message="";
            for (StackTraceElement e : stackTrace) {
                message += "\n" + e.toString();
            }
        }
        log.error(message);


    }

}

在这里,您只需调用:LogWriterUtility.errorWithAnalysis( YOUR_EXCEPTION_INSTANCE);

它将把堆栈跟踪打印到日志中。


0
Try this:

catch (Throwable t) {
    logger.error("any message" + t);
    StackTraceElement[] s = t.getStackTrace();
    for(StackTraceElement e : s){
        logger.error("\tat " + e);
    }   
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接