View Javadoc
1   package net.sf.logdistiller;
2   
3   /*
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  import java.io.IOException;
18  import java.io.PrintWriter;
19  import java.io.StringWriter;
20  import java.text.ParseException;
21  import java.util.regex.Matcher;
22  
23  import net.sf.logdistiller.LogType.AttributeInfo;
24  
25  import org.apache.commons.lang.ArrayUtils;
26  
27  /**
28   * Log events base class. Every new log type must implement an inherited class, with appropriate:
29   * <ul>
30   * <li>{@link Factory} to create events from the log stream</li>
31   * <li>{@link LogType} to declare the log type and eventually available parameters to customize its format</li>
32   * </ul>
33   *
34   * @see LogType
35   * @see LogTypes
36   */
37  public abstract class LogEvent
38  {
39      private final Factory factory;
40  
41      private final String rawLog;
42  
43      /** provided attributes */
44      private String[] attributes;
45  
46      /** extended attributes */
47      private Extension[] extensions;
48  
49      protected LogEvent( Factory factory, String rawLog )
50      {
51          this.factory = factory;
52          this.rawLog = rawLog;
53          this.extensions = new Extension[factory.getDescription().getExtensions().length];
54      }
55  
56      /**
57       * Set the provided attributes values.
58       *
59       * @param attributes attributes values
60       */
61      protected void setAttributes( String[] attributes )
62      {
63          int count = factory.getDescription().getAttributesCount();
64          if ( attributes.length != count )
65          {
66              throw new IllegalArgumentException( "expected " + count + " attributes, got " + attributes.length );
67          }
68          this.attributes = attributes;
69      }
70  
71      protected void checkInitialized()
72      {
73          if ( attributes == null )
74          {
75              throw new IllegalStateException( "log event not initialized: call setAttributes() to initialize it" );
76          }
77      }
78  
79      /**
80       * Get the raw log text from which this event was parsed.
81       *
82       * @return String
83       */
84      public String getRawLog()
85      {
86          return rawLog;
87      }
88  
89      /**
90       * Get a provided attribute value by its position index.
91       *
92       * @param pos the position index of the attribute.
93       * @return the value of the attribute.
94       */
95      public String getAttribute( int pos )
96      {
97          checkInitialized();
98          return attributes[pos];
99      }
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 }