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.io.IOException;
23 import java.lang.reflect.Field;
24 import org.apache.log4j.Level;
25 import org.apache.log4j.Logger;
26 import org.apache.log4j.PatternLayout;
27 import org.apache.log4j.RollingFileAppender;
28 import org.apache.velocity.runtime.RuntimeConstants;
29 import org.apache.velocity.runtime.RuntimeServices;
30 import org.apache.velocity.util.ExceptionUtils;
31
32 /**
33 * Implementation of a simple log4j system that will either latch onto
34 * an existing category, or just do a simple rolling file log.
35 *
36 * @author <a href="mailto:geirm@apache.org>Geir Magnusson Jr.</a>
37 * @author <a href="mailto:dlr@finemaltcoding.com>Daniel L. Rall</a>
38 * @author <a href="mailto:nbubna@apache.org>Nathan Bubna</a>
39 * @version $Id: Log4JLogChute.java 522566 2007-03-26 16:42:00Z nbubna $
40 * @since Velocity 1.5
41 */
42 public class Log4JLogChute implements LogChute
43 {
44 public static final String RUNTIME_LOG_LOG4J_LOGGER =
45 "runtime.log.logsystem.log4j.logger";
46 public static final String RUNTIME_LOG_LOG4J_LOGGER_LEVEL =
47 "runtime.log.logsystem.log4j.logger.level";
48
49 private RuntimeServices rsvc = null;
50 private boolean hasTrace = false;
51 private RollingFileAppender appender = null;
52
53 /**
54 * <a href="http://jakarta.apache.org/log4j/">Log4J</a> logging API.
55 */
56 protected Logger logger = null;
57
58 /**
59 * @see org.apache.velocity.runtime.log.LogChute#init(org.apache.velocity.runtime.RuntimeServices)
60 */
61 public void init(RuntimeServices rs) throws Exception
62 {
63 rsvc = rs;
64
65 /* first see if there is a category specified and just use that - it allows
66 * the application to make us use an existing logger
67 */
68 String name = (String)rsvc.getProperty(RUNTIME_LOG_LOG4J_LOGGER);
69 if (name != null)
70 {
71 logger = Logger.getLogger(name);
72 log(DEBUG_ID, "Log4JLogChute using logger '" + name + '\'');
73 }
74 else
75 {
76 // create a logger with this class name to avoid conflicts
77 logger = Logger.getLogger(this.getClass().getName());
78
79 // if we have a file property, then create a separate
80 // rolling file log for velocity messages only
81 String file = rsvc.getString(RuntimeConstants.RUNTIME_LOG);
82 if (file != null && file.length() > 0)
83 {
84 initAppender(file);
85 }
86 }
87
88 /* get and set specified level for this logger, default to WARN */
89 String lvl = rsvc.getString(RUNTIME_LOG_LOG4J_LOGGER_LEVEL, "WARN");
90 Level level = Level.toLevel(lvl);
91 logger.setLevel(level);
92
93 /* Ok, now let's see if this version of log4j supports the trace level. */
94 try
95 {
96 Field traceLevel = Level.class.getField("TRACE");
97 // we'll never get here in pre 1.2.12 log4j
98 hasTrace = true;
99 }
100 catch (NoSuchFieldException e)
101 {
102 log(DEBUG_ID,
103 "The version of log4j being used does not support the \"trace\" level.");
104 }
105 }
106
107 // This tries to create a file appender for the specified file name.
108 private void initAppender(String file) throws Exception
109 {
110 try
111 {
112 // to add the appender
113 PatternLayout layout = new PatternLayout("%d - %m%n");
114 this.appender = new RollingFileAppender(layout, file, true);
115
116 // if we successfully created the file appender,
117 // configure it and set the logger to use only it
118 appender.setMaxBackupIndex(1);
119 appender.setMaximumFileSize(100000);
120
121 // don't inherit appenders from higher in the logger heirarchy
122 logger.setAdditivity(false);
123 logger.addAppender(appender);
124 log(DEBUG_ID, "Log4JLogChute initialized using file '"+file+'\'');
125 }
126 catch (IOException ioe)
127 {
128 rsvc.getLog().warn("Could not create file appender '"+file+'\'', ioe);
129 throw ExceptionUtils.createRuntimeException("Error configuring Log4JLogChute : ", ioe);
130 }
131 }
132
133 /**
134 * logs messages
135 *
136 * @param level severity level
137 * @param message complete error message
138 */
139 public void log(int level, String message)
140 {
141 switch (level)
142 {
143 case LogChute.WARN_ID:
144 logger.warn(message);
145 break;
146 case LogChute.INFO_ID:
147 logger.info(message);
148 break;
149 case LogChute.DEBUG_ID:
150 logger.debug(message);
151 break;
152 case LogChute.TRACE_ID:
153 if (hasTrace)
154 {
155 logger.trace(message);
156 }
157 else
158 {
159 logger.debug(message);
160 }
161 break;
162 case LogChute.ERROR_ID:
163 logger.error(message);
164 break;
165 default:
166 logger.debug(message);
167 break;
168 }
169 }
170
171 /**
172 * @see org.apache.velocity.runtime.log.LogChute#log(int, java.lang.String, java.lang.Throwable)
173 */
174 public void log(int level, String message, Throwable t)
175 {
176 switch (level)
177 {
178 case LogChute.WARN_ID:
179 logger.warn(message, t);
180 break;
181 case LogChute.INFO_ID:
182 logger.info(message, t);
183 break;
184 case LogChute.DEBUG_ID:
185 logger.debug(message, t);
186 break;
187 case LogChute.TRACE_ID:
188 if (hasTrace)
189 {
190 logger.trace(message, t);
191 }
192 else
193 {
194 logger.debug(message, t);
195 }
196 break;
197 case LogChute.ERROR_ID:
198 logger.error(message, t);
199 break;
200 default:
201 logger.debug(message, t);
202 break;
203 }
204 }
205
206 /**
207 * @see org.apache.velocity.runtime.log.LogChute#isLevelEnabled(int)
208 */
209 public boolean isLevelEnabled(int level)
210 {
211 switch (level)
212 {
213 case LogChute.DEBUG_ID:
214 return logger.isDebugEnabled();
215 case LogChute.INFO_ID:
216 return logger.isInfoEnabled();
217 case LogChute.TRACE_ID:
218 if (hasTrace)
219 {
220 return logger.isTraceEnabled();
221 }
222 else
223 {
224 return logger.isDebugEnabled();
225 }
226 case LogChute.WARN_ID:
227 return logger.isEnabledFor(Level.WARN);
228 case LogChute.ERROR_ID:
229 // can't be disabled in log4j
230 return logger.isEnabledFor(Level.ERROR);
231 default:
232 return true;
233 }
234 }
235
236 /**
237 * Also do a shutdown if the object is destroy()'d.
238 * @throws Throwable
239 */
240 protected void finalize() throws Throwable
241 {
242 shutdown();
243 }
244
245 /** Close all destinations*/
246 public void shutdown()
247 {
248 if (appender != null)
249 {
250 logger.removeAppender(appender);
251 appender.close();
252 appender = null;
253 }
254 }
255
256 }