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.regex.Pattern;
023
024import net.sf.logdistiller.LogEvent;
025import net.sf.logdistiller.LogType;
026import net.sf.logdistiller.util.LogEventBuilder;
027import net.sf.logdistiller.util.StringCutter;
028
029/**
030 * Log event for <a href="http://jboss.org/">JBoss</a>'s server logs. By default, the classification rules generated by
031 * the GUI for this type of logs will sort events based on the following attributes: <code>level</code> then
032 * <code>logger</code>.
033 *
034 * @since 0.9
035 */
036public class JBossLogEvent
037    extends LogEvent
038    implements Comparable<JBossLogEvent>
039{
040    public final static String ID = "jboss";
041
042    public final String timestamp;
043
044    public final Date date;
045
046    public final String level;
047
048    public final String logger;
049
050    public final String message;
051
052    public final String throwable;
053
054    public final String throwable_firstLine;
055
056    public final String throwable_class;
057
058    public final static LogType LOGTYPE = new LogType.Basic( ID );
059
060    private final static DateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss,SSS" );
061
062    private final static String[] ATTRIBUTE_NAMES =
063        { "logSource", "timestamp", "timestamp.date", "timestamp.time", "level", "logger", "message", "throwable",
064            "throwable.firstline", "throwable.class" };
065
066    public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES );
067
068    public JBossLogEvent( LogEvent.Factory factory, String firstLine, String secondLine, String throwable )
069        throws ParseException
070    {
071        super( factory, ( secondLine == null ) ? firstLine : ( firstLine + Factory.NEWLINE + throwable ) );
072        timestamp = firstLine.substring( 0, 23 );
073        date = DATE_FORMAT.parse( timestamp );
074        level = firstLine.substring( 24, 29 ).trim();
075        StringCutter cutter = new StringCutter( firstLine.substring( 31 ) );
076        logger = cutter.parseTo( "] " );
077        message = cutter.getRemaining();
078        this.throwable = throwable;
079        throwable_firstLine = ( secondLine == null ) ? "" : secondLine;
080        int index = throwable_firstLine.indexOf( ':' );
081        throwable_class = ( index > 0 ) ? throwable.substring( 0, index ) : "";
082        setAttributes( new String[] { factory.getLogSource(), timestamp, timestamp.substring( 0, 10 ),
083            timestamp.substring( 11, 23 ), level, logger, message, throwable, throwable_firstLine, throwable_class } );
084    }
085
086    public int compareTo( JBossLogEvent 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=\"warn\">\n"
109                + "    <description>WARN events</description>\n"
110                + "    <condition>\n"
111                + "      <match attribute=\"level\" type=\"equals\">WARN</match>\n"
112                + "    </condition>\n"
113                + "    <report publisher=\"file\"/>\n"
114                + "    <plugin type=\"sampling\">\n"
115                + "      <param name=\"attributes\">logger</param>\n"
116                + "    </plugin>\n"
117                + "  </group>\n"
118                + "\n"
119                + "  <group id=\"error\">\n"
120                + "    <description>ERROR events</description>\n"
121                + "    <condition>\n"
122                + "      <match attribute=\"level\" type=\"equals\">ERROR</match>\n"
123                + "    </condition>\n"
124                + "    <report publisher=\"file\"/>\n"
125                + "    <plugin type=\"sampling\">\n"
126                + "      <param name=\"attributes\">logger</param>\n"
127                + "    </plugin>\n"
128                + "  </group>";
129        }
130
131        public String getDefaultSamplingAttributes()
132        {
133            return "level,logger";
134        }
135    }
136
137    private static class Factory
138        extends LogEvent.Factory
139    {
140        private final LineNumberReader reader;
141
142        private String curLine;
143
144        public Factory( Description description, Reader reader, String logSource )
145            throws FileNotFoundException
146        {
147            super( description, logSource );
148            this.reader = new LineNumberReader( reader );
149        }
150
151        private final static Pattern DATE_PATTERN =
152            Pattern.compile( "\\A\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3} " );
153
154        /**
155         * Detect the start of a new log event
156         */
157        protected boolean detectLogEventStart( String line )
158        {
159            // heuristics: detects the beginning of the date...
160            return DATE_PATTERN.matcher( line ).find();
161        }
162
163        protected LogEvent readNextEvent()
164            throws IOException, ParseException
165        {
166            if ( curLine == null )
167            {
168                curLine = reader.readLine();
169                if ( curLine == null )
170                {
171                    // EOF
172                    return null;
173                }
174            }
175            String firstLine = curLine;
176            int lineNumber = reader.getLineNumber();
177
178            String secondLine = null;
179            StringBuffer buffer = new StringBuffer();
180            while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) )
181            {
182                if ( secondLine == null )
183                {
184                    secondLine = curLine;
185                }
186                else
187                {
188                    buffer.append( NEWLINE );
189                }
190                buffer.append( curLine );
191            }
192            return BUILDER.buildLogEvent( this, lineNumber, firstLine, secondLine, buffer.toString() );
193        }
194
195        private final static LogEventBuilder BUILDER = new LogEventBuilder()
196        {
197            protected LogEvent newEvent( LogEvent.Factory factory, String curLine, Object... objects )
198                throws ParseException
199            {
200                return new JBossLogEvent( factory, curLine, (String) objects[0], (String) objects[1] );
201            }
202        };
203    }
204}