View Javadoc

1   package org.lsst.ccs.utilities.logging;
2   
3   
4   import org.lsst.ccs.bootstrap.resources.BootstrapResourceUtils;
5   import org.lsst.ccs.bootstrap.resources.ResourcesUtils;
6   import org.lsst.ccs.utilities.tracers.Tracer;
7   
8   import java.io.ByteArrayInputStream;
9   import java.io.ByteArrayOutputStream;
10  import java.io.IOException;
11  import java.nio.file.FileSystems;
12  import java.nio.file.Files;
13  import java.util.*;
14  import java.util.logging.*;
15  import java.util.logging.Formatter;
16  
17  
18  /**
19   * The role of this class is:
20   * <UL>
21  *     <LI/> to read the logging.properties and offer other codes (namely handlers) access
22   *     to the properties described (a thing not permitted by the standard logManager)
23   *     <LI/>  to be sure that the Handlers are lazily loaded  (the standard LogManager may
24   *     not be able to load the Handlers class due to ClassLoading problems)
25  *     <LI/> to delegate informations to the LogManager so it can behave properly (e.g. transmit
26   *     logging properties, register Loggers)
27  * /UL>
28   * @ImplSpec
29   * Specifications
30   * <BR/>
31   * Instances of this class must be created during load time by the <TT>LogManagement</TT>
32   * the static code must:
33   * <UL>
34   *     <LI/> create an instance of this class (so there is one instance per ClassLoader)
35   *     this instance load the Handlers  if not already done
36   * </UL>
37   * When operating as a code that delegates to the Top Manager
38   * <UL>
39   *     <LI/> the methods that deal with handlers creation should avoid duplications.
40   *     they should query the corresponding <TT>Logger</TT> from the "top" manager and verify
41   *     that the requested Handler is not already registered to this Logger.
42   *     <LI/> static services that require information from the logging.properties should
43   *     be addressed to  the LogManager
44   * </UL>
45   * Technical hurdles:
46   * <UL>
47   *     <LI/>  the "top" manager is the standard manager, there may be many ContextLogManagers
48   *     that delegate to it: what happens when we ask the top manager to read again the properties
49   *     (do we need to "reset" each time), how to know that the manager has all the needed information
50   *     and that it is useless to duplicate configuration?
51   *     <BR/> a special property <TT>ccsLoaded</TT>is  used for this purpose
52   * </UL>
53   *
54   *
55   */
56  public class LogPropertiesLoader {
57      public static final String LOG_PROPS = "/logging.properties";
58      /**
59       * if the property to define a config file is set then the corresponding file
60       * is used for properties otherwise it is "LOG_PROPS"
61       */
62      public static final String LOG_CONF_FILE = System.getProperty("java.util.logging.config.file", LOG_PROPS);
63  
64  
65      // ------------------------------------------------------------Constructors
66      /**
67       * in fact always the standard LogManager (if no parent ClassLoader does not decide otherwise)
68       */
69      static LogManager systemLoaderLogManager;
70  
71      Properties loggingProperties ;
72  
73      public LogPropertiesLoader() {
74          systemLoaderLogManager = LogManager.getLogManager() ;
75          /* */
76          readInitialConfiguration();
77          /**/ 
78          
79  
80      }
81  
82  
83      /**
84       * this method guarantees that the LogProperties Loader has been initialized
85       *  (in the context of the current ClassLoader)
86       * @return
87       * //TODO: returning an Optional object is pointless : change!
88       */
89      public static org.lsst.ccs.utilities.beanutils.Optional<LogManager> getSystemLoaderLogManager() {
90          org.lsst.ccs.utilities.beanutils.Optional<LogManager> res = org.lsst.ccs.utilities.beanutils.Optional.empty();
91          try {
92              if (systemLoaderLogManager == null) {
93  
94                  Class clazz = ClassLoader.getSystemClassLoader().loadClass("org.lsst.ccs.utilities.logging" +
95                          ".LogPropertiesLoader");
96                  clazz.newInstance() ;
97              }
98          } catch (Exception exc) {
99              System.err.println(" ERROR " + exc);
100         }
101          res = org.lsst.ccs.utilities.beanutils.Optional.ofNullable(systemLoaderLogManager);
102         return res;
103     }
104 
105 
106     ///////////////// INITIALIZATION METHODS
107 
108 
109     /**
110      * invoked by constructor (that should be itself invoked during load time).
111      * since this method will be called each time a new ClassLoader fires load time and
112      * that we decided there should be only one logging context when CCS classes are loaded
113      * then the standard LogManager should be initialized once.
114      * for this purpose we create a dummy property and we check each time if this property is present.
115      */
116     void readInitialConfiguration() {
117 
118         String resourceName =  LOG_CONF_FILE ;
119         String dummyCheckProperty= "ccsLoaded" ;
120         if( null != systemLoaderLogManager.getProperty(dummyCheckProperty))  {
121             return ;
122         }
123         loggingProperties = ResourcesUtils.getFlatPropertiesObject(BootstrapResourceUtils.getBootstrapProperties(resourceName, this.getClass()));
124         loggingProperties.setProperty(dummyCheckProperty, "true") ;
125 
126         assert Tracer.trace(loggingProperties.toString()) ;
127 
128         //HERE DO something for files
129         Set<String> set = loggingProperties.stringPropertyNames();
130         for (String key : set) {
131             //TODO: reinforce this code : just ending by .pattern may be prone to bugs
132             if (key.endsWith(".pattern")) {
133                 String pattern = loggingProperties.getProperty(key);
134                 try {
135                     pattern = checkForFile(pattern);
136                     loggingProperties.put(key, pattern) ;
137                 } catch (IOException e) {
138                     System.err.println("problem while creating log directory " + e);
139                 }
140                 break;
141             }
142         }
143 
144         ByteArrayOutputStream bos = new ByteArrayOutputStream();
145         try {
146             loggingProperties.store(bos, "");
147             byte[] bytes = bos.toByteArray();
148             ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
149             systemLoaderLogManager.readConfiguration(bis);
150             handlersLazyLoad();
151         } catch (IOException e) {
152             System.err.println(" can't load properties for LogManager" + e);
153         }
154     }
155 
156 
157     /**
158      * initialize the Handlers that are in the current ClassLoader
159      * the "top" manager may not have access to the Handlers classes.
160      * this method should be called only once.
161      * @throws IOException
162      */
163     protected void handlersLazyLoad() throws IOException {
164         Enumeration<String> loggerNames = systemLoaderLogManager.getLoggerNames() ;
165         while(loggerNames.hasMoreElements()) {
166             String loggerName = loggerNames.nextElement() ;
167             //System.out.println(" found logger : " + loggerName);
168             java.util.logging.Logger logger = systemLoaderLogManager.getLogger(loggerName) ;
169             if(logger != null) {
170                 loadLoggerHandlers(loggerName, logger);
171             }
172 
173         }
174     }
175 
176     /**
177      * add Handlers to a Logger known to the LogManager.
178      * @ImplNote
179      * if the Handler is already registered it is not registered again
180      *
181      * @param loggerName
182      * @param logger
183      * @throws IOException
184      */
185     protected void loadLoggerHandlers(String loggerName, java.util.logging.Logger logger)
186             throws IOException {
187 
188         Handler[] logHandlers = logger.getHandlers();
189         Set<String> currentHandlersNames = new HashSet<>() ;
190         for(Handler handler : logHandlers) {
191             currentHandlersNames.add(handler.getClass().getName()) ;
192         }
193         // Create handlers for the root logger of this classloader
194         String handlers = loggingProperties.getProperty(loggerName +".handlersN");
195         if(handlers == null && "".equals(loggerName)) {
196             handlers = loggingProperties.getProperty("handlersN");
197         }
198         if (handlers != null) {
199             StringTokenizer tok = new StringTokenizer(handlers, ",");
200             while (tok.hasMoreTokens()) {
201                 String handlerName = (tok.nextToken().trim());
202                 String handlerClassName = handlerName;
203                 String prefix = "";
204                 if (handlerClassName.length() <= 0) {
205                     continue;
206                 }
207                 // Parse and remove a prefix (prefix start with a digit, such as
208                 // "10WebappFooHanlder.")
209                 if (Character.isDigit(handlerClassName.charAt(0))) {
210                     int pos = handlerClassName.indexOf('.');
211                     if (pos >= 0) {
212                         prefix = handlerClassName.substring(0, pos + 1);
213                         handlerClassName = handlerClassName.substring(pos + 1);
214                     }
215                 }
216                 if(currentHandlersNames.contains(handlerClassName)) {
217                     continue ;
218                 }
219                 try {
220                     ClassLoader classLoader = this.getClass().getClassLoader() ;
221                     Handler handler =
222                             (Handler) classLoader.loadClass(handlerClassName).newInstance();
223                     // The specification strongly implies all configuration should be done
224                     // during the creation of the handler object.
225                     // This includes setting level, filter, formatter and encoding.
226                     configureHandler(handlerName, handler);
227                     //handlerMap.put(handlerName, handler);
228                     logger.addHandler(handler);
229                 } catch (Exception e) {
230                     // Report error
231                     System.err.println("Handler error");
232                     e.printStackTrace();
233                 }
234             }
235 
236         }
237         //TODO: this was added .... is it relevant?
238         setLevelOnExistingLogger(loggerName, logger) ;
239 
240     }
241 
242 
243     /**
244      * should set level, formatter, filter
245      *
246      * @param handlerName
247      * @param handler
248      */
249     protected void configureHandler(String handlerName, Handler handler) {
250         Level level = getLevelProperty(handlerName + ".level", null);
251         if (level == null) {
252             level = getLevelProperty(".level", Level.OFF);
253         }        handler.setLevel(level);
254         Filter filter = getFilterProperty(handlerName + ".filter", null);
255         if (filter == null) {
256             filter = getFilterProperty(".filter", null);
257         }
258         if (filter != null) {
259             handler.setFilter(filter);
260         }
261         java.util.logging.Formatter formatter = getFormatterProperty(handlerName + ".formatter", new SimpleFormatter());
262         handler.setFormatter(formatter);
263     }
264 
265 
266 
267     /**
268      * System property replacement in the given string.
269      *
270      * @param str The original string
271      * @return the modified string
272      */
273     protected String replace(String str, Properties props) {
274         String result = str;
275         int pos_start = result.indexOf("${");
276         if (pos_start != -1) {
277             int pos_end = result.indexOf('}');
278             if (pos_end != -1) {
279                 String propName = result.substring(pos_start + 2, pos_end);
280                 String replacement = props.getProperty(propName);
281                 if (replacement != null) {
282                     if (pos_start > 0) {
283                         result = result.substring(0, pos_start) +
284                                 replacement + replace(result.substring(pos_end + 1), props);
285                     } else {
286                         result = replacement + replace(result.substring(pos_end + 1), props);
287                     }
288                 }
289             }
290         }
291         return result;
292     }
293 
294 
295 // ---------------------------------------------------- LogNode Inner Class
296 
297 
298 
299     /**
300      * read the file pattern until last  separator (this will give the directory)
301      * then will try to create missing directories.
302      *
303      * @param filePattern
304      */
305     String checkForFile(String filePattern) throws IOException {
306         //our own specifications
307         Properties bootstrapSystemProperties = BootstrapResourceUtils.getBootstrapSystemProperties();
308         if (filePattern.contains("%W")) {
309             String logdir = bootstrapSystemProperties.getProperty("org.lsst.ccs.workdir","%h") ;
310             filePattern = filePattern.replace("%W", logdir);
311         }
312         if (filePattern.contains("%L")) {
313             String logdir = bootstrapSystemProperties.getProperty("org.lsst.ccs.logdir","%h") ;
314             filePattern = filePattern.replace("%L", logdir);
315         }
316         if (filePattern.contains("%A")) {
317             String applicationName = bootstrapSystemProperties.getProperty("org.lsst.ccs.application.name","%u") ;
318             filePattern = filePattern.replace("%A", applicationName);
319         }
320 
321         int lastSlash = filePattern.lastIndexOf('/');
322         if (lastSlash >= 0) {
323             String directorySpec = filePattern.substring(0, lastSlash);
324 
325             if (directorySpec.contains("%h")) {
326                 directorySpec = directorySpec.replace("%h", System.getProperty("user.home"));
327             } else if (directorySpec.contains("%t")) {
328                 directorySpec = directorySpec.replace("%t", System.getProperty("java.io.tmpdir"));
329             }
330             Files.createDirectories(FileSystems.getDefault().getPath(directorySpec));
331         }
332         return filePattern ;
333     }
334 
335     ///////////////// END INITIALISATION
336     public static String loaderGetProperty(String name) {
337         getSystemLoaderLogManager();
338         try {
339             String res = systemLoaderLogManager.getProperty(name) ;
340             return res;
341         } catch (Exception e) {
342             System.err.println(" loaderGetProperty :" + e);
343             return null;
344         }
345 
346     }
347     public static String loaderGetStringProperty(String name, String defaultValue) {
348         String val = loaderGetProperty(name);
349         if (val == null) {
350             return defaultValue;
351         }
352         return val.trim();
353     }
354     public static int loaderGetIntProperty(String name, int defaultValue) {
355 
356         String val = loaderGetProperty(name);
357         if (val == null) {
358             return defaultValue;
359         }
360         try {
361             return Integer.parseInt(val.trim());
362         } catch (Exception ex) {
363             return defaultValue;
364         }
365     }
366 
367 
368     public static Level loaderGetLevelProperty(String name, Level defaultValue) {
369         String val = loaderGetProperty(name);
370         if (val == null) {
371             return defaultValue;
372         }
373         try {
374             return Level.parse(val.trim());
375         } catch (Exception ex) {
376             return defaultValue;
377         }
378 
379     }
380 
381 
382 
383 
384 
385 
386     public static Formatter loaderGetFormatterProperty(String name, Formatter defaultValue) {
387 
388         String val = loaderGetProperty(name);
389         try {
390             if (val != null) {
391                 //Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
392                 Class clazz = LogPropertiesLoader.class.getClassLoader().loadClass(val) ;
393                 return (Formatter) clazz.newInstance();
394             }
395         } catch (Exception ex) {
396             // We got one of a variety of exceptions in creating the
397             // class or creating an instance.
398             // Drop through.
399         }
400         // We got an exception.  Return the defaultValue.
401         return defaultValue;
402     }
403 
404 
405 
406 
407 
408 
409     synchronized private void setLevelOnExistingLogger(String loggerName, java.util.logging.Logger logger) {
410         Level level = getLevelProperty(loggerName + ".level", null) ;
411         //LSSTCCS-296
412         if ( level != null ) {
413             logger.setLevel(level);
414         }
415     }
416 
417     public String getProperty(String property) {
418         return systemLoaderLogManager.getProperty(property) ;
419     }
420 
421     
422     //  method to get a String property.
423     // If the property is not defined we return the given
424     // default value.
425     public String getStringProperty(String name, String defaultValue) {
426         String val = getProperty(name);
427         if (val == null) {
428             return defaultValue;
429         }
430         return val.trim();
431     }
432 
433 
434     // If the property is not defined or cannot be parsed
435     // we return the given default value.
436     public int getIntProperty(String name, int defaultValue) {
437         String val = getProperty(name);
438         if (val == null) {
439             return defaultValue;
440         }
441         try {
442             return Integer.parseInt(val.trim());
443         } catch (Exception ex) {
444             return defaultValue;
445         }
446     }
447 
448 
449     // If the property is not defined or cannot be parsed
450     // we return the given default value.
451     public boolean getBooleanProperty(String name, boolean defaultValue) {
452         String val = getProperty(name);
453         if (val == null) {
454             return defaultValue;
455         }
456         val = val.toLowerCase();
457         if (val.equals("true") || val.equals("1")) {
458             return true;
459         } else if (val.equals("false") || val.equals("0")) {
460             return false;
461         }
462         return defaultValue;
463     }
464 
465     // we return the given default value.
466     public Level getLevelProperty(String name, Level defaultValue) {
467         String val = getProperty(name);
468         if (val == null) {
469             return defaultValue;
470         }
471         try {
472             return Level.parse(val.trim());
473         } catch (Exception ex) {
474             return defaultValue;
475         }
476     }
477 
478 
479     //  method to get a filter property.
480     // We return an instance of the class named by the "name"
481     // property. If the property is not defined or has problems
482     // we return the defaultValue.
483     public Filter getFilterProperty(String name, Filter defaultValue) {
484         String val = getProperty(name);
485         try {
486             if (val != null) {
487                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
488                 return (Filter) clz.newInstance();
489             }
490         } catch (Exception ex) {
491             // We got one of a variety of exceptions in creating the
492             // class or creating an instance.
493             // Drop through.
494         }
495         // We got an exception.  Return the defaultValue.
496         return defaultValue;
497     }
498 
499 
500     //  method to get a formatter property.
501     // We return an instance of the class named by the "name"
502     // property. If the property is not defined or has problems
503     // we return the defaultValue.
504     public Formatter getFormatterProperty(String name, Formatter defaultValue) {
505         String val = getProperty(name);
506         try {
507             if (val != null) {
508                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
509                 return (Formatter) clz.newInstance();
510             }
511         } catch (Exception ex) {
512             // We got one of a variety of exceptions in creating the
513             // class or creating an instance.
514             // Drop through.
515         }
516         // We got an exception.  Return the defaultValue.
517         return defaultValue;
518     }
519 
520 
521 
522 }
523 
524 
525 
526 
527