001 package 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
017 import java.io.IOException;
018 import java.io.PrintWriter;
019 import java.io.StringWriter;
020 import java.text.ParseException;
021 import java.util.regex.Matcher;
022
023 import net.sf.logdistiller.LogType.AttributeInfo;
024
025 import 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 */
037 public 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 }