001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Mananagement System 004 * 005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.util; 029 030import java.util.ArrayList; 031import java.util.Iterator; 032import java.util.List; 033 034import org.apache.log4j.Layout; 035import org.apache.log4j.PatternLayout; 036import org.apache.log4j.spi.LoggingEvent; 037import org.apache.log4j.spi.ThrowableInformation; 038 039/** 040 * Extends the default pattern layout of log4j by adding functionality for filtering the 041 * stack traces output.<p> 042 * 043 * CAUTION: Do not use classes which instantiate a logger in this class!!!<p> 044 * 045 * Usage (log4j.properties):<br/> 046 * log4j.appender.OC.layout=org.opencms.util.CmsPatternLayout<br/> 047 * log4j.appender.OC.layout.ConversionPattern=%d{DATE} %5p [%30.30C:%4L] %m%n<br/> 048 * log4j.appender.OC.layout.Filter=org.apache.tomcat,org.apache.catalina,org.apache.coyote<br/> 049 * log4j.appender.OC.layout.Exclude=org.opencms.workplace.list.A_CmsListDialog<br/> 050 * log4j.appender.OC.layout.MaxLength=5<p> 051 * 052 * @since 7.0.5 053 */ 054public class CmsPatternLayout extends PatternLayout { 055 056 /** List of class names which prevents displaying the stack trace. */ 057 private List<String> m_excludes; 058 059 /** List of class names which should be filtered. */ 060 private List<String> m_filters; 061 062 /** Maximum length of the filtered stack trace. */ 063 private int m_maxLength; 064 065 /** 066 * Default constructor.<p> 067 */ 068 public CmsPatternLayout() { 069 070 this(DEFAULT_CONVERSION_PATTERN); 071 } 072 073 /** 074 * Constructs a PatternLayout using the supplied conversion pattern. 075 * 076 * @param pattern the pattern to use for the layout 077 */ 078 CmsPatternLayout(String pattern) { 079 080 super(pattern); 081 m_filters = new ArrayList<String>(); 082 m_excludes = new ArrayList<String>(); 083 m_maxLength = Integer.MAX_VALUE; 084 } 085 086 /** 087 * @see org.apache.log4j.PatternLayout#format(org.apache.log4j.spi.LoggingEvent) 088 */ 089 @Override 090 public String format(LoggingEvent event) { 091 092 String result = super.format(event); 093 094 ThrowableInformation ti = event.getThrowableInformation(); 095 if (ti != null) { 096 // buffer for the complete filtered trace 097 StringBuffer trace = new StringBuffer(); 098 // buffer for the minimum trace if an exclusion matches 099 StringBuffer minTrace = new StringBuffer(); 100 101 boolean exclFound = false; 102 int count = 0; 103 int filtered = 0; 104 int truncated = 0; 105 106 String[] elements = ti.getThrowableStrRep(); 107 for (int i = 0; i < elements.length; i++) { 108 String elem = elements[i]; 109 110 // if entry not start with "at" -> put in minimum trace 111 if (!elem.trim().startsWith("at ") && !elem.trim().startsWith("...")) { 112 minTrace.append(elem).append(Layout.LINE_SEP); 113 } 114 115 // if cause trace starts reset counter (subtrace) 116 if (elem.trim().startsWith("Caused")) { 117 if (!exclFound && ((truncated > 0) || (filtered > 0))) { 118 trace.append(createSummary(truncated, filtered)); 119 } 120 count = 0; 121 filtered = 0; 122 truncated = 0; 123 } 124 125 // filter the entry 126 if (!matches(elem, m_filters) && !exclFound) { 127 if (count < m_maxLength) { 128 trace.append(elem).append(Layout.LINE_SEP); 129 130 count++; 131 } else { 132 truncated++; 133 } 134 } else { 135 filtered++; 136 } 137 138 // check for exclusion 139 if (!exclFound && matches(elem, m_excludes)) { 140 exclFound = true; 141 } 142 } 143 144 if (exclFound) { 145 result += minTrace.toString(); 146 } else { 147 if ((truncated > 0) || (filtered > 0)) { 148 trace.append(createSummary(truncated, filtered)); 149 } 150 result += trace.toString(); 151 } 152 } 153 154 return result; 155 } 156 157 /** 158 * @see org.apache.log4j.PatternLayout#ignoresThrowable() 159 */ 160 @Override 161 public boolean ignoresThrowable() { 162 163 return false; 164 } 165 166 /** 167 * Sets an exclusion for preventing the stack trace output.<p> 168 * 169 * @param exclude the names of a classes (comma separated) which should prevent the stack trace output 170 */ 171 public void setExclude(String exclude) { 172 173 String[] entries = exclude.split(","); 174 for (int i = 0; i < entries.length; i++) { 175 String entry = entries[i].trim(); 176 177 if (!entry.startsWith("at ")) { 178 entry = "at " + entry; 179 } 180 181 m_excludes.add(entry); 182 } 183 } 184 185 /** 186 * Sets a filter for the stack trace output.<p> 187 * 188 * @param filter the names of a classes (comma separated) which should be filtered in the stack trace output 189 */ 190 public void setFilter(String filter) { 191 192 String[] entries = filter.split(","); 193 for (int i = 0; i < entries.length; i++) { 194 String entry = entries[i].trim(); 195 196 if (!entry.startsWith("at ")) { 197 entry = "at " + entry; 198 } 199 200 m_filters.add(entry); 201 } 202 } 203 204 /** 205 * Sets the maximum length of the stack trace.<p> 206 * 207 * @param len the maximum length (lines) of the stack trace 208 */ 209 public void setMaxLength(String len) { 210 211 try { 212 m_maxLength = Integer.parseInt(len); 213 } catch (NumberFormatException ex) { 214 m_maxLength = Integer.MAX_VALUE; 215 } 216 } 217 218 /** 219 * Creates a string with the count of filtered and truncated elements.<p> 220 * 221 * @param truncated the number of truncated elements 222 * @param filtered the number of filtered elements 223 * 224 * @return a string with the count of filtered and truncated elements 225 */ 226 private String createSummary(int truncated, int filtered) { 227 228 StringBuffer result = new StringBuffer(128); 229 230 result.append("\t... "); 231 result.append(filtered + truncated); 232 result.append(" more ("); 233 result.append(filtered); 234 result.append(" filtered; "); 235 result.append(truncated); 236 result.append(" truncated)"); 237 result.append(Layout.LINE_SEP); 238 239 return result.toString(); 240 } 241 242 /** 243 * Checks if the element in the stack trace is filtered.<p> 244 * 245 * @param element the element in the stack trace to check 246 * @param list the list to check against 247 * 248 * @return true if filtered otherwise false 249 */ 250 private boolean matches(String element, List<String> list) { 251 252 boolean result = false; 253 254 Iterator<String> iter = list.iterator(); 255 while (iter.hasNext()) { 256 String rule = iter.next(); 257 258 if (element.trim().startsWith(rule)) { 259 return true; 260 } 261 } 262 263 return result; 264 } 265}