sylvain 01/07/24 02:34:08 Modified: src/org/apache/cocoon/servlet CocoonServlet.java Added: src/org/apache/cocoon/util/log CocoonLogFormatter.java ExtensiblePatternFormatter.java package.html Log: New log formatter : outputs request URI and caller class name in the log file Revision Changes Path 1.23 +14 -4 xml-cocoon2/src/org/apache/cocoon/servlet/CocoonServlet.java Index: CocoonServlet.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/org/apache/cocoon/servlet/CocoonServlet.java,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- CocoonServlet.java 2001/07/20 09:03:29 1.22 +++ CocoonServlet.java 2001/07/24 09:34:08 1.23 @@ -42,12 +42,13 @@ import org.apache.cocoon.util.ClassUtils; import org.apache.cocoon.util.IOUtils; import org.apache.cocoon.util.NetUtils; +import org.apache.cocoon.util.log.CocoonLogFormatter; +import org.apache.log.ContextStack; import org.apache.log.Hierarchy; import org.apache.log.LogTarget; import org.apache.log.Logger; import org.apache.log.Priority; import org.apache.log.filter.PriorityFilter; -import org.apache.log.format.AvalonFormatter; import org.apache.log.output.FileOutputLogTarget; import org.apache.log.output.ServletOutputLogTarget; import org.xml.sax.SAXException; @@ -61,7 +62,7 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Nicola Ken Barozzi</a> Aisa * @author <a href="mailto:[EMAIL PROTECTED]">Berin Loritsch</a> * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a> - * @version CVS $Revision: 1.22 $ $Date: 2001/07/20 09:03:29 $ + * @version CVS $Revision: 1.23 $ $Date: 2001/07/24 09:34:08 $ */ public class CocoonServlet extends HttpServlet { @@ -334,9 +335,9 @@ this.appContext.put(Constants.CONTEXT_LOG_FILE, logName); final String path = logDir + logName; - final AvalonFormatter formatter = new AvalonFormatter(); + final CocoonLogFormatter formatter = new CocoonLogFormatter(); formatter.setFormat( "%7.7{priority} %5.5{time} [%8.8{category}] " + - "(%{context}): %{message}\\n%{throwable}" ); + "(%{uri}) %{thread}/%{class:short}: %{message}\\n%{throwable}" ); this.log = Hierarchy.getDefaultHierarchy().getLoggerFor("cocoon"); this.log.setPriority(logPriority); @@ -485,6 +486,15 @@ } Environment env = this.getEnvironment(uri, request, res); + + // Initialize a fresh log context containing the object model : it + // will be used by the CocoonLogFormatter + ContextStack ctxStack = org.apache.log.ContextStack.getCurrentContext(); + ctxStack.clear(); + // Add thread name (default content for empty context) + ctxStack.push(Thread.currentThread().getName()); + // Add the object model + ctxStack.push(env.getObjectModel()); if (!this.cocoon.process(env)) { 1.1 xml-cocoon2/src/org/apache/cocoon/util/log/CocoonLogFormatter.java Index: CocoonLogFormatter.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.util.log; import java.util.Map; import org.apache.avalon.framework.CascadingThrowable; import org.apache.cocoon.Constants; import org.apache.cocoon.environment.Request; import org.apache.log.ContextStack; import org.apache.log.LogEvent; /** * An extended pattern formatter. New patterns are defined by this class are : * <ul> * <li><code>class</code> : outputs the name of the class that has logged the * message. The optional <code>short</code> subformat removes the * package name. Warning : this pattern works only if formatting occurs in * the same thread as the call to Logger, i.e. it won't work with * <code>AsyncLogTarget</code>.</li> * <li><code>thread</code> : outputs the name of the current thread (first element * on the context stack).</li> * <li><code>uri</code> : outputs the request URI.<li> * </ul> * * @author <a href="[EMAIL PROTECTED]">Sylvain Wallez</a> */ public class CocoonLogFormatter extends ExtensiblePatternFormatter { protected final static int TYPE_CLASS = MAX_TYPE + 1; protected final static int TYPE_URI = MAX_TYPE + 2; protected final static int TYPE_THREAD = MAX_TYPE + 3; protected final static String TYPE_CLASS_STR = "class"; protected final static String TYPE_CLASS_SHORT_STR = "short"; protected final static String TYPE_URI_STR = "uri"; protected final static String TYPE_THREAD_STR = "thread"; /** * Hack to get the call stack as an array of classes. The * SecurityManager class provides it as a protected method, so * change it to public through a new method ! */ static public class CallStack extends SecurityManager { /** * Returns the current execution stack as an array of classes. * The length of the array is the number of methods on the execution * stack. The element at index 0 is the class of the currently executing * method, the element at index 1 is the class of that method's caller, * and so on. */ public Class[] get() { return getClassContext(); } } /** The class that we will search for in the call stack */ private Class loggerClass = org.apache.log.Logger.class; private CallStack callStack = new CallStack(); protected int getTypeIdFor(String type) { // Search for new patterns defined here, or else delegate // to the parent class if (type.equalsIgnoreCase(TYPE_CLASS_STR)) return TYPE_CLASS; else if (type.equalsIgnoreCase(TYPE_URI_STR)) return TYPE_URI; else if (type.equalsIgnoreCase(TYPE_THREAD_STR)) return TYPE_THREAD; else return super.getTypeIdFor( type ); } protected String formatPatternRun(LogEvent event, PatternRun run) { // Format new patterns defined here, or else delegate to // the parent class switch (run.m_type) { case TYPE_CLASS : return getClass(run.m_format); case TYPE_URI : return getURI(event.getContextStack()); case TYPE_THREAD : return getThread(event.getContextStack()); } return super.formatPatternRun(event, run); } /** * Finds the class that has called Logger. */ private String getClass(String format) { Class[] stack = this.callStack.get(); // Traverse the call stack in reverse order until we find a Logger for (int i = stack.length-1; i >= 0; i--) { if (this.loggerClass.isAssignableFrom(stack[i])) { // Found : the caller is the previous stack element String className = stack[i+1].getName(); // Handle optional format if (TYPE_CLASS_SHORT_STR.equalsIgnoreCase(format)) { int pos = className.lastIndexOf('.'); if (pos >= 0) className = className.substring(pos + 1); } return className; } } // No Logger found in call stack : can occur with AsyncLogTarget // where formatting takes place in a different thread. return "Unknown-class"; } /** * Find the URI that is being processed. */ private String getURI(ContextStack ctxStack) { String result = "Unknown-URI"; // Get URI from the first context stack element, if it's a Map (the object model). if (ctxStack.getSize() > 1) { Object context = ctxStack.get(1); if (context instanceof Map) { // Get the request Request request = (Request)((Map)context).get(Constants.REQUEST_OBJECT); if (request != null) { result = request.getRequestURI(); } } } return result; } /** * Find the thread that is logged this event. */ private String getThread(ContextStack ctxStack) { if (ctxStack.getSize() > 0) return String.valueOf(ctxStack.get(0)); else return "Unknown-thread"; } /** * Utility method to format stack trace so that CascadingExceptions are * formatted with all nested exceptions. * * FIXME : copied from AvalonFormatter, to be removed if ExtensiblePatternFormatter * replaces PatternFormatter. * * @param throwable the throwable instance * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getStackTrace( final Throwable throwable, final String format ) { final StringBuffer sb = new StringBuffer(); sb.append( super.getStackTrace( throwable, format ) ); if( throwable instanceof CascadingThrowable ) { final Throwable t = ((CascadingThrowable)throwable).getCause(); sb.append( getStackTrace( t, format ) ); } return sb.toString(); } } 1.1 xml-cocoon2/src/org/apache/cocoon/util/log/ExtensiblePatternFormatter.java Index: ExtensiblePatternFormatter.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.util.log; import java.io.StringWriter; import java.util.Stack; import org.apache.log.*; /** * A refactoring of <code>org.apache.log.format.PatternFormatter</code> that * can be extended. * This formater formats the LogEntries according to a input pattern * string. * * The format of each pattern element can be %[+|-]#.#{field:subformat} * * The +|- indicates left or right justify. * The #.# indicates the minimum and maximum size of output. * 'field' indicates which field is to be output and must be one of * proeprties of LogEvent * 'subformat' indicates a particular subformat and is currently unused. * * @author <a href="mailto:[EMAIL PROTECTED]">Peter Donald</a> * @author <a href="[EMAIL PROTECTED]">Sylvain Wallez</a> */ public class ExtensiblePatternFormatter implements Formatter { protected final static int TYPE_TEXT = 1; protected final static int TYPE_CATEGORY = 2; protected final static int TYPE_CONTEXT = 3; protected final static int TYPE_MESSAGE = 4; protected final static int TYPE_TIME = 5; protected final static int TYPE_RELATIVE_TIME = 6; protected final static int TYPE_THROWABLE = 7; protected final static int TYPE_PRIORITY = 8; /** * The maximum value used for TYPEs. Subclasses can define their own TYPEs * starting at <code>MAX_TYPE + 1</code>. */ protected final static int MAX_TYPE = 8; protected final static String TYPE_CATEGORY_STR = "category"; protected final static String TYPE_CONTEXT_STR = "context"; protected final static String TYPE_MESSAGE_STR = "message"; protected final static String TYPE_TIME_STR = "time"; protected final static String TYPE_RELATIVE_TIME_STR = "rtime"; protected final static String TYPE_THROWABLE_STR = "throwable"; protected final static String TYPE_PRIORITY_STR = "priority"; protected final static String SPACE_16 = " "; protected final static String SPACE_8 = " "; protected final static String SPACE_4 = " "; protected final static String SPACE_2 = " "; protected final static String SPACE_1 = " "; protected final static String EOL = System.getProperty("line.separator", "\n"); protected static class PatternRun { public String m_data; public boolean m_rightJustify; public int m_minSize; public int m_maxSize; public int m_type; public String m_format; } protected PatternRun m_formatSpecification[]; /** * Extract and build a pattern from input string. * * @param stack the stack on which to place patterns * @param pattern the input string * @param index the start of pattern run * @return the number of characters in pattern run */ protected int addPatternRun( final Stack stack, final char pattern[], int index ) { final PatternRun run = new PatternRun(); final int start = index++; //first check for a +|- sign if( '+' == pattern[ index ] ) index++; else if( '-' == pattern[ index ] ) { run.m_rightJustify = true; index++; } if( Character.isDigit( pattern[ index ] )) { int total = 0; while( Character.isDigit( pattern[ index ] ) ) { total = total * 10 + (pattern[ index ] - '0'); index++; } run.m_minSize = total; } //check for . sign indicating a maximum is to follow if( index < pattern.length && '.' == pattern[ index ] ) { index++; if( Character.isDigit( pattern[ index ] )) { int total = 0; while( Character.isDigit( pattern[ index ] ) ) { total = total * 10 + (pattern[ index ] - '0'); index++; } run.m_maxSize = total; } } if( index >= pattern.length || '{' != pattern[ index ] ) { throw new IllegalArgumentException( "Badly formed pattern at character " + index ); } int typeStart = index; while( index < pattern.length && pattern[ index ]!= ':' && pattern[ index ] != '}' ) { index++; } int typeEnd = index - 1; final String type = new String( pattern, typeStart + 1, typeEnd - typeStart ); run.m_type = getTypeIdFor( type ); if( index < pattern.length && pattern[ index ] == ':' ) { index++; while( index < pattern.length && pattern[ index ] != '}' ) index++; final int length = index - typeEnd - 2; if( 0 != length ) { run.m_format = new String( pattern, typeEnd + 2, length ); } } if( index >= pattern.length || '}' != pattern[ index ] ) { throw new IllegalArgumentException("Unterminated type in pattern at character " + index ); } index++; stack.push( run ); return index - start; } /** * Extract and build a text run from input string. * It does special handling of '\n' and '\t' replaceing * them with newline and tab. * * @param stack the stack on which to place runs * @param pattern the input string * @param index the start of the text run * @return the number of characters in run */ protected int addTextRun( final Stack stack, final char pattern[], int index ) { final PatternRun run = new PatternRun(); final int start = index; boolean escapeMode = false; if( '%' == pattern[ index ] ) index++; final StringBuffer sb = new StringBuffer(); while( index < pattern.length && pattern[ index ] != '%' ) { if( escapeMode ) { if( 'n' == pattern[ index ] ) sb.append( EOL ); else if( 't' == pattern[ index ] ) sb.append( '\t' ); else sb.append( pattern[ index ] ); escapeMode = false; } else if( '\\' == pattern[ index ] ) escapeMode = true; else sb.append( pattern[ index ] ); index++; } run.m_data = sb.toString(); run.m_type = TYPE_TEXT; stack.push( run ); return index - start; } /** * Utility to append a string to buffer given certain constraints. * * @param sb the StringBuffer * @param minSize the minimum size of output (0 to ignore) * @param maxSize the maximum size of output (0 to ignore) * @param rightJustify true if the string is to be right justified in it's box. * @param output the input string */ protected void append( final StringBuffer sb, final int minSize, final int maxSize, final boolean rightJustify, final String output ) { final int size = output.length(); if( size < minSize ) { //assert( minSize > 0 ); if( rightJustify ) { appendWhiteSpace( sb, minSize - size ); sb.append( output ); } else { sb.append( output ); appendWhiteSpace( sb, minSize - size ); } } else if( maxSize > 0 && maxSize < size ) { sb.append( output.substring( 0, maxSize ) ); } else { sb.append( output ); } } /** * Append a certain number of whitespace characters to a StringBuffer. * * @param sb the StringBuffer * @param length the number of spaces to append */ protected void appendWhiteSpace( final StringBuffer sb, int length ) { while( length >= 16 ) { sb.append( SPACE_16 ); length -= 16; } if( length >= 8 ) { sb.append( SPACE_8 ); length -= 8; } if( length >= 4 ) { sb.append( SPACE_4 ); length -= 4; } if( length >= 2 ) { sb.append( SPACE_2 ); length -= 2; } if( length >= 1 ) { sb.append( SPACE_1 ); length -= 1; } } /** * Format the event according to the pattern. * * @param event the event * @return the formatted output */ public String format( final LogEvent event ) { final StringBuffer sb = new StringBuffer(); String str = null; for( int i = 0; i < m_formatSpecification.length; i++ ) { final PatternRun run = m_formatSpecification[ i ]; //treat text differently as it doesn't need min/max padding if ( run.m_type == TYPE_TEXT ) { sb.append( run.m_data ); } else { str = formatPatternRun( event, run ); if (str != null) { append( sb, run.m_minSize, run.m_maxSize, run.m_rightJustify, str ); } } } return sb.toString(); } /** * Formats a single pattern run (can be extended in subclasses). * * @param run the pattern run to format. * @return the formatted result. */ protected String formatPatternRun( final LogEvent event, final PatternRun run ) { String str = null; switch( run.m_type ) { case TYPE_RELATIVE_TIME: str = getTime( event.getRelativeTime(), run.m_format ); break; case TYPE_TIME: str = getTime( event.getTime(), run.m_format ); break; case TYPE_THROWABLE: str = getStackTrace( event.getThrowable(), run.m_format ); break; case TYPE_MESSAGE: str = getMessage( event.getMessage(), run.m_format ); break; case TYPE_CONTEXT: str = getContext( event.getContextStack(), run.m_format ); break; case TYPE_CATEGORY: str = getCategory( event.getCategory(), run.m_format ); break; case TYPE_PRIORITY: str = getPriority( event.getPriority(), run.m_format ); break; default: //TODO: Convert next line to use error handler Hierarchy.getDefaultHierarchy().log( "Unknown Pattern specification." + run.m_type ); } return str; } /** * Utility method to format category. * * @param category the category string * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getCategory( final String category, final String format ) { return category; } /** * Get formatted priority string. */ protected String getPriority( final Priority priority, final String format ) { return priority.getName(); } /** * Utility method to format context. * * @param context the context string * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getContext( final ContextStack stack, final String format ) { //TODO: Retrieve StringBuffers from a cache final StringBuffer sb = new StringBuffer(); final int size = stack.getSize(); int sizeSpecification = Integer.MAX_VALUE; if( null != format ) { try { sizeSpecification = Integer.parseInt( format ); } catch( final NumberFormatException nfe ) { nfe.printStackTrace(); } } final int end = size - 1; final int start = Math.max( end - sizeSpecification + 1, 0 ); for( int i = start; i < end; i++ ) { sb.append( fix( stack.get( i ).toString() ) ); sb.append( '.' ); } sb.append( stack.get( end ) ); return sb.toString(); } /** * Correct a context string by replacing '.''s with a '_'. * * @param context the un-fixed context * @return the fixed context */ protected final String fix( final String context ) { return context.replace( '.', '_' ); } /** * Utility method to format message. * * @param message the message string * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getMessage( final String message, final String format ) { return message; } /** * Utility method to format stack trace. * * @param throwable the throwable instance * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getStackTrace( final Throwable throwable, final String format ) { if( null == throwable ) return ""; final StringWriter sw = new StringWriter(); throwable.printStackTrace( new java.io.PrintWriter( sw ) ); return sw.toString(); } /** * Utility method to format time. * * @param time the time * @param format ancilliary format parameter - allowed to be null * @return the formatted string */ protected String getTime( final long time, final String format ) { return Long.toString( time ); } /** * Retrieve the type-id for a particular string. * * @param type the string * @return the type-id */ protected int getTypeIdFor( final String type ) { if( type.equalsIgnoreCase( TYPE_CATEGORY_STR ) ) return TYPE_CATEGORY; else if( type.equalsIgnoreCase( TYPE_CONTEXT_STR ) ) return TYPE_CONTEXT; else if( type.equalsIgnoreCase( TYPE_MESSAGE_STR ) ) return TYPE_MESSAGE; else if( type.equalsIgnoreCase( TYPE_PRIORITY_STR ) ) return TYPE_PRIORITY; else if( type.equalsIgnoreCase( TYPE_TIME_STR ) ) return TYPE_TIME; else if( type.equalsIgnoreCase( TYPE_RELATIVE_TIME_STR ) ) return TYPE_RELATIVE_TIME; else if( type.equalsIgnoreCase( TYPE_THROWABLE_STR ) ) { return TYPE_THROWABLE; } else { throw new IllegalArgumentException( "Unknown Type in pattern - " + type ); } } /** * Parse the input pattern and build internal data structures. * * @param patternString the pattern */ protected void parse( final String patternString ) { final Stack stack = new Stack(); final int size = patternString.length(); final char pattern[] = new char[ size ]; int index = 0; patternString.getChars( 0, size, pattern, 0 ); while( index < size ) { if( pattern[ index ] == '%' && !( index != size - 1 && pattern[ index + 1 ] == '%' ) ) { index += addPatternRun( stack, pattern, index ); } else { index += addTextRun( stack, pattern, index ); } } final int elementCount = stack.size(); m_formatSpecification = new PatternRun[ elementCount ]; for( int i = 0; i < elementCount; i++ ) { m_formatSpecification[ i ] = (PatternRun) stack.elementAt( i ); } } /** * Set the string description that the format is extracted from. * * @param format the string format */ public void setFormat( final String format ) { parse( format ); } } 1.1 xml-cocoon2/src/org/apache/cocoon/util/log/package.html Index: package.html =================================================================== <html> <head> </head> <body> LogKit related utilities. </body> ---------------------------------------------------------------------- In case of troubles, e-mail: [EMAIL PROTECTED] To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]