View Javadoc

1   package org.apache.velocity.runtime.log;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.util.List;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import org.apache.velocity.exception.VelocityException;
26  import org.apache.velocity.runtime.RuntimeServices;
27  import org.apache.velocity.runtime.RuntimeConstants;
28  import org.apache.velocity.util.ClassUtils;
29  
30  /**
31   * <p>
32   * This class is responsible for instantiating the correct LogChute
33   * </p>
34   *
35   * <p>
36   * The approach is :
37   * </p>
38   * <ul>
39   * <li>
40   *      First try to see if the user is passing in a living object
41   *      that is a LogChute, allowing the app to give its living
42   *      custom loggers.
43   *  </li>
44   *  <li>
45   *       Next, run through the (possible) list of classes specified
46   *       specified as loggers, taking the first one that appears to
47   *       work.  This is how we support finding logkit, log4j or
48   *       jdk logging, whichever is in the classpath and found first,
49   *       as all three are listed as defaults.
50   *  </li>
51   *  <li>
52   *      Finally, we turn to the System.err stream and print log messages
53   *      to it if nothing else works.
54   *  </li>
55   *
56   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
57   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
58   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
59   * @author <a href="mailto:nbubna@apache.org">Nathan Bubna</a>
60   * @version $Id: LogManager.java 699307 2008-09-26 13:16:05Z cbrisson $
61   */
62  public class LogManager
63  {
64      // Creates a new logging system or returns an existing one
65      // specified by the application.
66      private static LogChute createLogChute(RuntimeServices rsvc) throws Exception
67      {
68          Log log = rsvc.getLog();
69  
70          /* If a LogChute or LogSystem instance was set as a configuration
71           * value, use that.  This is any class the user specifies.
72           */
73          Object o = rsvc.getProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM);
74          if (o != null)
75          {
76              // first check for a LogChute
77              if (o instanceof LogChute)
78              {
79                  try
80                  {
81                      ((LogChute)o).init(rsvc);
82                      return (LogChute)o;
83                  }
84                  catch (Exception e)
85                  {
86                      String msg = "Could not init runtime.log.logsystem " + o;
87                      log.error(msg, e);
88                      throw new VelocityException(msg, e);
89                  }
90              }
91              // then check for a LogSystem
92              else if (o instanceof LogSystem)
93              {
94                  // inform the user about the deprecation
95                  log.debug("LogSystem has been deprecated. Please use a LogChute implementation.");
96                  try
97                  {
98                      // wrap the LogSystem into a chute.
99                      LogChute chute = new LogChuteSystem((LogSystem)o);
100                     chute.init(rsvc);
101                     return chute;
102                 }
103                 catch (Exception e)
104                 {
105                     String msg = "Could not init runtime.log.logsystem " + o;
106                     log.error(msg, e);
107                     throw new VelocityException(msg, e);
108                 }
109             }
110             else
111             {
112                 String msg = o.getClass().getName() + " object set as runtime.log.logsystem is not a valid log implementation.";
113                 log.error(msg);
114                 throw new VelocityException(msg);
115             }
116         }
117 
118         /* otherwise, see if a class was specified.  You can put multiple
119          * classes, and we use the first one we find.
120          *
121          * Note that the default value of this property contains the
122          * AvalonLogChute, the Log4JLogChute, CommonsLogLogChute,
123          * ServletLogChute, and the JdkLogChute for
124          * convenience - so we use whichever we works first.
125          */
126         List classes = new ArrayList();
127         Object obj = rsvc.getProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS );
128 
129         /*
130          *  we might have a list, or not - so check
131          */
132         if ( obj instanceof List)
133         {
134             classes = (List) obj;
135         }
136         else if ( obj instanceof String)
137         {
138             classes.add( obj );
139         }
140 
141         /*
142          *  now run through the list, trying each.  It's ok to
143          *  fail with a class not found, as we do this to also
144          *  search out a default simple file logger
145          */
146         for( Iterator ii = classes.iterator(); ii.hasNext(); )
147         {
148             String claz = (String) ii.next();
149             if (claz != null && claz.length() > 0 )
150             {
151                 log.debug("Trying to use logger class " + claz );
152                 try
153                 {
154                     o = ClassUtils.getNewInstance( claz );
155                     if (o instanceof LogChute)
156                     {
157                         ((LogChute)o).init(rsvc);
158                         log.debug("Using logger class " + claz);
159                         return (LogChute)o;
160                     }
161                     else if (o instanceof LogSystem)
162                     {
163                         // inform the user about the deprecation
164                         log.debug("LogSystem has been deprecated. Please use a LogChute implementation.");
165                         LogChute chute = new LogChuteSystem((LogSystem)o);
166                         chute.init(rsvc);
167                         return chute;
168                     }
169                     else
170                     {
171                         String msg = "The specified logger class " + claz +
172                                      " does not implement the "+LogChute.class.getName()+" interface.";
173                         log.error(msg);
174                         // be extra informative if it appears to be a classloader issue
175                         // this should match all our provided LogChutes
176                         if (isProbablyProvidedLogChute(claz))
177                         {
178                             // if it's likely to be ours, tip them off about classloader stuff
179                             log.error("This appears to be a ClassLoader issue.  Check for multiple Velocity jars in your classpath.");
180                         }
181                         throw new VelocityException(msg);
182                     }
183                 }
184                 catch(NoClassDefFoundError ncdfe)
185                 {
186                     // note these errors for anyone debugging the app
187                     if (isProbablyProvidedLogChute(claz))
188                     {
189                         log.debug("Target log system for " + claz +
190                                   " is not available (" + ncdfe.toString() +
191                                   ").  Falling back to next log system...");
192                     }
193                     else
194                     {
195                         log.debug("Couldn't find class " + claz +
196                                   " or necessary supporting classes in classpath.",
197                                   ncdfe);
198                     }
199                 }
200                 catch(Exception e)
201                 {
202                     String msg = "Failed to initialize an instance of " + claz +
203                                  " with the current runtime configuration.";
204                     // log unexpected init exception at higher priority
205                     log.error(msg, e);
206                     throw new VelocityException(msg,e);
207                 }
208             }
209         }
210 
211         /* If the above failed, that means either the user specified a
212          * logging class that we can't find, there weren't the necessary
213          * dependencies in the classpath for it, or there were the same
214          * problems for the default loggers, log4j and Java1.4+.
215          * Since we really don't know and we want to be sure the user knows
216          * that something went wrong with the logging, let's fall back to the
217          * surefire SystemLogChute. No panicking or failing to log!!
218          */
219         LogChute slc = new SystemLogChute();
220         slc.init(rsvc);
221         log.debug("Using SystemLogChute.");
222         return slc;
223     }
224 
225     /**
226      * Simply tells whether the specified classname probably is provided
227      * by Velocity or is implemented by someone else.  Not surefire, but
228      * it'll probably always be right.  In any case, this method shouldn't
229      * be relied upon for anything important.
230      */
231     private static boolean isProbablyProvidedLogChute(String claz)
232     {
233         if (claz == null)
234         {
235             return false;
236         }
237         else
238         {
239             return (claz.startsWith("org.apache.velocity.runtime.log")
240                     && claz.endsWith("LogChute"));
241         }
242     }
243 
244     /**
245      * Update the Log instance with the appropriate LogChute and other
246      * settings determined by the RuntimeServices.
247      * @param log
248      * @param rsvc
249      * @throws Exception
250      * @since 1.5
251      */
252     public static void updateLog(Log log, RuntimeServices rsvc) throws Exception
253     {
254         // create a new LogChute using the RuntimeServices
255         LogChute newLogChute = createLogChute(rsvc);
256         LogChute oldLogChute = log.getLogChute();
257 
258         // pass the new LogChute to the log first,
259         // (if the old was a HoldingLogChute, we don't want it
260         //  to accrue new messages during the transfer below)
261         log.setLogChute(newLogChute);
262 
263         // If the old LogChute was the pre-Init logger,
264         // dump its messages into the new system.
265         if (oldLogChute instanceof HoldingLogChute)
266         {
267             HoldingLogChute hlc = (HoldingLogChute)oldLogChute;
268             hlc.transferTo(newLogChute);
269         }
270     }
271 
272 }
273