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 991708 2010-09-01 21:17:56Z nbubna $
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(UnsupportedOperationException uoe)
201 {
202 // note these errors for anyone debugging the app
203 if (isProbablyProvidedLogChute(claz))
204 {
205 log.debug("Target log system for " + claz +
206 " is not supported (" + uoe.toString() +
207 "). Falling back to next log system...");
208 }
209 else
210 {
211 log.debug("Couldn't find necessary resources for "+claz, uoe);
212 }
213 }
214 catch(Exception e)
215 {
216 String msg = "Failed to initialize an instance of " + claz +
217 " with the current runtime configuration.";
218 // log unexpected init exception at higher priority
219 log.error(msg, e);
220 throw new VelocityException(msg,e);
221 }
222 }
223 }
224
225 /* If the above failed, that means either the user specified a
226 * logging class that we can't find, there weren't the necessary
227 * dependencies in the classpath for it, or there were the same
228 * problems for the default loggers, log4j and Java1.4+.
229 * Since we really don't know and we want to be sure the user knows
230 * that something went wrong with the logging, let's fall back to the
231 * surefire SystemLogChute. No panicking or failing to log!!
232 */
233 LogChute slc = new SystemLogChute();
234 slc.init(rsvc);
235 log.debug("Using SystemLogChute.");
236 return slc;
237 }
238
239 /**
240 * Simply tells whether the specified classname probably is provided
241 * by Velocity or is implemented by someone else. Not surefire, but
242 * it'll probably always be right. In any case, this method shouldn't
243 * be relied upon for anything important.
244 */
245 private static boolean isProbablyProvidedLogChute(String claz)
246 {
247 if (claz == null)
248 {
249 return false;
250 }
251 else
252 {
253 return (claz.startsWith("org.apache.velocity.runtime.log")
254 && claz.endsWith("LogChute"));
255 }
256 }
257
258 /**
259 * Update the Log instance with the appropriate LogChute and other
260 * settings determined by the RuntimeServices.
261 * @param log
262 * @param rsvc
263 * @throws Exception
264 * @since 1.5
265 */
266 public static void updateLog(Log log, RuntimeServices rsvc) throws Exception
267 {
268 // create a new LogChute using the RuntimeServices
269 LogChute newLogChute = createLogChute(rsvc);
270 LogChute oldLogChute = log.getLogChute();
271
272 // pass the new LogChute to the log first,
273 // (if the old was a HoldingLogChute, we don't want it
274 // to accrue new messages during the transfer below)
275 log.setLogChute(newLogChute);
276
277 // If the old LogChute was the pre-Init logger,
278 // dump its messages into the new system.
279 if (oldLogChute instanceof HoldingLogChute)
280 {
281 HoldingLogChute hlc = (HoldingLogChute)oldLogChute;
282 hlc.transferTo(newLogChute);
283 }
284 }
285
286 }
287