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.StringCutter;
28  
29  /**
30   * Log event for log4j XMLLayout (<code>log4j-XML</code>). By default, the classification rules generated by the GUI for
31   * this type of logs will sort events based on the following attributes: <code>level</code>, then <code>logger</code>.
32   */
33  public class Log4jXmlLogEvent
34      extends LogEvent
35      implements Comparable
36  {
37      public final static String ID = "log4j-XML";
38  
39      public final String logger;
40  
41      public final String timestamp;
42  
43      public final String datetime;
44  
45      public final long timestampValue;
46  
47      public final String level;
48  
49      public final String thread;
50  
51      public final String message;
52  
53      public final String ndc;
54  
55      public final String throwable;
56  
57      public final String locationInfoClass;
58  
59      public final String locationInfoMethod;
60  
61      public final String locationInfoFile;
62  
63      public final String locationInfoLine;
64  
65      public final static LogType LOGTYPE = new LogType.Basic( ID );
66  
67      private final static DateFormat DATE_FORMAT = new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" );
68  
69      private final static String LOGEVENT_START = "<log4j:event logger=\"";
70  
71      private final static String[] ATTRIBUTE_NAMES =
72          { "logSource", "datetime", "timestamp", "level", "logger", "thread", "message", "NDC", "throwable",
73              "locationInfo.class", "locationInfo.method", "locationInfo.file", "locationInfo.line" };
74  
75      public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES );
76  
77      public Log4jXmlLogEvent( Factory factory, String rawLog )
78          throws ParseException
79      {
80          super( factory, rawLog );
81          StringCutter cutter = new StringCutter( rawLog );
82          cutter.parseTo( LOGEVENT_START );
83          logger = cutter.parseTo( "\" timestamp=\"" );
84          timestamp = cutter.parseTo( "\" level=\"" );
85          timestampValue = Long.parseLong( timestamp );
86          datetime = DATE_FORMAT.format( new Date( timestampValue ) );
87          level = cutter.parseTo( "\" thread=\"" );
88          thread = cutter.parseTo( "\">" );
89          cutter.parseTo( "<log4j:message><![CDATA[" );
90          message = unescapeCDATA( cutter.parseTo( "]]></log4j:message>" ) );
91  
92          String remaining = cutter.getRemaining();
93          if ( remaining.indexOf( "<log4j:NDC>" ) < 0 )
94          {
95              ndc = "";
96          }
97          else
98          {
99              cutter.parseTo( "<log4j:NDC><![CDATA[" );
100             ndc = unescapeCDATA( cutter.parseTo( "]]></log4j:NDC>" ) );
101             remaining = cutter.getRemaining();
102         }
103         if ( remaining.indexOf( "<log4j:throwable>" ) < 0 )
104         {
105             throwable = "";
106         }
107         else
108         {
109             cutter.parseTo( "<log4j:throwable><![CDATA[" );
110             throwable = unescapeCDATA( cutter.parseTo( "]]></log4j:throwable>" ) );
111             remaining = cutter.getRemaining();
112         }
113         if ( remaining.indexOf( "<log4j:locationInfo" ) < 0 )
114         {
115             locationInfoClass = "";
116             locationInfoMethod = "";
117             locationInfoFile = "";
118             locationInfoLine = "";
119         }
120         else
121         {
122             cutter.parseTo( "<log4j:locationInfo class=\"" );
123             locationInfoClass = cutter.parseTo( "\" method=\"" );
124             locationInfoMethod = cutter.parseTo( "\" file=\"" );
125             locationInfoFile = cutter.parseTo( "\" line=\"" );
126             locationInfoLine = cutter.parseTo( "\"/>" );
127         }
128         setAttributes( new String[] { factory.getLogSource(), datetime, timestamp, level, logger, thread, message, ndc,
129             throwable, locationInfoClass, locationInfoMethod, locationInfoFile, locationInfoLine } );
130     }
131 
132     public static String unescapeCDATA( String cdata )
133     {
134         return StringUtils.replace( cdata, "]]>]]&gt;<![CDATA[", "]]>" );
135     }
136 
137     public int compareTo( Object o )
138     {
139         long diff = timestampValue - ( (Log4jXmlLogEvent) o ).timestampValue;
140         return ( diff < 0 ) ? -1 : ( diff > 0 ) ? 1 : 0;
141     }
142 
143     private static class Description
144         extends LogType.Description
145     {
146         public Description( LogType.Basic logtype, String[] attributeNames )
147         {
148             super( logtype, attributeNames );
149             logtype.setDescription( this );
150         }
151 
152         public LogEvent.Factory newFactory( Reader reader, String logSource )
153             throws IOException
154         {
155             return new Factory( this, reader, logSource );
156         }
157 
158         public String getDefaultSpecificGroups()
159         {
160             return "  <group id=\"warn\">\n"
161                 + "    <description>WARN events</description>\n"
162                 + "    <condition>\n"
163                 + "      <match attribute=\"level\" type=\"equals\">WARN</match>\n"
164                 + "    </condition>\n"
165                 + "    <report publisher=\"file\"/>\n"
166                 + "    <plugin type=\"sampling\">\n"
167                 + "      <param name=\"attributes\">logger</param>\n"
168                 + "    </plugin>\n"
169                 + "  </group>\n"
170                 + "\n"
171                 + "  <group id=\"error\">\n"
172                 + "    <description>ERROR events</description>\n"
173                 + "    <condition>\n"
174                 + "      <match attribute=\"level\" type=\"equals\">ERROR</match>\n"
175                 + "    </condition>\n"
176                 + "    <report publisher=\"file\"/>\n"
177                 + "    <plugin type=\"sampling\">\n"
178                 + "      <param name=\"attributes\">logger</param>\n"
179                 + "    </plugin>\n"
180                 + "  </group>";
181         }
182 
183         public String getDefaultSamplingAttributes()
184         {
185             return "level,logger";
186         }
187     }
188 
189     private static class Factory
190         extends LogEvent.Factory
191     {
192         private final LineNumberReader reader;
193 
194         private String curLine;
195 
196         public Factory( Description description, Reader reader, String logSource )
197             throws FileNotFoundException
198         {
199             super( description, logSource );
200             this.reader = new LineNumberReader( reader );
201         }
202 
203         protected boolean detectLogEventStart( String line )
204         {
205             return line.startsWith( LOGEVENT_START );
206         }
207 
208         protected LogEvent readNextEvent()
209             throws IOException, ParseException
210         {
211             if ( curLine == null )
212             {
213                 curLine = reader.readLine();
214                 if ( curLine == null )
215                 {
216                     // EOF
217                     return null;
218                 }
219             }
220             if ( !detectLogEventStart( curLine ) )
221             {
222                 throw new ParseException(
223                                           "bad log format, beginnig of line " + reader.getLineNumber() + ": " + curLine,
224                                           0 );
225             }
226             StringBuffer buffer = new StringBuffer( curLine );
227             while ( ( ( curLine = reader.readLine() ) != null ) && ( !detectLogEventStart( curLine ) ) )
228             {
229                 buffer.append( "\r\n" ); // fixed newline, as log4j
230                 buffer.append( curLine );
231             }
232             return new Log4jXmlLogEvent( this, buffer.toString() );
233         }
234     }
235 }