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    import java.util.Locale;
023    import java.util.regex.Pattern;
024    
025    import net.sf.logdistiller.LogEvent;
026    import net.sf.logdistiller.LogType;
027    
028    /**
029     * Oracle Database's <code>alert.log</code> file parser. A typical rule configuration with such log files is:
030     *
031     * <pre>
032     * &lt;group id=&quot;ORA&quot;&gt;
033     *  &lt;description&gt;ORA-* messages&lt;/description&gt;
034     *  &lt;condition&gt;
035     *    &lt;match attribute=&quot;message&quot; type=&quot;contains&quot;&gt;ORA-&lt;/match&gt;
036     *  &lt;/condition&gt;
037     *  &lt;plugin type=&quot;sampling&quot;&gt;
038     *    &lt;param name=&quot;attribute&quot;&gt;message&lt;/param&gt;
039     *    &lt;param name=&quot;regexp&quot;&gt;(ORA-\d+)&lt;/param&gt;
040     *  &lt;/plugin&gt;
041     * lt;/group&gt;
042     * </pre>
043     *
044     * By default, the classification rules generated by the GUI for this type of logs:
045     * <ol>
046     * <li>will contain a such "ORA" group for events with an ORA- id</li>
047     * <li>but won't be able to sort other events, as the log event structure is too generic (the message has no specific
048     * format).</li>
049     * </ol>
050     *
051     * @since 0.9
052     */
053    public class OracleAlertLogEvent
054        extends LogEvent
055        implements Comparable
056    {
057        public final static String ID = "oracle-alert";
058    
059        public final String timestamp;
060    
061        public final Date date;
062    
063        public final String message;
064    
065        public final static LogType LOGTYPE = new LogType.Basic( ID );
066    
067        private final static DateFormat DATE_FORMAT = new SimpleDateFormat( "EEE MMM dd HH:mm:ss yyyy", Locale.US );
068    
069        private final static String[] ATTRIBUTE_NAMES =
070            { "logSource", "timestamp", "timestamp.day", "timestamp.time", "message" };
071    
072        public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES );
073    
074        public OracleAlertLogEvent( Factory factory, String firstLine, String message )
075            throws ParseException
076        {
077            super( factory, firstLine + Factory.NEWLINE + message );
078            timestamp = firstLine;
079            date = DATE_FORMAT.parse( timestamp );
080            this.message = message;
081            setAttributes( new String[] { factory.getLogSource(), timestamp,
082                timestamp.substring( 0, 10 ) + timestamp.substring( 19, 24 ), timestamp.substring( 11, 19 ), message } );
083        }
084    
085        public int compareTo( Object o )
086        {
087            return date.compareTo( ( (OracleAlertLogEvent) o ).date );
088        }
089    
090        private static class Description
091            extends LogType.Description
092        {
093            public Description( LogType.Basic logtype, String[] attributeNames )
094            {
095                super( logtype, attributeNames );
096                logtype.setDescription( this );
097            }
098    
099            public LogEvent.Factory newFactory( Reader reader, String logSource )
100                throws IOException
101            {
102                return new Factory( this, reader, logSource );
103            }
104    
105            public String getDefaultSpecificGroups()
106            {
107                return "  <group id=\"ORA\">\n"
108                    + "    <description>ORA-* messages</description>\n"
109                    + "    <condition>\n"
110                    + "      <match attribute=\"message\" type=\"contains\">ORA-</match>\n"
111                    + "    </condition>\n"
112                    + "    <report publisher=\"file\"/>\n"
113                    + "    <plugin type=\"sampling\">\n"
114                    + "      <param name=\"attribute\">message</param>\n"
115                    + "      <param name=\"regexp\">(ORA-\\d+)</param>\n"
116                    + "    </plugin>\n"
117                    + "  </group>";
118            }
119        }
120    
121        private static class Factory
122            extends LogEvent.Factory
123        {
124            private final LineNumberReader reader;
125    
126            private String curLine;
127    
128            private final static Pattern DATE_PATTERN =
129                Pattern.compile( "\\w{3} \\w{3} \\d{2} \\d{2}:\\d{2}:\\d{2} \\d{4}" );
130    
131            public Factory( Description description, Reader reader, String logSource )
132                throws FileNotFoundException
133            {
134                super( description, logSource );
135                this.reader = new LineNumberReader( reader );
136            }
137    
138            /**
139             * Detect the start of a new log event
140             */
141            protected boolean detectLogEventStart( String line )
142            {
143                return DATE_PATTERN.matcher( line ).matches();
144            }
145    
146            protected LogEvent readNextEvent()
147                throws IOException, ParseException
148            {
149                if ( curLine == null )
150                {
151                    curLine = reader.readLine();
152                    if ( curLine == null )
153                    {
154                        // EOF
155                        return null;
156                    }
157                    // beginning of log file does not start with a complete log event with timestamp
158                    while ( !detectLogEventStart( curLine ) )
159                    {
160                        curLine = reader.readLine();
161                        if ( curLine == null )
162                        {
163                            // EOF
164                            return null;
165                        }
166                    }
167                }
168                String firstLine = curLine;
169                StringBuffer buffer = new StringBuffer();
170                while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) )
171                {
172                    if ( buffer.length() > 0 )
173                    {
174                        buffer.append( NEWLINE );
175                    }
176                    buffer.append( curLine );
177                }
178                return new OracleAlertLogEvent( this, firstLine, buffer.toString() );
179            }
180        }
181    }