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
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 530526 2007-04-19 19:16:16Z 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 log.error("Could not init runtime.log.logsystem " + o, e);
87 }
88 }
89 // then check for a LogSystem
90 else if (o instanceof LogSystem)
91 {
92 // inform the user about the deprecation
93 log.info("LogSystem has been deprecated. Please use a LogChute implementation.");
94 try
95 {
96 // wrap the LogSystem into a chute.
97 LogChute chute = new LogChuteSystem((LogSystem)o);
98 chute.init(rsvc);
99 return chute;
100 }
101 catch (Exception e)
102 {
103 log.error("Could not init runtime.log.logsystem " + o, e);
104 }
105 }
106 else
107 {
108 log.warn(o.getClass().getName() + " object set as runtime.log.logsystem is not a valid log implementation.");
109 }
110 }
111
112 /* otherwise, see if a class was specified. You can put multiple
113 * classes, and we use the first one we find.
114 *
115 * Note that the default value of this property contains the
116 * AvalonLogChute, the Log4JLogChute, CommonsLogLogChute,
117 * ServletLogChute, and the JdkLogChute for
118 * convenience - so we use whichever we works first.
119 */
120 List classes = new ArrayList();
121 Object obj = rsvc.getProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS );
122
123 /*
124 * we might have a list, or not - so check
125 */
126 if ( obj instanceof List)
127 {
128 classes = (List) obj;
129 }
130 else if ( obj instanceof String)
131 {
132 classes.add( obj );
133 }
134
135 /*
136 * now run through the list, trying each. It's ok to
137 * fail with a class not found, as we do this to also
138 * search out a default simple file logger
139 */
140 for( Iterator ii = classes.iterator(); ii.hasNext(); )
141 {
142 String claz = (String) ii.next();
143 if (claz != null && claz.length() > 0 )
144 {
145 log.debug("Trying to use logger class " + claz );
146 try
147 {
148 o = ClassUtils.getNewInstance( claz );
149 if (o instanceof LogChute)
150 {
151 ((LogChute)o).init(rsvc);
152 log.debug("Using logger class " + claz);
153 return (LogChute)o;
154 }
155 else if (o instanceof LogSystem)
156 {
157 // inform the user about the deprecation
158 log.info("LogSystem has been deprecated. Please use a LogChute implementation.");
159 LogChute chute = new LogChuteSystem((LogSystem)o);
160 chute.init(rsvc);
161 return chute;
162 }
163 else
164 {
165 log.error("The specified logger class " + claz +
166 " does not implement the "+LogChute.class.getName()+" interface.");
167 // be extra informative if it appears to be a classloader issue
168 // this should match all our provided LogChutes
169 if (isProbablyProvidedLogChute(claz))
170 {
171 // if it's likely to be ours, tip them off about classloader stuff
172 log.error("This appears to be a ClassLoader issue. Check for multiple Velocity jars in your classpath.");
173 }
174 }
175 }
176 catch(NoClassDefFoundError ncdfe)
177 {
178 // note these errors for anyone debugging the app
179 if (isProbablyProvidedLogChute(claz))
180 {
181 log.debug("Target log system for " + claz +
182 " is not available (" + ncdfe.toString() +
183 "). Falling back to next log system...");
184 }
185 else
186 {
187 log.debug("Couldn't find class " + claz +
188 " or necessary supporting classes in classpath.",
189 ncdfe);
190 }
191 }
192 catch(Exception e)
193 {
194 // log init exception at slightly higher priority
195 log.info("Failed to initialize an instance of " + claz +
196 " with the current runtime configuration.", e);
197 }
198 }
199 }
200
201 /* If the above failed, that means either the user specified a
202 * logging class that we can't find, there weren't the necessary
203 * dependencies in the classpath for it, or there were the same
204 * problems for the default loggers, log4j and Java1.4+.
205 * Since we really don't know and we want to be sure the user knows
206 * that something went wrong with the logging, let's fall back to the
207 * surefire SystemLogChute. No panicking or failing to log!!
208 */
209 LogChute slc = new SystemLogChute();
210 slc.init(rsvc);
211 log.debug("Using SystemLogChute.");
212 return slc;
213 }
214
215 /**
216 * Simply tells whether the specified classname probably is provided
217 * by Velocity or is implemented by someone else. Not surefire, but
218 * it'll probably always be right. In any case, this method shouldn't
219 * be relied upon for anything important.
220 */
221 private static boolean isProbablyProvidedLogChute(String claz)
222 {
223 if (claz == null)
224 {
225 return false;
226 }
227 else
228 {
229 return (claz.startsWith("org.apache.velocity.runtime.log")
230 && claz.endsWith("LogChute"));
231 }
232 }
233
234 /**
235 * Update the Log instance with the appropriate LogChute and other
236 * settings determined by the RuntimeServices.
237 * @param log
238 * @param rsvc
239 * @throws Exception
240 */
241 public static void updateLog(Log log, RuntimeServices rsvc) throws Exception
242 {
243 // create a new LogChute using the RuntimeServices
244 LogChute newLogChute = createLogChute(rsvc);
245 LogChute oldLogChute = log.getLogChute();
246
247 // If the old LogChute was the pre-Init logger,
248 // dump its messages into the new system first.
249 if (oldLogChute instanceof HoldingLogChute)
250 {
251 HoldingLogChute hlc = (HoldingLogChute)oldLogChute;
252 hlc.transferTo(newLogChute);
253 }
254
255 // pass the new LogChute to the log
256 log.setLogChute(newLogChute);
257 }
258
259 }
260