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