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  
26  import static org.apache.maven.jline.MessageUtils.builder;
27  
28  /**
29   * Logger for Maven, that support colorization of levels and stacktraces. This class implements 2 methods introduced in
30   * slf4j-simple provider local copy.
31   *
32   * @since 3.5.0
33   */
34  public class MavenSimpleLogger extends MavenBaseLogger {
35  
36      private String traceRenderedLevel;
37      private String debugRenderedLevel;
38      private String infoRenderedLevel;
39      private String warnRenderedLevel;
40      private String errorRenderedLevel;
41  
42      static Consumer<String> logSink;
43  
44      public static void setLogSink(Consumer<String> logSink) {
45          MavenSimpleLogger.logSink = logSink;
46      }
47  
48      MavenSimpleLogger(String name) {
49          super(name);
50      }
51  
52      @Override
53      protected String renderLevel(int level) {
54          if (traceRenderedLevel == null) {
55              traceRenderedLevel = builder().trace("TRACE").build();
56              debugRenderedLevel = builder().debug("DEBUG").build();
57              infoRenderedLevel = builder().info("INFO").build();
58              warnRenderedLevel = builder().warning("WARNING").build();
59              errorRenderedLevel = builder().error("ERROR").build();
60          }
61          switch (level) {
62              case LOG_LEVEL_TRACE:
63                  return traceRenderedLevel;
64              case LOG_LEVEL_DEBUG:
65                  return debugRenderedLevel;
66              case LOG_LEVEL_INFO:
67                  return infoRenderedLevel;
68              case LOG_LEVEL_WARN:
69                  return warnRenderedLevel;
70              case LOG_LEVEL_ERROR:
71              default:
72                  return errorRenderedLevel;
73          }
74      }
75  
76      protected void write(StringBuilder buf, Throwable t) {
77          Consumer<String> sink = logSink;
78          if (sink != null) {
79              sink.accept(buf.toString());
80              if (t != null) {
81                  writeThrowable(t, sink);
82              }
83          } else {
84              super.write(buf, t);
85          }
86      }
87  
88      @Override
89      protected void writeThrowable(Throwable t, PrintStream stream) {
90          writeThrowable(t, stream::println);
91      }
92  
93      protected void writeThrowable(Throwable t, Consumer<String> stream) {
94          if (t == null) {
95              return;
96          }
97          MessageBuilder builder = builder().failure(t.getClass().getName());
98          if (t.getMessage() != null) {
99              builder.a(": ").failure(t.getMessage());
100         }
101         stream.accept(builder.toString());
102 
103         printStackTrace(t, stream, "");
104     }
105 
106     protected void printStackTrace(Throwable t, Consumer<String> stream, String prefix) {
107         MessageBuilder builder = builder();
108         for (StackTraceElement e : t.getStackTrace()) {
109             builder.a(prefix);
110             builder.a("    ");
111             builder.strong("at");
112             builder.a(" ");
113             builder.a(e.getClassName());
114             builder.a(".");
115             builder.a(e.getMethodName());
116             builder.a("(");
117             builder.strong(getLocation(e));
118             builder.a(")");
119             stream.accept(builder.toString());
120             builder.setLength(0);
121         }
122         for (Throwable se : t.getSuppressed()) {
123             writeThrowable(se, stream, "Suppressed", prefix + "    ");
124         }
125         Throwable cause = t.getCause();
126         if (cause != null && t != cause) {
127             writeThrowable(cause, stream, "Caused by", prefix);
128         }
129     }
130 
131     protected void writeThrowable(Throwable t, Consumer<String> stream, String caption, String prefix) {
132         MessageBuilder builder =
133                 builder().a(prefix).strong(caption).a(": ").a(t.getClass().getName());
134         if (t.getMessage() != null) {
135             builder.a(": ").failure(t.getMessage());
136         }
137         stream.accept(builder.toString());
138 
139         printStackTrace(t, stream, prefix);
140     }
141 
142     protected String getLocation(final StackTraceElement e) {
143         assert e != null;
144 
145         if (e.isNativeMethod()) {
146             return "Native Method";
147         } else if (e.getFileName() == null) {
148             return "Unknown Source";
149         } else if (e.getLineNumber() >= 0) {
150             return e.getFileName() + ":" + e.getLineNumber();
151         } else {
152             return e.getFileName();
153         }
154     }
155 
156     public void configure(int defaultLogLevel) {
157         String levelString = recursivelyComputeLevelString();
158         if (levelString != null) {
159             this.currentLogLevel = SimpleLoggerConfiguration.stringToLevel(levelString);
160         } else {
161             this.currentLogLevel = defaultLogLevel;
162         }
163         traceRenderedLevel = builder().trace("TRACE").build();
164         debugRenderedLevel = builder().debug("DEBUG").build();
165         infoRenderedLevel = builder().info("INFO").build();
166         warnRenderedLevel = builder().warning("WARNING").build();
167         errorRenderedLevel = builder().error("ERROR").build();
168     }
169 
170     public void setLogLevel(int logLevel) {
171         this.currentLogLevel = logLevel;
172     }
173 }