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 730039 2008-12-30 03:53:19Z byron $
40 * @since Velocity 1.5
41 * @since 1.5
42 */
43 public class Log4JLogChute implements LogChute
44 {
45 public static final String RUNTIME_LOG_LOG4J_LOGGER =
46 "runtime.log.logsystem.log4j.logger";
47 public static final String RUNTIME_LOG_LOG4J_LOGGER_LEVEL =
48 "runtime.log.logsystem.log4j.logger.level";
49
50 private RuntimeServices rsvc = null;
51 private boolean hasTrace = false;
52 private RollingFileAppender appender = null;
53
54 /**
55 * <a href="http://jakarta.apache.org/log4j/">Log4J</a> logging API.
56 */
57 protected Logger logger = null;
58
59 /**
60 * @see org.apache.velocity.runtime.log.LogChute#init(org.apache.velocity.runtime.RuntimeServices)
61 */
62 public void init(RuntimeServices rs) throws Exception
63 {
64 rsvc = rs;
65
66 /* first see if there is a category specified and just use that - it allows
67 * the application to make us use an existing logger
68 */
69 String name = (String)rsvc.getProperty(RUNTIME_LOG_LOG4J_LOGGER);
70 if (name != null)
71 {
72 logger = Logger.getLogger(name);
73 log(DEBUG_ID, "Log4JLogChute using logger '" + name + '\'');
74 }
75 else
76 {
77 // create a logger with this class name to avoid conflicts
78 logger = Logger.getLogger(this.getClass().getName());
79
80 // if we have a file property, then create a separate
81 // rolling file log for velocity messages only
82 String file = rsvc.getString(RuntimeConstants.RUNTIME_LOG);
83 if (file != null && file.length() > 0)
84 {
85 initAppender(file);
86 }
87 }
88
89 /* get and set specified level for this logger */
90 String lvl = rsvc.getString(RUNTIME_LOG_LOG4J_LOGGER_LEVEL);
91 if (lvl != null)
92 {
93 Level level = Level.toLevel(lvl);
94 logger.setLevel(level);
95 }
96
97 /* Ok, now let's see if this version of log4j supports the trace level. */
98 try
99 {
100 Field traceLevel = Level.class.getField("TRACE");
101 // we'll never get here in pre 1.2.12 log4j
102 hasTrace = true;
103 }
104 catch (NoSuchFieldException e)
105 {
106 log(DEBUG_ID,
107 "The version of log4j being used does not support the \"trace\" level.");
108 }
109 }
110
111 // This tries to create a file appender for the specified file name.
112 private void initAppender(String file) throws Exception
113 {
114 try
115 {
116 // to add the appender
117 PatternLayout layout = new PatternLayout("%d - %m%n");
118 this.appender = new RollingFileAppender(layout, file, true);
119
120 // if we successfully created the file appender,
121 // configure it and set the logger to use only it
122 appender.setMaxBackupIndex(1);
123 appender.setMaximumFileSize(100000);
124
125 // don't inherit appenders from higher in the logger heirarchy
126 logger.setAdditivity(false);
127 logger.addAppender(appender);
128 log(DEBUG_ID, "Log4JLogChute initialized using file '"+file+'\'');
129 }
130 catch (IOException ioe)
131 {
132 rsvc.getLog().error("Could not create file appender '"+file+'\'', ioe);
133 throw ExceptionUtils.createRuntimeException("Error configuring Log4JLogChute : ", ioe);
134 }
135 }
136
137 /**
138 * logs messages
139 *
140 * @param level severity level
141 * @param message complete error message
142 */
143 public void log(int level, String message)
144 {
145 switch (level)
146 {
147 case LogChute.WARN_ID:
148 logger.warn(message);
149 break;
150 case LogChute.INFO_ID:
151 logger.info(message);
152 break;
153 case LogChute.TRACE_ID:
154 if (hasTrace)
155 {
156 logger.trace(message);
157 }
158 else
159 {
160 logger.debug(message);
161 }
162 break;
163 case LogChute.ERROR_ID:
164 logger.error(message);
165 break;
166 case LogChute.DEBUG_ID:
167 default:
168 logger.debug(message);
169 break;
170 }
171 }
172
173 /**
174 * @see org.apache.velocity.runtime.log.LogChute#log(int, java.lang.String, java.lang.Throwable)
175 */
176 public void log(int level, String message, Throwable t)
177 {
178 switch (level)
179 {
180 case LogChute.WARN_ID:
181 logger.warn(message, t);
182 break;
183 case LogChute.INFO_ID:
184 logger.info(message, t);
185 break;
186 case LogChute.TRACE_ID:
187 if (hasTrace)
188 {
189 logger.trace(message, t);
190 }
191 else
192 {
193 logger.debug(message, t);
194 }
195 break;
196 case LogChute.ERROR_ID:
197 logger.error(message, t);
198 break;
199 case LogChute.DEBUG_ID:
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 }