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