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