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