View Javadoc

1   /*
2    * Copyright 2013 University of Glasgow.
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    *      http://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  package broadwick;
17  
18  import ch.qos.logback.classic.Level;
19  import ch.qos.logback.classic.Logger;
20  import ch.qos.logback.classic.LoggerContext;
21  import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
22  import ch.qos.logback.classic.filter.ThresholdFilter;
23  import ch.qos.logback.classic.spi.ILoggingEvent;
24  import ch.qos.logback.core.Appender;
25  import ch.qos.logback.core.ConsoleAppender;
26  import ch.qos.logback.core.FileAppender;
27  import ch.qos.logback.core.encoder.Encoder;
28  import ch.qos.logback.core.filter.Filter;
29  import java.io.IOException;
30  import java.nio.file.Files;
31  import java.nio.file.Paths;
32  import lombok.Getter;
33  import lombok.extern.slf4j.Slf4j;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * Facade class to the underlying logger to allow the loggers to be configured programmatically.
38   */
39  @Slf4j
40  public final class LoggingFacade {
41  
42      /**
43       * Create the logging facade, by default adding a console logger to output logging info to the screen (console). A
44       * level of 'error' is configured by default, once the configuration file is read the logging level can be changed.
45       */
46      public LoggingFacade() {
47  
48          try {
49              // assume SLF4J is bound to logback in the current environment
50              loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
51              loggerContext.start();
52  
53              rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
54              rootLogger.detachAndStopAllAppenders();
55              rootLogger.setLevel(Level.OFF);
56  
57              // Create the layout pattern for the appenders.
58              addConsoleLogger("error", logFormatThreadMsg);
59  
60          } catch (ClassCastException cce) {
61              rootLogger.error("Apparently SLF4J is not backed by Logback. This is a requirement, thus an internal fault.");
62          }
63      }
64  
65      /**
66       * Add a console logger to the list of loggers of the project. This will replace all the console loggers already
67       * added using this method.
68       * @param level   the logging level to be applied.
69       * @param pattern the pattern for the logger.
70       */
71      public void addConsoleLogger(final String level, final String pattern) {
72  
73          final String consoleName = "Console";
74  
75          // remove the old console appender - it will be called "Console" if it was added using this method.
76          rootLogger.detachAppender(consoleName);
77  
78          final ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
79          appender.setName(consoleName);
80          appender.setEncoder(createPatternLayoutEncoder(pattern));
81          appender.addFilter(createThresholdFilter(level));
82  
83          appender.setContext(loggerContext);
84          appender.start();
85          rootLogger.addAppender((Appender<ILoggingEvent>) appender);
86      }
87  
88      /**
89       * Add a file logger to the list of loggers of the project.
90       * @param file      the name of the file to which the logging messages will be added.
91       * @param level     the logging level to be applied.
92       * @param pattern   the pattern for the logger.
93       * @param overwrite if true, the contents of any previous log are overwritten. If false, the logs are appended.
94       */
95      public void addFileLogger(final String file, final String level, final String pattern, final Boolean overwrite) {
96  
97          // Delete the old log file.
98          try {
99              if (overwrite != null && overwrite.booleanValue()) {
100                 Files.delete(Paths.get(file));
101             }
102         } catch (IOException ex) {
103             log.error("Could not delete old log file; {}", ex.getLocalizedMessage());
104         }
105 
106         final FileAppender<ILoggingEvent> appender = new FileAppender<>();
107         appender.setFile(file);
108         appender.setEncoder(createPatternLayoutEncoder(pattern));
109         appender.addFilter(createThresholdFilter(level));
110 
111         appender.setContext(loggerContext);
112         appender.start();
113         rootLogger.addAppender((Appender<ILoggingEvent>) appender);
114     }
115 
116     /**
117      * Create a pattern layout encoder for a given pattern.
118      * @param pattern the pattern to apply.
119      * @return the created encoder.
120      */
121     private Encoder<ILoggingEvent> createPatternLayoutEncoder(final String pattern) {
122         // Set a default pattern if the supplied pattern is empty
123         final String patt = (pattern == null) ? logFormatThreadMsg : pattern;
124 
125         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
126         encoder.setContext(loggerContext);
127         encoder.setPattern(patt);
128         encoder.start();
129         return (Encoder<ILoggingEvent>) encoder;
130     }
131 
132     /**
133      * Create a thresholdFilter for a given level.
134      * @param level the level to set.
135      * @return the created filter.
136      */
137     private Filter<ILoggingEvent> createThresholdFilter(final String level) {
138         // Set a default level if the supplied level is empty
139         final String lev = (level == null) ? "info" : level;
140         setLoggingLevel(level);
141 
142         final ThresholdFilter filter = new ThresholdFilter();
143         filter.setLevel(lev);
144         filter.start();
145         return (Filter<ILoggingEvent>) filter;
146     }
147 
148     /**
149      * Convert the string passed as argument to a Level. If the conversion fails, then this method returns Level.DEBUG.
150      * @param level the logging level that should be used.
151      */
152     private void setLoggingLevel(final String level) {
153         rootLogger.setLevel(Level.toLevel(level));
154     }
155     @Getter
156     private Logger rootLogger;
157     private LoggerContext loggerContext;
158     private String logFormatThreadMsg = "[%thread] %msg\n";
159 }