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