1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.classic.layout;
16  
17  import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
18  import ch.qos.logback.classic.spi.ILoggingEvent;
19  import ch.qos.logback.classic.spi.IThrowableProxy;
20  import ch.qos.logback.core.CoreConstants;
21  import ch.qos.logback.core.LayoutBase;
22  import ch.qos.logback.core.util.CachingDateFormatter;
23  import org.slf4j.event.KeyValuePair;
24  
25  import java.util.List;
26  
27  /**
28   * A layout with a fixed format. The output is equivalent to that produced by
29   * {@link ch.qos.logback.classic.PatternLayout PatternLayout} with the pattern:
30   * </p>
31   * 
32   * <pre>
33   * %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
34   * </pre>
35   * 
36   * <p>
37   * TTLLLayout has the advantage of faster load time whereas
38   * {@link ch.qos.logback.classic.PatternLayout PatternLayout} requires roughly
39   * 40 milliseconds to load its parser classes. Note that the second run of
40   * PatternLayout will be much much faster (approx. 10 micro-seconds).
41   * </p>
42   * 
43   * <p>
44   * Fixed format layouts such as TTLLLayout should be considered as an
45   * alternative to PatternLayout only if the extra 40 milliseconds at application
46   * start-up is considered significant.
47   * </p>
48   * 
49   * @author Ceki G&uuml;lc&uuml;
50   * @since 1.1.6
51   */
52  public class TTLLLayout extends LayoutBase<ILoggingEvent> {
53  
54      CachingDateFormatter cachingDateFormatter = new CachingDateFormatter("HH:mm:ss.SSS");
55      ThrowableProxyConverter tpc = new ThrowableProxyConverter();
56  
57      @Override
58      public void start() {
59          tpc.start();
60          super.start();
61      }
62  
63      @Override
64      public String doLayout(ILoggingEvent event) {
65          if (!isStarted()) {
66              return CoreConstants.EMPTY_STRING;
67          }
68          StringBuilder sb = new StringBuilder();
69  
70          long timestamp = event.getTimeStamp();
71  
72          sb.append(cachingDateFormatter.format(timestamp));
73          sb.append(" [");
74          sb.append(event.getThreadName());
75          sb.append("] ");
76          sb.append(event.getLevel().toString());
77          sb.append(" ");
78          sb.append(event.getLoggerName());
79          sb.append(" -");
80          kvp(event, sb);
81          sb.append("- ");
82          sb.append(event.getFormattedMessage());
83          sb.append(CoreConstants.LINE_SEPARATOR);
84          IThrowableProxy tp = event.getThrowableProxy();
85          if (tp != null) {
86              String stackTrace = tpc.convert(event);
87              sb.append(stackTrace);
88          }
89          return sb.toString();
90      }
91  
92      static final char DOUBLE_QUOTE_CHAR = '"';
93      private void kvp(ILoggingEvent event, StringBuilder sb) {
94          List<KeyValuePair> kvpList = event.getKeyValuePairs();
95          if (kvpList == null || kvpList.isEmpty()) {
96              return;
97          }
98  
99          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 }