简体   繁体   中英

Is it possible to prefix every line of a stacktrace in log4j?

when you write

logger.error("message", exception);

log4j produces the message and the complete stack trace:

Aug  9 06:26:13 10.175.60.14 myPrefix: [error] [TP-Processor114] [my.class.Name] message : exception
at fatherOfException
at fatherof_fatherOfException
at fatherof_fatherof_fatherOfException
...

my conversion pattern is

log4j.appender.syslog.layout.ConversionPattern=myPrefix: [%p] [%t] [%c] [%x] - %m%n

So, is it possible to prefix every line with myPrefix, as:

    Aug  9 06:26:13 10.175.60.14 myPrefix: [error] [TP-Processor114] [my.class.Name] message : exception
myPrefix    at fatherOfException
myPrefix    at fatherof_fatherOfException
myPrefix    at fatherof_fatherof_fatherOfException
    ...

When I grep my logs on myPrefix, i don't see the stack trace. We have many different prefixes (one per module)

Thanks in advance.

Refer to Alex's answer as it's a cleaner one.

You can write your own implementation of org.apache.log4j.spi.ThrowableRenderer :

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/spi/ThrowableRenderer.html

Then, edit your log4j configuration:

log4j.throwableRenderer= your-custom-class-name

The ThrowableRenderer returns an array of String s. Here's your code:

String prefix = "myPrefix"; // Or some constant

List<String> l = new LinkedList<String>();
l.add(String.format("%s %s: %s", prefix, t.getClass().getName(), t.getMessage()));

for (StackTraceElement ste: t.getStackTrace()){
    l.add(String.format("%s %s", prefix, ste.toString()));
}

return (String[]) l.toArray();

Another idea would be to print the Throwable into a PrintWriter that is wrapping some Writer that writes into memory, and then re-iterate over strings delimited by line.separator , adding each line to a list:

StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));

List<String> l = new LinkedList<String>();
for (String s: sw.toString().split(System.lineSeparator())) {
    l.add(String.format("%s %s", prefix, s));
}

return (String[]) l.toArray();

Subclass ThrowableRenderer , for example:

import org.apache.log4j.DefaultThrowableRenderer;
import org.apache.log4j.spi.ThrowableRenderer;

public class LogThrowableRenderer implements ThrowableRenderer {

    DefaultThrowableRenderer def = new DefaultThrowableRenderer();

    @Override
    public String[] doRender(Throwable t) {
        String[] temp = def.doRender(t);
        for (int i = 0; i < temp.length; i++) {
            temp[i] = "myPrefix "+temp[i];
        }
        return temp;
    }

}

Add to your log4j.properties :

log4j.throwableRenderer=whatever.package.LogThrowableRenderer

This uses the existing DefaultThrowableRenderer to render the stacktrace in the familiar way before adding the prefix, so it will include the Throwable class, message, and cause.

With log4j2 you can add a prefix to each new line of your stacktraces by specifying the separator as a newline followed by your prefix:

%xThrowable{separator(\nmyPrefix)}%n

You can similarly add prefixes to each new line of each multi-line message:

%replace{%m}{[\r\n]+}{\nmyPrefix}

For example, to add a whitespace prefix to each multi-line log and stacktrace line:

appender.rolling.layout.pattern = %d [%t] %-5p %c %L - %replace{%m}{[\r\n]+}{\n }%xThrowable{separator(\n )}%n

Note that you want place the %xThrowable bit after your %m .

Write a wrapper function to do it for you.

private void writeToLogs(String message, Exception excp) {
    logger.error("myPrefix\t" + message, excp);
}

Here is one solution that I found for logback and I used. The API for now does not let you prefix every line of the stack trace. For the other lines I already had a Pattern in place with a tracking ID. So I had to use that. So what I did is

  1. I created a CustomLayout extends LayoutBase
  2. In the doLayout method you can check if you have on the event an TrowableProxy and from that you can parse every line and add a prefix. I just checked if I have on the line my tracking ID, if there is not, I just appended it.

Here is the code

public class MyLayout extends LayoutBase<ILoggingEvent> {

    private PatternLayout pattern = null;
    private String thePattern;

   @Override
    public String doLayout(ILoggingEvent event) {
        if (!pattern.isStarted()) {
            pattern.setPattern(thePattern);
            pattern.start();
        }

        String patternLayoutResult = pattern.doLayout(event);
        StringBuilder sb = new StringBuilder();

        String trackingId = MDC.get(TRACKING_ID);
        if (trackingId != null) {
            for (String line : patternLayoutResult.split(LINE_SEPARATOR)) {
                if (!line.contains(trackingId)) {
                    sb.append("Exception [");
                    sb.append(TRACKING_ID);
                    sb.append(":");
                    sb.append(trackingId);
                    sb.append("] ");

                }
                sb.append(line);
                sb.append(LINE_SEPARATOR);
            }

            return sb.toString();
        }

        return patternLayoutResult;
    }

And in the logback.xml

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="mypackage.CustomLoggingLayout">
                <thePattern>%d{dd.MM.yyyy HH:mm:ss} %-5p [trackingId:%X{trackingId}] %c{1}:%L - %m%n</thePattern>
                <Pattern></Pattern>
            </layout>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

And now the stackTrace looks like:

Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at org.glassfish.jersey.jdkhttp.JdkHttpHandlerContainer.handle(JdkHttpHandlerContainer.java:161)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:700)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:672)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
Exception [trackingId:d3746a12a86e46f991a338b4317045f5] at java.lang.Thread.run(Thread.java:748)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM