001 package net.sf.logdistiller.logtypes; 002 003 /* 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 import java.io.*; 018 import java.text.SimpleDateFormat; 019 import java.text.DateFormat; 020 import java.text.ParseException; 021 import java.util.Date; 022 023 import org.apache.commons.lang.StringUtils; 024 025 import net.sf.logdistiller.LogEvent; 026 import net.sf.logdistiller.LogType; 027 import net.sf.logdistiller.util.StringCutter; 028 029 /** 030 * Log event for log4j XMLLayout (<code>log4j-XML</code>). By default, the classification rules generated by the GUI for 031 * this type of logs will sort events based on the following attributes: <code>level</code>, then <code>logger</code>. 032 */ 033 public class Log4jXmlLogEvent 034 extends LogEvent 035 implements Comparable 036 { 037 public final static String ID = "log4j-XML"; 038 039 public final String logger; 040 041 public final String timestamp; 042 043 public final String datetime; 044 045 public final long timestampValue; 046 047 public final String level; 048 049 public final String thread; 050 051 public final String message; 052 053 public final String ndc; 054 055 public final String throwable; 056 057 public final String locationInfoClass; 058 059 public final String locationInfoMethod; 060 061 public final String locationInfoFile; 062 063 public final String locationInfoLine; 064 065 public final static LogType LOGTYPE = new LogType.Basic( ID ); 066 067 private final static DateFormat DATE_FORMAT = new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" ); 068 069 private final static String LOGEVENT_START = "<log4j:event logger=\""; 070 071 private final static String[] ATTRIBUTE_NAMES = 072 { "logSource", "datetime", "timestamp", "level", "logger", "thread", "message", "NDC", "throwable", 073 "locationInfo.class", "locationInfo.method", "locationInfo.file", "locationInfo.line" }; 074 075 public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES ); 076 077 public Log4jXmlLogEvent( Factory factory, String rawLog ) 078 throws ParseException 079 { 080 super( factory, rawLog ); 081 StringCutter cutter = new StringCutter( rawLog ); 082 cutter.parseTo( LOGEVENT_START ); 083 logger = cutter.parseTo( "\" timestamp=\"" ); 084 timestamp = cutter.parseTo( "\" level=\"" ); 085 timestampValue = Long.parseLong( timestamp ); 086 datetime = DATE_FORMAT.format( new Date( timestampValue ) ); 087 level = cutter.parseTo( "\" thread=\"" ); 088 thread = cutter.parseTo( "\">" ); 089 cutter.parseTo( "<log4j:message><![CDATA[" ); 090 message = unescapeCDATA( cutter.parseTo( "]]></log4j:message>" ) ); 091 092 String remaining = cutter.getRemaining(); 093 if ( remaining.indexOf( "<log4j:NDC>" ) < 0 ) 094 { 095 ndc = ""; 096 } 097 else 098 { 099 cutter.parseTo( "<log4j:NDC><![CDATA[" ); 100 ndc = unescapeCDATA( cutter.parseTo( "]]></log4j:NDC>" ) ); 101 remaining = cutter.getRemaining(); 102 } 103 if ( remaining.indexOf( "<log4j:throwable>" ) < 0 ) 104 { 105 throwable = ""; 106 } 107 else 108 { 109 cutter.parseTo( "<log4j:throwable><![CDATA[" ); 110 throwable = unescapeCDATA( cutter.parseTo( "]]></log4j:throwable>" ) ); 111 remaining = cutter.getRemaining(); 112 } 113 if ( remaining.indexOf( "<log4j:locationInfo" ) < 0 ) 114 { 115 locationInfoClass = ""; 116 locationInfoMethod = ""; 117 locationInfoFile = ""; 118 locationInfoLine = ""; 119 } 120 else 121 { 122 cutter.parseTo( "<log4j:locationInfo class=\"" ); 123 locationInfoClass = cutter.parseTo( "\" method=\"" ); 124 locationInfoMethod = cutter.parseTo( "\" file=\"" ); 125 locationInfoFile = cutter.parseTo( "\" line=\"" ); 126 locationInfoLine = cutter.parseTo( "\"/>" ); 127 } 128 setAttributes( new String[] { factory.getLogSource(), datetime, timestamp, level, logger, thread, message, ndc, 129 throwable, locationInfoClass, locationInfoMethod, locationInfoFile, locationInfoLine } ); 130 } 131 132 public static String unescapeCDATA( String cdata ) 133 { 134 return StringUtils.replace( cdata, "]]>]]><![CDATA[", "]]>" ); 135 } 136 137 public int compareTo( Object o ) 138 { 139 long diff = timestampValue - ( (Log4jXmlLogEvent) o ).timestampValue; 140 return ( diff < 0 ) ? -1 : ( diff > 0 ) ? 1 : 0; 141 } 142 143 private static class Description 144 extends LogType.Description 145 { 146 public Description( LogType.Basic logtype, String[] attributeNames ) 147 { 148 super( logtype, attributeNames ); 149 logtype.setDescription( this ); 150 } 151 152 public LogEvent.Factory newFactory( Reader reader, String logSource ) 153 throws IOException 154 { 155 return new Factory( this, reader, logSource ); 156 } 157 158 public String getDefaultSpecificGroups() 159 { 160 return " <group id=\"warn\">\n" 161 + " <description>WARN events</description>\n" 162 + " <condition>\n" 163 + " <match attribute=\"level\" type=\"equals\">WARN</match>\n" 164 + " </condition>\n" 165 + " <report publisher=\"file\"/>\n" 166 + " <plugin type=\"sampling\">\n" 167 + " <param name=\"attributes\">logger</param>\n" 168 + " </plugin>\n" 169 + " </group>\n" 170 + "\n" 171 + " <group id=\"error\">\n" 172 + " <description>ERROR events</description>\n" 173 + " <condition>\n" 174 + " <match attribute=\"level\" type=\"equals\">ERROR</match>\n" 175 + " </condition>\n" 176 + " <report publisher=\"file\"/>\n" 177 + " <plugin type=\"sampling\">\n" 178 + " <param name=\"attributes\">logger</param>\n" 179 + " </plugin>\n" 180 + " </group>"; 181 } 182 183 public String getDefaultSamplingAttributes() 184 { 185 return "level,logger"; 186 } 187 } 188 189 private static class Factory 190 extends LogEvent.Factory 191 { 192 private final LineNumberReader reader; 193 194 private String curLine; 195 196 public Factory( Description description, Reader reader, String logSource ) 197 throws FileNotFoundException 198 { 199 super( description, logSource ); 200 this.reader = new LineNumberReader( reader ); 201 } 202 203 protected boolean detectLogEventStart( String line ) 204 { 205 return line.startsWith( LOGEVENT_START ); 206 } 207 208 protected LogEvent readNextEvent() 209 throws IOException, ParseException 210 { 211 if ( curLine == null ) 212 { 213 curLine = reader.readLine(); 214 if ( curLine == null ) 215 { 216 // EOF 217 return null; 218 } 219 } 220 if ( !detectLogEventStart( curLine ) ) 221 { 222 throw new ParseException( 223 "bad log format, beginnig of line " + reader.getLineNumber() + ": " + curLine, 224 0 ); 225 } 226 StringBuffer buffer = new StringBuffer( curLine ); 227 while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) ) 228 { 229 buffer.append( "\r\n" ); // fixed newline, as log4j 230 buffer.append( curLine ); 231 } 232 return new Log4jXmlLogEvent( this, buffer.toString() ); 233 } 234 } 235 }