001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jorphan.logging;
020:
021: import java.io.FileWriter;
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.io.Writer;
025: import java.text.SimpleDateFormat;
026: import java.util.Date;
027: import java.util.Iterator;
028: import java.util.Properties;
029:
030: import org.apache.avalon.excalibur.logger.LogKitLoggerManager;
031: import org.apache.avalon.framework.configuration.Configuration;
032: import org.apache.avalon.framework.configuration.ConfigurationException;
033: import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
034: import org.apache.avalon.framework.context.Context;
035: import org.apache.avalon.framework.context.ContextException;
036: import org.apache.avalon.framework.context.DefaultContext;
037: import org.apache.jorphan.util.ClassContext;
038: import org.apache.log.Hierarchy;
039: import org.apache.log.LogTarget;
040: import org.apache.log.Logger;
041: import org.apache.log.Priority;
042: import org.apache.log.format.PatternFormatter;
043: import org.apache.log.output.NullOutputLogTarget;
044: import org.apache.log.output.io.WriterTarget;
045: import org.xml.sax.SAXException;
046:
047: /**
048: * Manages JMeter logging
049: */
050: public final class LoggingManager {
051: // N.B time pattern is passed to java.text.SimpleDateFormat
052: /*
053: * Predefined format patterns, selected by the property log_format_type (see
054: * jmeter.properties) The new-line is added later
055: */
056: private static final String DEFAULT_PATTERN = "%{time:yyyy/MM/dd HH:mm:ss} %5.5{priority} - "
057: + "%{category}: %{message} %{throwable}";
058:
059: private static final String PATTERN_THREAD_PREFIX = "%{time:yyyy/MM/dd HH:mm:ss} %5.5{priority} "
060: + "%12{thread} %{category}: %{message} %{throwable}";
061:
062: private static final String PATTERN_THREAD_SUFFIX = "%{time:yyyy/MM/dd HH:mm:ss} %5.5{priority} "
063: + "%{category}[%{thread}]: %{message} %{throwable}";
064:
065: private static PatternFormatter format = null;
066:
067: /** Used to hold the default logging target. */
068: private static LogTarget target = new NullOutputLogTarget();
069:
070: // Hack to detect when System.out has been set as the target, to avoid closing it
071: private static boolean isTargetSystemOut = false;// Is the target System.out?
072:
073: private static boolean isWriterSystemOut = false;// Is the Writer System.out?
074:
075: public final static String LOG_FILE = "log_file";
076:
077: public final static String LOG_PRIORITY = "log_level";
078:
079: private static LoggingManager logManager = null;
080:
081: private LoggingManager() {
082: }
083:
084: public static LoggingManager getLogManager() {
085: return logManager;
086: }
087:
088: /**
089: * Initialise the logging system from the Jmeter properties. Logkit loggers
090: * inherit from their parents.
091: *
092: * Normally the jmeter properties file defines a single log file, so set
093: * this as the default from "log_file", default "jmeter.log" The default
094: * priority is set from "log_level", with a default of INFO
095: *
096: */
097: public static void initializeLogging(Properties properties) {
098: if (logManager == null) {
099: logManager = new LoggingManager();
100: }
101:
102: setFormat(properties);
103:
104: // Set the top-level defaults
105: setTarget(makeWriter(properties.getProperty(LOG_FILE,
106: "jmeter.log"), LOG_FILE));
107: setPriority(properties.getProperty(LOG_PRIORITY, "INFO"));
108:
109: setLoggingLevels(properties);
110: // now set the individual categories (if any)
111:
112: setConfig(properties);// Further configuration
113: }
114:
115: private static void setFormat(Properties properties) {
116: String pattern = DEFAULT_PATTERN;
117: String type = properties.getProperty("log_format_type", "");
118: if (type.length() == 0) {
119: pattern = properties.getProperty("log_format",
120: DEFAULT_PATTERN);
121: } else {
122: if (type.equalsIgnoreCase("thread_suffix")) {
123: pattern = PATTERN_THREAD_SUFFIX;
124: } else if (type.equalsIgnoreCase("thread_prefix")) {
125: pattern = PATTERN_THREAD_PREFIX;
126: } else {
127: pattern = DEFAULT_PATTERN;
128: }
129: }
130: format = new PatternFormatter(pattern + "\n");
131: }
132:
133: private static void setConfig(Properties p) {
134: String cfg = p.getProperty("log_config");
135: if (cfg == null)
136: return;
137:
138: // Make sure same hierarchy is used
139: Hierarchy hier = Hierarchy.getDefaultHierarchy();
140: LogKitLoggerManager manager = new LogKitLoggerManager(null,
141: hier, null, null);
142:
143: DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
144: try {
145: Configuration c = builder.buildFromFile(cfg);
146: Context ctx = new DefaultContext();
147: manager.contextualize(ctx);
148: manager.configure(c);
149: } catch (IllegalArgumentException e) {
150: // This happens if the default log-target id-ref specifies a non-existent target
151: System.out
152: .println("Error processing logging config " + cfg);
153: System.out.println(e.toString());
154: } catch (NullPointerException e) {
155: // This can happen if a log-target id-ref specifies a non-existent target
156: System.out
157: .println("Error processing logging config " + cfg);
158: System.out.println("Perhaps a log target is missing?");
159: } catch (ConfigurationException e) {
160: System.out
161: .println("Error processing logging config " + cfg);
162: System.out.println(e.toString());
163: } catch (SAXException e) {
164: System.out
165: .println("Error processing logging config " + cfg);
166: System.out.println(e.toString());
167: } catch (IOException e) {
168: System.out
169: .println("Error processing logging config " + cfg);
170: System.out.println(e.toString());
171: } catch (ContextException e) {
172: System.out
173: .println("Error processing logging config " + cfg);
174: System.out.println(e.toString());
175: }
176: }
177:
178: /*
179: * Helper method to ensure that format is initialised if initializeLogging()
180: * has not yet been called.
181: */
182: private static PatternFormatter getFormat() {
183: if (format == null) {
184: format = new PatternFormatter(DEFAULT_PATTERN + "\n");
185: }
186: return format;
187: }
188:
189: /*
190: * Helper method to handle log target creation. If there is an error
191: * creating the file, then it uses System.out.
192: */
193: private static Writer makeWriter(String logFile, String propName) {
194: // If the name contains at least one set of paired single-quotes, reformat using DateFormat
195: final int length = logFile.split("'", -1).length;
196: if (length > 1 && length % 2 == 1) {
197: try {
198: SimpleDateFormat df = new SimpleDateFormat(logFile);
199: logFile = df.format(new Date());
200: } catch (Exception ignored) {
201: }
202: }
203: Writer wt;
204: isWriterSystemOut = false;
205: try {
206: wt = new FileWriter(logFile);
207: } catch (Exception e) {
208: System.out.println(propName + "=" + logFile + " "
209: + e.toString());
210: System.out.println("[" + propName + "-> System.out]");
211: isWriterSystemOut = true;
212: wt = new PrintWriter(System.out);
213: }
214: return wt;
215: }
216:
217: /**
218: * Handle LOG_PRIORITY.category=priority and LOG_FILE.category=file_name
219: * properties. If the prefix is detected, then remove it to get the
220: * category.
221: */
222: public static void setLoggingLevels(Properties appProperties) {
223: Iterator props = appProperties.keySet().iterator();
224: while (props.hasNext()) {
225: String prop = (String) props.next();
226: if (prop.startsWith(LOG_PRIORITY + "."))
227: // don't match the empty category
228: {
229: String category = prop
230: .substring(LOG_PRIORITY.length() + 1);
231: setPriority(appProperties.getProperty(prop), category);
232: }
233: if (prop.startsWith(LOG_FILE + ".")) {
234: String category = prop.substring(LOG_FILE.length() + 1);
235: String file = appProperties.getProperty(prop);
236: setTarget(new WriterTarget(makeWriter(file, prop),
237: getFormat()), category);
238: }
239: }
240: }
241:
242: private final static String PACKAGE_PREFIX = "org.apache.";
243:
244: /*
245: * Stack contains the follow when the context is obtained:
246: * 0 - getCallerClassNameAt()
247: * 1 - this method
248: * 2 - getLoggerForClass()
249: *
250: */
251: private static String getCallerClassName() {
252: String name = ClassContext.getCallerClassNameAt(3);
253: return name;
254: }
255:
256: public static String removePrefix(String name) {
257: if (name.startsWith(PACKAGE_PREFIX)) { // remove the package prefix
258: name = name.substring(PACKAGE_PREFIX.length());
259: }
260: return name;
261: }
262:
263: /**
264: * Get the Logger for a class - no argument needed because the calling class
265: * name is derived automatically from the call stack.
266: *
267: * @return Logger
268: */
269: public static Logger getLoggerForClass() {
270: String className = getCallerClassName();
271: return Hierarchy.getDefaultHierarchy().getLoggerFor(
272: removePrefix(className));
273: }
274:
275: public static Logger getLoggerFor(String category) {
276: return Hierarchy.getDefaultHierarchy().getLoggerFor(category);
277: }
278:
279: public static Logger getLoggerForShortName(String category) {
280: return Hierarchy.getDefaultHierarchy().getLoggerFor(
281: removePrefix(category));
282: }
283:
284: public static void setPriority(String p, String category) {
285: setPriority(Priority.getPriorityForName(p), category);
286: }
287:
288: /**
289: *
290: * @param p - priority, e.g. DEBUG, INFO
291: * @param fullName - e.g. org.apache.jmeter.etc
292: */
293: public static void setPriorityFullName(String p, String fullName) {
294: setPriority(Priority.getPriorityForName(p),
295: removePrefix(fullName));
296: }
297:
298: public static void setPriority(Priority p, String category) {
299: Hierarchy.getDefaultHierarchy().getLoggerFor(category)
300: .setPriority(p);
301: }
302:
303: public static void setPriority(String p) {
304: setPriority(Priority.getPriorityForName(p));
305: }
306:
307: public static void setPriority(Priority p) {
308: Hierarchy.getDefaultHierarchy().setDefaultPriority(p);
309: }
310:
311: public static void setTarget(LogTarget target, String category) {
312: Logger logger = Hierarchy.getDefaultHierarchy().getLoggerFor(
313: category);
314: logger.setLogTargets(new LogTarget[] { target });
315: }
316:
317: /**
318: * Sets the default log target from the parameter. The existing target is
319: * first closed if necessary.
320: *
321: * @param targetFile
322: * (Writer)
323: */
324: public static void setTarget(Writer targetFile) {
325: if (target == null) {
326: target = getTarget(targetFile, getFormat());
327: isTargetSystemOut = isWriterSystemOut;
328: } else {
329: if (!isTargetSystemOut && target instanceof WriterTarget) {
330: ((WriterTarget) target).close();
331: }
332: target = getTarget(targetFile, getFormat());
333: isTargetSystemOut = isWriterSystemOut;
334: }
335: Hierarchy.getDefaultHierarchy().setDefaultLogTarget(target);
336: }
337:
338: private static LogTarget getTarget(Writer targetFile,
339: PatternFormatter fmt) {
340: return new WriterTarget(targetFile, fmt);
341: }
342: }
|