View Javadoc
1   /*
2    * Copyright 2019-2022 Foreseeti AB <https://foreseeti.com>
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.mal_lang.lib;
18  
19  import java.util.Set;
20  import java.util.TreeSet;
21  import java.util.logging.ConsoleHandler;
22  import java.util.logging.Formatter;
23  import java.util.logging.Level;
24  import java.util.logging.LogRecord;
25  import java.util.logging.Logger;
26  
27  public class MalLogger extends Logger {
28    @SuppressWarnings("serial")
29    private static class MalLevel extends Level {
30      public static final Level DEBUG = new MalLevel("DEBUG", Level.SEVERE.intValue() + 1);
31      public static final Level INFO = new MalLevel("INFO", Level.SEVERE.intValue() + 2);
32      public static final Level WARNING = new MalLevel("WARNING", Level.SEVERE.intValue() + 3);
33      public static final Level ERROR = new MalLevel("ERROR", Level.SEVERE.intValue() + 4);
34  
35      private MalLevel(String name, int value) {
36        super(name, value);
37      }
38    }
39  
40    private static class MalFormatter extends Formatter {
41      @Override
42      public String format(LogRecord record) {
43        StringBuffer sb = new StringBuffer();
44        sb.append(printLevel(record.getLoggerName(), record.getLevel()));
45        sb.append(" ");
46        sb.append(record.getMessage());
47        sb.append(String.format("%n"));
48        return sb.toString();
49      }
50  
51      private String printLevel(String loggerName, Level level) {
52        String colorInit = "";
53        String colorClear = "";
54  
55        if (System.console() != null) {
56          switch (level.getName()) {
57            case "ERROR":
58              colorInit = "\u001B[1;31m";
59              break;
60            case "WARNING":
61              colorInit = "\u001B[1;33m";
62              break;
63            case "INFO":
64              colorInit = "\u001B[1;34m";
65              break;
66            case "DEBUG":
67              colorInit = "\u001B[1;36m";
68              break;
69            default:
70          }
71          colorClear = "\u001B[m";
72        }
73        return String.format("[%s%s %s%s]", colorInit, loggerName, level.getName(), colorClear);
74      }
75    }
76  
77    private class LogMessage implements Comparable<LogMessage> {
78      public final Level level;
79      public final String message;
80  
81      public LogMessage(Level level, String message) {
82        this.level = level;
83        this.message = message;
84      }
85  
86      @Override
87      public int compareTo(LogMessage o) {
88        if (o instanceof LogMessagePosition) {
89          return -1;
90        }
91        int cmp = Integer.compare(this.level.intValue(), o.level.intValue());
92        if (cmp != 0) {
93          return cmp;
94        }
95        return this.message.compareTo(o.message);
96      }
97  
98      @Override
99      public String toString() {
100       return message;
101     }
102   }
103 
104   private class LogMessagePosition extends LogMessage {
105     public final Position position;
106 
107     public LogMessagePosition(Level level, String message, Position position) {
108       super(level, message);
109       this.position = position;
110     }
111 
112     @Override
113     public int compareTo(LogMessage o) {
114       if (!(o instanceof LogMessagePosition)) {
115         return 1;
116       }
117       var other = (LogMessagePosition) o;
118       int cmp = this.position.compareTo(other.position);
119       if (cmp != 0) {
120         return cmp;
121       }
122       cmp = Integer.compare(this.level.intValue(), other.level.intValue());
123       if (cmp != 0) {
124         return cmp;
125       }
126       return this.message.compareTo(other.message);
127     }
128 
129     @Override
130     public String toString() {
131       return String.format("%s %s", position.posString(), message);
132     }
133   }
134 
135   private boolean verbose;
136   private boolean debug;
137   private boolean isBuffered;
138 
139   private Set<LogMessage> logMessages = new TreeSet<>();
140 
141   public MalLogger(String name) {
142     this(name, false, false);
143   }
144 
145   public MalLogger(String name, boolean verbose, boolean debug) {
146     this(name, verbose, debug, true);
147   }
148 
149   public MalLogger(String name, boolean verbose, boolean debug, boolean isBuffered) {
150     this(name, null);
151     this.verbose = verbose;
152     this.debug = debug;
153     this.isBuffered = isBuffered;
154     if (debug) {
155       setLevel(MalLevel.DEBUG);
156     } else if (verbose) {
157       setLevel(MalLevel.INFO);
158     } else {
159       setLevel(MalLevel.WARNING);
160     }
161   }
162 
163   private MalLogger(String name, String resourceBundleName) {
164     super(name, resourceBundleName);
165     setUseParentHandlers(false);
166     ConsoleHandler handler = new ConsoleHandler();
167     handler.setFormatter(new MalFormatter());
168     addHandler(handler);
169   }
170 
171   public boolean isVerbose() {
172     return this.verbose;
173   }
174 
175   public boolean isDebug() {
176     return this.debug;
177   }
178 
179   public boolean isBuffered() {
180     return isBuffered;
181   }
182 
183   private void log(LogMessage logMessage) {
184     if (isBuffered) {
185       logMessages.add(logMessage);
186     } else {
187       log(logMessage.level, logMessage.toString());
188     }
189   }
190 
191   public void debug(Position pos, String msg) {
192     log(new LogMessagePosition(MalLevel.DEBUG, msg, pos));
193   }
194 
195   public void debug(String msg) {
196     log(new LogMessage(MalLevel.DEBUG, msg));
197   }
198 
199   public void info(Position pos, String msg) {
200     log(new LogMessagePosition(MalLevel.INFO, msg, pos));
201   }
202 
203   @Override
204   public void info(String msg) {
205     log(new LogMessage(MalLevel.INFO, msg));
206   }
207 
208   public void warning(Position pos, String msg) {
209     log(new LogMessagePosition(MalLevel.WARNING, msg, pos));
210   }
211 
212   @Override
213   public void warning(String msg) {
214     log(new LogMessage(MalLevel.WARNING, msg));
215   }
216 
217   public void error(Position pos, String msg) {
218     log(new LogMessagePosition(MalLevel.ERROR, msg, pos));
219   }
220 
221   public void error(String msg) {
222     log(new LogMessage(MalLevel.ERROR, msg));
223   }
224 
225   public void print() {
226     for (var logMessage : logMessages) {
227       log(logMessage.level, logMessage.toString());
228     }
229   }
230 }