View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.slf4j;
20  
21  import java.io.PrintStream;
22  import java.util.function.Consumer;
23  
24  import org.apache.maven.api.services.MessageBuilder;
25  import org.slf4j.simple.ExtSimpleLogger;
26  
27  import static org.apache.maven.jline.MessageUtils.builder;
28  
29  /**
30   * Logger for Maven, that support colorization of levels and stacktraces. This class implements 2 methods introduced in
31   * slf4j-simple provider local copy.
32   *
33   * @since 3.5.0
34   */
35  public class MavenSimpleLogger extends ExtSimpleLogger {
36  
37      private String traceRenderedLevel;
38      private String debugRenderedLevel;
39      private String infoRenderedLevel;
40      private String warnRenderedLevel;
41      private String errorRenderedLevel;
42  
43      static Consumer<String> logSink;
44  
45      public static void setLogSink(Consumer<String> logSink) {
46          MavenSimpleLogger.logSink = logSink;
47      }
48  
49      MavenSimpleLogger(String name) {
50          super(name);
51      }
52  
53      @Override
54      protected String renderLevel(int level) {
55          if (traceRenderedLevel == null) {
56              traceRenderedLevel = builder().trace("TRACE").build();
57              debugRenderedLevel = builder().debug("DEBUG").build();
58              infoRenderedLevel = builder().info("INFO").build();
59              warnRenderedLevel = builder().warning("WARNING").build();
60              errorRenderedLevel = builder().error("ERROR").build();
61          }
62          switch (level) {
63              case LOG_LEVEL_TRACE:
64                  return traceRenderedLevel;
65              case LOG_LEVEL_DEBUG:
66                  return debugRenderedLevel;
67              case LOG_LEVEL_INFO:
68                  return infoRenderedLevel;
69              case LOG_LEVEL_WARN:
70                  return warnRenderedLevel;
71              case LOG_LEVEL_ERROR:
72              default:
73                  return errorRenderedLevel;
74          }
75      }
76  
77      @Override
78      protected void doWrite(StringBuilder buf, Throwable t) {
79          Consumer<String> sink = logSink;
80          if (sink != null) {
81              sink.accept(buf.toString());
82          } else {
83              super.doWrite(buf, t);
84          }
85      }
86  
87      @Override
88      protected void writeThrowable(Throwable t, PrintStream stream) {
89          if (t == null) {
90              return;
91          }
92          MessageBuilder builder = builder().failure(t.getClass().getName());
93          if (t.getMessage() != null) {
94              builder.a(": ").failure(t.getMessage());
95          }
96          stream.println(builder);
97  
98          printStackTrace(t, stream, "");
99      }
100 
101     private void printStackTrace(Throwable t, PrintStream stream, String prefix) {
102         MessageBuilder builder = builder();
103         for (StackTraceElement e : t.getStackTrace()) {
104             builder.a(prefix);
105             builder.a("    ");
106             builder.strong("at");
107             builder.a(" ");
108             builder.a(e.getClassName());
109             builder.a(".");
110             builder.a(e.getMethodName());
111             builder.a("(");
112             builder.strong(getLocation(e));
113             builder.a(")");
114             stream.println(builder);
115             builder.setLength(0);
116         }
117         for (Throwable se : t.getSuppressed()) {
118             writeThrowable(se, stream, "Suppressed", prefix + "    ");
119         }
120         Throwable cause = t.getCause();
121         if (cause != null && t != cause) {
122             writeThrowable(cause, stream, "Caused by", prefix);
123         }
124     }
125 
126     private void writeThrowable(Throwable t, PrintStream stream, String caption, String prefix) {
127         MessageBuilder builder =
128                 builder().a(prefix).strong(caption).a(": ").a(t.getClass().getName());
129         if (t.getMessage() != null) {
130             builder.a(": ").failure(t.getMessage());
131         }
132         stream.println(builder);
133 
134         printStackTrace(t, stream, prefix);
135     }
136 
137     protected String getLocation(final StackTraceElement e) {
138         assert e != null;
139 
140         if (e.isNativeMethod()) {
141             return "Native Method";
142         } else if (e.getFileName() == null) {
143             return "Unknown Source";
144         } else if (e.getLineNumber() >= 0) {
145             return e.getFileName() + ":" + e.getLineNumber();
146         } else {
147             return e.getFileName();
148         }
149     }
150 }