001package ch.qos.logback.classic.layout;
002
003import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
004import ch.qos.logback.classic.spi.ILoggingEvent;
005import ch.qos.logback.classic.spi.IThrowableProxy;
006import ch.qos.logback.core.CoreConstants;
007import ch.qos.logback.core.LayoutBase;
008import ch.qos.logback.core.util.CachingDateFormatter;
009import org.slf4j.event.KeyValuePair;
010
011import java.util.List;
012
013/**
014 * A layout with a fixed format. The output is equivalent to that produced by
015 * {@link ch.qos.logback.classic.PatternLayout PatternLayout} with the pattern:
016 * </p>
017 * 
018 * <pre>
019 * %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
020 * </pre>
021 * 
022 * <p>
023 * TTLLLayout has the advantage of faster load time whereas
024 * {@link ch.qos.logback.classic.PatternLayout PatternLayout} requires roughly
025 * 40 milliseconds to load its parser classes. Note that the second run of
026 * PatternLayout will be much much faster (approx. 10 micro-seconds).
027 * </p>
028 * 
029 * <p>
030 * Fixed format layouts such as TTLLLayout should be considered as an
031 * alternative to PatternLayout only if the extra 40 milliseconds at application
032 * start-up is considered significant.
033 * </p>
034 * 
035 * @author Ceki G&uuml;lc&uuml;
036 * @since 1.1.6
037 */
038public class TTLLLayout extends LayoutBase<ILoggingEvent> {
039
040    CachingDateFormatter cachingDateFormatter = new CachingDateFormatter("HH:mm:ss.SSS");
041    ThrowableProxyConverter tpc = new ThrowableProxyConverter();
042
043    @Override
044    public void start() {
045        tpc.start();
046        super.start();
047    }
048
049    @Override
050    public String doLayout(ILoggingEvent event) {
051        if (!isStarted()) {
052            return CoreConstants.EMPTY_STRING;
053        }
054        StringBuilder sb = new StringBuilder();
055
056        long timestamp = event.getTimeStamp();
057
058        sb.append(cachingDateFormatter.format(timestamp));
059        sb.append(" [");
060        sb.append(event.getThreadName());
061        sb.append("] ");
062        sb.append(event.getLevel().toString());
063        sb.append(" ");
064        sb.append(event.getLoggerName());
065        sb.append(" -");
066        kvp(event, sb);
067        sb.append("- ");
068        sb.append(event.getFormattedMessage());
069        sb.append(CoreConstants.LINE_SEPARATOR);
070        IThrowableProxy tp = event.getThrowableProxy();
071        if (tp != null) {
072            String stackTrace = tpc.convert(event);
073            sb.append(stackTrace);
074        }
075        return sb.toString();
076    }
077
078    static final char DOUBLE_QUOTE_CHAR = '"';
079    private void kvp(ILoggingEvent event, StringBuilder sb) {
080        List<KeyValuePair> kvpList = event.getKeyValuePairs();
081        if (kvpList == null || kvpList.isEmpty()) {
082            return;
083        }
084
085        int len = kvpList.size();
086
087        for (int i = 0; i < len; i++) {
088            KeyValuePair kvp = kvpList.get(i);
089            if (i != 0)
090                sb.append(' ');
091            sb.append(String.valueOf(kvp.key));
092            sb.append('=');
093            sb.append(DOUBLE_QUOTE_CHAR);
094            sb.append(String.valueOf(kvp.value));
095            sb.append(DOUBLE_QUOTE_CHAR);
096        }
097    }
098
099}