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.DateFormat;
019    import java.text.ParseException;
020    import java.text.SimpleDateFormat;
021    import java.util.Date;
022    import java.util.Locale;
023    import java.util.Map;
024    
025    import net.sf.logdistiller.LogEvent;
026    import net.sf.logdistiller.LogType;
027    import net.sf.logdistiller.util.FormatUtil;
028    import net.sf.logdistiller.util.StringCutter;
029    
030    /**
031     * Log event for <a href="http://edocs.bea.com/">BEA Weblogic server</a>'s logs. By default, the date format is
032     * <code>MMM d, yyyy K:mm:ss a zz</code> with <code>en_US</code> locale, but you can change these values with logtype
033     * parameters <code>date.format</code> (expressed as a
034     * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html">java.text.SimpleDateFormat pattern</a>)
035     * and <code>date.locale</code>.<br>
036     * Example:
037     *
038     * <pre>
039     * &lt;logtype id=&quot;weblogic&quot;&gt;
040     *  &lt;param name=&quot;date.locale&quot;&gt;fr_FR&lt;/param&gt;
041     *  &lt;param name=&quot;date.format&quot;&gt;MMM d, yyyy K:mm:ss a zz&lt;/param&gt;
042     * &lt;/logtype&gt;
043     * </pre>
044     *
045     * By default, the classification rules generated by the GUI for this type of logs will sort events based on the
046     * following attributes: <code>severity</code>, then <code>subsystem</code> then <code>message_id</code>.
047     *
048     * @see java.text.SimpleDateFormat
049     */
050    public class WeblogicLogEvent
051        extends LogEvent
052        implements Comparable
053    {
054        public final static String ID = "weblogic";
055    
056        public final Date date;
057    
058        public final String timestamp;
059    
060        public final String severity;
061    
062        public final String subsystem;
063    
064        public final String machine;
065    
066        public final String server;
067    
068        public final String thread_id;
069    
070        public final String transaction_id;
071    
072        public final String user_id;
073    
074        public final String message_id;
075    
076        public final String message_text;
077    
078        public final String stacktrace;
079    
080        public final static LogType LOGTYPE = new WlLogType();
081    
082        private final static String LOGEVENT_START = "####<";
083    
084        private final static String[] ATTRIBUTE_NAMES =
085            { "logSource", "timestamp", "severity", "subsystem", "machine", "server", "thread_id", "transaction_id",
086                "user_id", "message_id", "message_text", "stacktrace" };
087    
088        public WeblogicLogEvent( Factory factory, String rawLog )
089            throws ParseException
090        {
091            super( factory, rawLog );
092            StringCutter cutter = new StringCutter( rawLog );
093            cutter.parseTo( LOGEVENT_START );
094            timestamp = cutter.parseTo( "> <" );
095            date = factory.parseDate( timestamp );
096            severity = cutter.parseTo( "> <" );
097            subsystem = cutter.parseTo( "> <" );
098            machine = cutter.parseTo( "> <" );
099            server = cutter.parseTo( "> <" );
100            thread_id = cutter.parseTo( "> <" );
101            transaction_id = cutter.parseTo( "> <" );
102            user_id = cutter.parseTo( "> <" );
103            message_id = cutter.parseTo( "> <" );
104            message_text = cutter.parseTo( "> " );
105            String remaining = cutter.getRemaining();
106            stacktrace = ( "".equals( remaining ) ) ? "" : remaining.substring( LogEvent.Factory.NEWLINE.length() );
107            setAttributes( new String[] { factory.getLogSource(), timestamp, severity, subsystem, machine, server,
108                thread_id, transaction_id, user_id, message_id, message_text, stacktrace } );
109        }
110    
111        public int compareTo( Object o )
112        {
113            return date.compareTo( ( (WeblogicLogEvent) o ).date );
114        }
115    
116        /**
117         * Weblogic log type supports following parameters:
118         * <ul>
119         * <li><code>date.format</code></li>
120         * <li><code>date.locale</code></li>
121         * </ul>
122         */
123        private static class WlLogType
124            extends LogType
125        {
126            private final static String DEFAULT_DATE_FORMAT = "MMM d, yyyy K:mm:ss a zz";
127    
128            public WlLogType()
129            {
130                super( WeblogicLogEvent.ID );
131            }
132    
133            public LogType.Description newDescription( Map params )
134            {
135                String dateFormat = (String) params.get( "date.format" );
136                dateFormat = ( dateFormat == null ) ? DEFAULT_DATE_FORMAT : dateFormat;
137                String locale = (String) params.get( "date.locale" );
138                Locale lcle = ( locale == null ) ? Locale.US : FormatUtil.parseLocale( locale );
139                return new WeblogicLogEvent.Description( this, WeblogicLogEvent.ATTRIBUTE_NAMES, dateFormat, lcle );
140            }
141        }
142    
143        private static class Description
144            extends LogType.Description
145        {
146            /** @since 0.9 */
147            private final String format;
148    
149            /** @since 0.9 */
150            private final Locale locale;
151    
152            /** @since 0.9 */
153            private final DateFormat df;
154    
155            public Description( LogType logtype, String[] attributeNames, String format, Locale locale )
156            {
157                super( logtype, attributeNames );
158                this.format = format;
159                this.locale = locale;
160                this.df = new SimpleDateFormat( format, locale );
161            }
162    
163            public LogEvent.Factory newFactory( Reader reader, String logSource )
164                throws IOException
165            {
166                return new Factory( this, reader, logSource );
167            }
168    
169            /** @since 0.9 */
170            public DateFormat getDateFormat()
171            {
172                return df;
173            }
174    
175            /** @since 0.9 */
176            public Locale getLocale()
177            {
178                return locale;
179            }
180    
181            /** @since 0.9 */
182            public String getFormat()
183            {
184                return format;
185            }
186    
187            public String getDefaultSpecificGroups()
188            {
189                return "  <group id=\"warning\">\n"
190                    + "    <description>Warning events</description>\n"
191                    + "    <condition>\n"
192                    + "      <match attribute=\"severity\" type=\"equals\">Warning</match>\n"
193                    + "    </condition>\n"
194                    + "    <report publisher=\"file\"/>\n"
195                    + "    <plugin type=\"sampling\">\n"
196                    + "      <param name=\"attributes\">subsystem,message_id</param>\n"
197                    + "    </plugin>\n"
198                    + "  </group>\n"
199                    + "\n"
200                    + "  <group id=\"error\">\n"
201                    + "    <description>Error events</description>\n"
202                    + "    <condition>\n"
203                    + "      <match attribute=\"severity\" type=\"equals\">Error</match>\n"
204                    + "    </condition>\n"
205                    + "    <report publisher=\"file\"/>\n"
206                    + "    <plugin type=\"sampling\">\n"
207                    + "      <param name=\"attributes\">subsystem,message_id</param>\n"
208                    + "    </plugin>\n"
209                    + "  </group>";
210            }
211    
212            public String getDefaultSamplingAttributes()
213            {
214                return "severity,subsystem,message_id";
215            }
216        }
217    
218        private static class Factory
219            extends LogEvent.Factory
220        {
221            private final DateFormat df;
222    
223            private final LineNumberReader reader;
224    
225            private String curLine;
226    
227            public Factory( Description description, Reader reader, String logSource )
228                throws FileNotFoundException
229            {
230                super( description, logSource );
231                this.reader = new LineNumberReader( reader );
232                this.df = description.getDateFormat();
233            }
234    
235            protected boolean detectLogEventStart( String line )
236            {
237                return line.startsWith( LOGEVENT_START );
238            }
239    
240            protected LogEvent readNextEvent()
241                throws IOException, ParseException
242            {
243                if ( curLine == null )
244                {
245                    curLine = reader.readLine();
246                    if ( curLine == null )
247                    {
248                        // EOF
249                        return null;
250                    }
251                }
252                if ( !detectLogEventStart( curLine ) )
253                {
254                    if ( curLine.length() > 15 )
255                    {
256                        curLine = curLine.substring( 0, 15 ) + "...";
257                    }
258                    String msg = " should start with '" + LOGEVENT_START + "' but found '" + curLine + "'";
259                    if ( reader.getLineNumber() == 1 )
260                    {
261                        throw new ParseException( "bad log format: is it really a Weblogic log file? It" + msg, 0 );
262                    }
263                    throw new ParseException( "bad log format, line " + reader.getLineNumber() + msg, 0 );
264                }
265                StringBuffer buffer = new StringBuffer( curLine );
266                while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) )
267                {
268                    buffer.append( NEWLINE );
269                    buffer.append( curLine );
270                }
271                return new WeblogicLogEvent( this, buffer.toString() );
272            }
273    
274            public Date parseDate( String date )
275                throws ParseException
276            {
277                try
278                {
279                    return df.parse( date );
280                }
281                catch ( ParseException pe )
282                {
283                    Description desc = (Description) description;
284                    throw new ParseException( pe.getMessage() + ", expected format: " + desc.getFormat()
285                        + ", expected locale: " + desc.getLocale(), pe.getErrorOffset() );
286                }
287            }
288        }
289    }