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 }