001package net.sf.logdistiller; 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 017import java.io.IOException; 018import java.io.PrintWriter; 019import java.io.StringWriter; 020import java.text.ParseException; 021import java.util.regex.Matcher; 022 023import net.sf.logdistiller.LogType.AttributeInfo; 024 025import org.apache.commons.lang.ArrayUtils; 026 027/** 028 * Log events base class. Every new log type must implement an inherited class, with appropriate: 029 * <ul> 030 * <li>{@link Factory} to create events from the log stream</li> 031 * <li>{@link LogType} to declare the log type and eventually available parameters to customize its format</li> 032 * </ul> 033 * 034 * @see LogType 035 * @see LogTypes 036 */ 037public abstract class LogEvent 038{ 039 private final Factory factory; 040 041 private final String rawLog; 042 043 /** provided attributes */ 044 private String[] attributes; 045 046 /** extended attributes */ 047 private Extension[] extensions; 048 049 protected LogEvent( Factory factory, String rawLog ) 050 { 051 this.factory = factory; 052 this.rawLog = rawLog; 053 this.extensions = new Extension[factory.getDescription().getExtensions().length]; 054 } 055 056 /** 057 * Set the provided attributes values. 058 * 059 * @param attributes attributes values 060 */ 061 protected void setAttributes( String[] attributes ) 062 { 063 int count = factory.getDescription().getAttributesCount(); 064 if ( attributes.length != count ) 065 { 066 throw new IllegalArgumentException( "expected " + count + " attributes, got " + attributes.length ); 067 } 068 this.attributes = attributes; 069 } 070 071 protected void checkInitialized() 072 { 073 if ( attributes == null ) 074 { 075 throw new IllegalStateException( "log event not initialized: call setAttributes() to initialize it" ); 076 } 077 } 078 079 /** 080 * Get the raw log text from which this event was parsed. 081 * 082 * @return String 083 */ 084 public String getRawLog() 085 { 086 return rawLog; 087 } 088 089 /** 090 * Get a provided attribute value by its position index. 091 * 092 * @param pos the position index of the attribute. 093 * @return the value of the attribute. 094 */ 095 public String getAttribute( int pos ) 096 { 097 checkInitialized(); 098 return attributes[pos]; 099 } 100 101 /** 102 * Get a clone copy of the provided attributes. Note the this method clones the values array. 103 * 104 * @return (a copy of) provided attributes 105 */ 106 public String[] getAttributes() 107 { 108 checkInitialized(); 109 return (String[]) ArrayUtils.clone( attributes ); 110 } 111 112 /** 113 * Get provided attributes count. 114 * 115 * @return the count of provided attributes 116 */ 117 public int getAttributesCount() 118 { 119 checkInitialized(); 120 return attributes.length; 121 } 122 123 /** 124 * Extracts an attribute's provided value of the log event. 125 * 126 * @param attributeName the name of the attribute (provided only) 127 * @return the value 128 */ 129 public String getAttribute( String attributeName ) 130 { 131 int index = factory.getDescription().getAttributeIndex( attributeName ); 132 if ( index < 0 ) 133 { 134 throw new IllegalArgumentException( "unknown provided attribute '" + attributeName + "'" ); 135 } 136 return getAttribute( index ); 137 } 138 139 public String getValue( AttributeInfo info ) 140 { 141 if ( info.extended ) 142 { 143 Extension extension = extensions[info.index]; 144 if ( extension == null ) 145 { 146 extension = new Extension( factory.getDescription().getExtensions()[info.index] ); 147 extensions[info.index] = extension; 148 } 149 return extension.getValue( info.regexpGroup ); 150 } 151 return getAttribute( info.index ); 152 } 153 154 public Factory getFactory() 155 { 156 return factory; 157 } 158 159 public void dump( PrintWriter out ) 160 { 161 out.println( "- raw log: " + getRawLog() ); 162 for ( int i = 0; i < getAttributesCount(); i++ ) 163 { 164 out.println( "- " + getFactory().getDescription().getAttributeName( i ) + ": " + getAttribute( i ) ); 165 } 166 } 167 168 public String dump() 169 { 170 StringWriter sw = new StringWriter(); 171 PrintWriter out = new PrintWriter( sw, false ); 172 dump( out ); 173 out.close(); 174 return sw.toString(); 175 } 176 177 /** 178 * Get the timestamp. 179 * 180 * @return the timestamp or "-" if no this log type does not have a timestamp 181 * @see LogType.Description#getTimestampAttribute() 182 */ 183 public String getTimestamp() 184 { 185 int index = factory.getDescription().getTimestampAttribute(); 186 return ( index > 0 ) ? getAttribute( index ) : "-"; 187 } 188 189 /** 190 * The base class for LogEvent factories, responsible for parsing an input stream into <code>LogEvent</code>s. 191 */ 192 public static abstract class Factory 193 { 194 public final static String NEWLINE = System.getProperty( "line.separator" ); 195 196 protected final LogType.Description description; 197 198 protected final String logSource; 199 200 private LogEvent lastEvent; 201 202 private LogEvent pushedbackEvent; 203 204 protected Factory( LogType.Description description, String logSource ) 205 { 206 this.description = description; 207 this.logSource = logSource; 208 } 209 210 /** 211 * gets the next LogEvent. 212 * 213 * @throws IOException 214 * @throws ParseException 215 * @return LogEvent the new LogEvent read, or <code>null</code> if none available any more 216 */ 217 public LogEvent nextEvent() 218 throws IOException, ParseException 219 { 220 LogEvent nextEvent = null; 221 if ( pushedbackEvent != null ) 222 { 223 nextEvent = pushedbackEvent; 224 pushedbackEvent = null; 225 } 226 else 227 { 228 try 229 { 230 nextEvent = readNextEvent(); 231 } 232 catch ( ParseException pe ) 233 { 234 ParseException pe2 = new ParseException( "error in '" + logSource + "' " + pe.getMessage(), 235 pe.getErrorOffset() ); 236 pe2.initCause( pe ); 237 throw pe2; 238 } 239 catch ( RuntimeException re ) 240 { 241 throw new RuntimeException( "error in '" + logSource + "' " + re.getMessage(), re ); 242 } 243 } 244 lastEvent = nextEvent; 245 return nextEvent; 246 } 247 248 public void pushbackLastEvent() 249 throws IllegalStateException 250 { 251 if ( pushedbackEvent != null ) 252 { 253 throw new IllegalStateException( "The last event has already been pushed back" ); 254 } 255 if ( lastEvent == null ) 256 { 257 throw new IllegalStateException( "There is no last event to push back" ); 258 } 259 pushedbackEvent = lastEvent; 260 } 261 262 public LogType.Description getDescription() 263 { 264 return description; 265 } 266 267 public String getLogSource() 268 { 269 return logSource; 270 } 271 272 protected abstract LogEvent readNextEvent() 273 throws IOException, ParseException; 274 } 275 276 /** 277 * Extended attributes calculated for a log event. 278 * 279 * @since 1.1 280 */ 281 private class Extension 282 { 283 /** 284 * Values of regexp match groups 285 */ 286 private final String[] values; 287 288 public Extension( Attributes.Extension definition ) 289 { 290 values = new String[definition.getProvides().size()]; 291 Matcher matcher = definition.getRegexp().matcher( getAttribute( definition.getSource() ) ); 292 if ( matcher.find() ) 293 { 294 int count = matcher.groupCount(); 295 for ( int i = 0; i < values.length; i++ ) 296 { 297 String value = ( i < count ) ? matcher.group( i + 1 ) : ""; 298 values[i] = ( value == null ) ? "" : value; 299 } 300 } 301 else 302 { 303 for ( int i = 0; i < values.length; i++ ) 304 { 305 values[i] = ""; 306 } 307 } 308 } 309 310 public String getValue( int index ) 311 { 312 return values[index]; 313 } 314 } 315}