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

Posted

技术标签:

【中文标题】如何将堆栈跟踪发送到 log4j?【英文标题】:How to send a stacktrace to log4j? 【发布时间】:2011-05-19 20:49:33 【问题描述】:

假设你捕获了一个异常并在标准输出(比如控制台)上得到以下信息,如果你执行 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 );

【问题讨论】:

【参考方案1】:

您将异常直接传递给记录器,例如

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

由 log4j 来呈现堆栈跟踪。

【讨论】:

记录器将一个对象作为它的第一个参数,并将 toString() 它。但是第二个参数必须是 Throwable 并显示堆栈跟踪。 我永远不知道在第一个字符串中填充什么,通常我最终会做log.error(e.getLocalizedMessage(), e),这完全是多余的。它是否处理第一个参数的空值? @Mark:尝试给它一个比异常消息更贴近上下文的消息,例如当时它到底想做什么? @Mark Peters,一个很好的例子是将当前方法的参数记录为消息,或者 - 对于 FileNotFound 异常,尝试打开的路径的全名。任何可以帮助您找出问题所在的方法——只要认为您无法调试情况即可。 @skaffman:实际上我正在使用log4j-1.2.8.jar,我想在我的日志文件上打印所有stackTrace,所以我尝试像上面提到的那样,但只是在我的日志文件上打印nullPointerException .我在我的代码e.printStackTrace() 上使用它打印所有跟踪。为什么是这个 Kolavery?【参考方案2】:

您还可以通过ExceptionUtils.getStackTrace 以字符串形式获取堆栈跟踪。

见:ExceptionUtils.java

我只将它用于log.debug,以保持log.error 简单。

【讨论】:

【参考方案3】:

创建这个class

public class StdOutErrLog 

private static final Logger logger = Logger.getLogger(StdOutErrLog.class);

public static void tieSystemOutAndErrToLog() 
    System.setOut(createLoggingProxy(System.out));
    System.setErr(createLoggingProxy(System.err));


public static PrintStream createLoggingProxy(final PrintStream realPrintStream) 
    return new PrintStream(realPrintStream) 
        public void print(final String string) 
            logger.info(string);
        
        public void println(final String string) 
            logger.info(string);
        
    ;


在你的代码中调用它

StdOutErrLog.tieSystemOutAndErrToLog();

【讨论】:

【参考方案4】:

此答案可能与提出的问题无关,但与问题的标题有关。

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());

【讨论】:

【参考方案5】:

只是因为它发生在我身上并且可能有用。 如果你这样做

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

您将获得异常的标头,而不是整个堆栈跟踪。因为记录器会认为您正在传递一个字符串。 正如 skaffman 所说,不用 就可以了

【讨论】:

您肯定会在这里获得整个堆栈跟踪,但是 也会逐字打印(没有任何替换)? 别那么肯定我的朋友!这对我不起作用,我只有标题。它可能取决于 log4j 的版本 @iberbeu 是正确的,因为Throwable.toString() 不返回堆栈跟踪。 (假设您使用的记录器知道如何处理“”。)【参考方案6】:

如果您想在不涉及异常的情况下记录堆栈跟踪,只需执行以下操作:

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

【讨论】:

System.lineSeparator() 的 +1,这帮助我打印了一个堆栈跟踪,这是我从远程服务获得的响应 我搜索过的,但你应该在这里使用 StringBuilder【参考方案7】:

skaffman 的答案绝对是正确的答案。 error()warn()info()debug() 等所有 logger 方法都将 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);

实际上,我编写了一个开源库,其中包含一个实用程序,用于将堆栈跟踪提取为字符串,并且可以选择巧妙地从堆栈跟踪中过滤掉一些噪音。 IE。如果您指定您对提取的堆栈跟踪感兴趣的包前缀,则会从一些不相关的部分中过滤掉,并为您留下非常简洁的信息。这是文章的链接,该链接解释了该库具有哪些实用程序以及从何处获取它(作为 maven 工件和 git 源)以及如何使用它。 Open Source Java library with stack trace filtering, Silent String parsing Unicode converter and Version comparison 见段落“Stacktrace 噪声过滤器

【讨论】:

一个隐藏但很好的答案。 谢谢,@payne。非常感谢您的反馈 谢谢。我最终提出了这个问题,因为我一直在寻找这个答案。 @axiopisty - 欢迎您。我很高兴它对你有用【参考方案8】:

这将是很好的 log4j 错误/异常日志记录 - 可由 splunk/其他日志记录/监控软件读取。一切都是键值对的形式。 log4j 将从 Exception obj e 获取堆栈跟踪

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

【讨论】:

【参考方案9】:
Try this:

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

【讨论】:

【参考方案10】:

您可以使用以下代码:

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

它会将 stackTrace 打印到您的日志中。

【讨论】:

【参考方案11】:

在 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());

以上是关于如何将堆栈跟踪发送到 log4j?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 log4j syslog appender 在一行中写入堆栈跟踪?

如何将 java.util.logging 发送到 log4j?

如何从 obj-c / ios 中的堆栈跟踪中获取源代码行

我的程序崩溃时如何自动生成堆栈跟踪

如何向开发者发送崩溃报告?

如何直接从浏览器将 OpenTelemetry 跟踪发送到 Honeycomb.io?