View Javadoc
1   package net.sf.logdistiller.logtypes;
2   
3   /*
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  import java.io.*;
18  import java.text.SimpleDateFormat;
19  import java.text.DateFormat;
20  import java.text.ParseException;
21  import java.util.Date;
22  
23  import org.apache.commons.lang.StringUtils;
24  
25  import net.sf.logdistiller.LogEvent;
26  import net.sf.logdistiller.LogType;
27  import net.sf.logdistiller.util.LogEventBuilder;
28  import net.sf.logdistiller.util.StringCutter;
29  
30  /**
31   * Log event for log4j XMLLayout (<code>log4j-XML</code>). By default, the classification rules generated by the GUI for
32   * this type of logs will sort events based on the following attributes: <code>level</code>, then <code>logger</code>.
33   */
34  public class Log4jXmlLogEvent
35      extends LogEvent
36      implements Comparable<Log4jXmlLogEvent>
37  {
38      public final static String ID = "log4j-XML";
39  
40      public final String logger;
41  
42      public final String timestamp;
43  
44      public final String datetime;
45  
46      public final long timestampValue;
47  
48      public final String level;
49  
50      public final String thread;
51  
52      public final String message;
53  
54      public final String ndc;
55  
56      public final String throwable;
57  
58      public final String locationInfoClass;
59  
60      public final String locationInfoMethod;
61  
62      public final String locationInfoFile;
63  
64      public final String locationInfoLine;
65  
66      public final static LogType LOGTYPE = new LogType.Basic( ID );
67  
68      private final static DateFormat DATE_FORMAT = new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" );
69  
70      private final static String LOGEVENT_START = "<log4j:event logger=\"";
71  
72      private final static String[] ATTRIBUTE_NAMES =
73          { "logSource", "datetime", "timestamp", "level", "logger", "thread", "message", "NDC", "throwable",
74              "locationInfo.class", "locationInfo.method", "locationInfo.file", "locationInfo.line" };
75  
76      public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES );
77  
78      public Log4jXmlLogEvent( LogEvent.Factory factory, String rawLog )
79          throws ParseException
80      {
81          super( factory, rawLog );
82          StringCutter cutter = new StringCutter( rawLog );
83          cutter.parseTo( LOGEVENT_START );
84          logger = cutter.parseTo( "\" timestamp=\"" );
85          timestamp = cutter.parseTo( "\" level=\"" );
86          timestampValue = Long.parseLong( timestamp );
87          datetime = DATE_FORMAT.format( new Date( timestampValue ) );
88          level = cutter.parseTo( "\" thread=\"" );
89          thread = cutter.parseTo( "\">" );
90          cutter.parseTo( "<log4j:message><![CDATA[" );
91          message = unescapeCDATA( cutter.parseTo( "]]></log4j:message>" ) );
92  
93          String remaining = cutter.getRemaining();
94          if ( remaining.indexOf( "<log4j:NDC>" ) < 0 )
95          {
96              ndc = "";
97          }
98          else
99          {
100             cutter.parseTo( "<log4j:NDC><![CDATA[" );
101             ndc = unescapeCDATA( cutter.parseTo( "]]></log4j:NDC>" ) );
102             remaining = cutter.getRemaining();
103         }
104         if ( remaining.indexOf( "<log4j:throwable>" ) < 0 )
105         {
106             throwable = "";
107         }
108         else
109         {
110             cutter.parseTo( "<log4j:throwable><![CDATA[" );
111             throwable = unescapeCDATA( cutter.parseTo( "]]></log4j:throwable>" ) );
112             remaining = cutter.getRemaining();
113         }
114         if ( remaining.indexOf( "<log4j:locationInfo" ) < 0 )
115         {
116             locationInfoClass = "";
117             locationInfoMethod = "";
118             locationInfoFile = "";
119             locationInfoLine = "";
120         }
121         else
122         {
123             cutter.parseTo( "<log4j:locationInfo class=\"" );
124             locationInfoClass = cutter.parseTo( "\" method=\"" );
125             locationInfoMethod = cutter.parseTo( "\" file=\"" );
126             locationInfoFile = cutter.parseTo( "\" line=\"" );
127             locationInfoLine = cutter.parseTo( "\"/>" );
128         }
129         setAttributes( new String[] { factory.getLogSource(), datetime, timestamp, level, logger, thread, message, ndc,
130             throwable, locationInfoClass, locationInfoMethod, locationInfoFile, locationInfoLine } );
131     }
132 
133     public static String unescapeCDATA( String cdata )
134     {
135         return StringUtils.replace( cdata, "]]>]]&gt;<![CDATA[", "]]>" );
136     }
137 
138     public int compareTo( Log4jXmlLogEvent o )
139     {
140         long diff = timestampValue - o.timestampValue;
141         return ( diff < 0 ) ? -1 : ( diff > 0 ) ? 1 : 0;
142     }
143 
144     private static class Description
145         extends LogType.Description
146     {
147         public Description( LogType.Basic logtype, String[] attributeNames )
148         {
149             super( logtype, attributeNames );
150             logtype.setDescription( this );
151         }
152 
153         public LogEvent.Factory newFactory( Reader reader, String logSource )
154             throws IOException
155         {
156             return new Factory( this, reader, logSource );
157         }
158 
159         public String getDefaultSpecificGroups()
160         {
161             return "  <group id=\"warn\">\n"
162                 + "    <description>WARN events</description>\n"
163                 + "    <condition>\n"
164                 + "      <match attribute=\"level\" type=\"equals\">WARN</match>\n"
165                 + "    </condition>\n"
166                 + "    <report publisher=\"file\"/>\n"
167                 + "    <plugin type=\"sampling\">\n"
168                 + "      <param name=\"attributes\">logger</param>\n"
169                 + "    </plugin>\n"
170                 + "  </group>\n"
171                 + "\n"
172                 + "  <group id=\"error\">\n"
173                 + "    <description>ERROR events</description>\n"
174                 + "    <condition>\n"
175                 + "      <match attribute=\"level\" type=\"equals\">ERROR</match>\n"
176                 + "    </condition>\n"
177                 + "    <report publisher=\"file\"/>\n"
178                 + "    <plugin type=\"sampling\">\n"
179                 + "      <param name=\"attributes\">logger</param>\n"
180                 + "    </plugin>\n"
181                 + "  </group>";
182         }
183 
184         public String getDefaultSamplingAttributes()
185         {
186             return "level,logger";
187         }
188     }
189 
190     private static class Factory
191         extends LogEvent.Factory
192     {
193         private final LineNumberReader reader;
194 
195         private String curLine;
196 
197         public Factory( Description description, Reader reader, String logSource )
198             throws FileNotFoundException
199         {
200             super( description, logSource );
201             this.reader = new LineNumberReader( reader );
202         }
203 
204         protected boolean detectLogEventStart( String line )
205         {
206             return line.startsWith( LOGEVENT_START );
207         }
208 
209         protected LogEvent readNextEvent()
210             throws IOException, ParseException
211         {
212             if ( curLine == null )
213             {
214                 curLine = reader.readLine();
215                 if ( curLine == null )
216                 {
217                     // EOF
218                     return null;
219                 }
220             }
221             if ( !detectLogEventStart( curLine ) )
222             {
223                 throw new ParseException(
224                                           "bad log format, beginnig of line " + reader.getLineNumber() + ": " + curLine,
225                                           0 );
226             }
227             StringBuffer buffer = new StringBuffer( curLine );
228             int lineNumber = reader.getLineNumber();
229 
230             while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) )
231             {
232                 buffer.append( "\r\n" ); // fixed newline, as log4j
233                 buffer.append( curLine );
234             }
235             return BUILDER.buildLogEvent( this, lineNumber, buffer.toString() );
236         }
237 
238         private final static LogEventBuilder BUILDER = new LogEventBuilder()
239         {
240             protected LogEvent newEvent( LogEvent.Factory factory, String curLine, Object... objects )
241                 throws ParseException
242             {
243                 return new Log4jXmlLogEvent( factory, curLine );
244             }
245         };
246     }
247 }