001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.log4j.lf5.util; 018 019import java.io.BufferedInputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.io.InputStream; 025import java.text.ParseException; 026import java.text.SimpleDateFormat; 027import java.util.Date; 028 029import javax.swing.SwingUtilities; 030 031import org.apache.log4j.lf5.Log4JLogRecord; 032import org.apache.log4j.lf5.LogLevel; 033import org.apache.log4j.lf5.LogLevelFormatException; 034import org.apache.log4j.lf5.LogRecord; 035import org.apache.log4j.lf5.viewer.LogBrokerMonitor; 036import org.apache.log4j.lf5.viewer.LogFactor5ErrorDialog; 037import org.apache.log4j.lf5.viewer.LogFactor5LoadingDialog; 038 039/** 040 * Provides utility methods for input and output streams. 041 * 042 * @author Brad Marlborough 043 * @author Richard Hurst 044 */ 045 046// Contributed by ThoughtWorks Inc. 047 048public class LogFileParser implements Runnable { 049 //-------------------------------------------------------------------------- 050 // Constants: 051 //-------------------------------------------------------------------------- 052 public static final String RECORD_DELIMITER = "[slf5s.start]"; 053 public static final String ATTRIBUTE_DELIMITER = "[slf5s."; 054 public static final String DATE_DELIMITER = ATTRIBUTE_DELIMITER + "DATE]"; 055 public static final String THREAD_DELIMITER = ATTRIBUTE_DELIMITER + "THREAD]"; 056 public static final String CATEGORY_DELIMITER = ATTRIBUTE_DELIMITER + "CATEGORY]"; 057 public static final String LOCATION_DELIMITER = ATTRIBUTE_DELIMITER + "LOCATION]"; 058 public static final String MESSAGE_DELIMITER = ATTRIBUTE_DELIMITER + "MESSAGE]"; 059 public static final String PRIORITY_DELIMITER = ATTRIBUTE_DELIMITER + "PRIORITY]"; 060 public static final String NDC_DELIMITER = ATTRIBUTE_DELIMITER + "NDC]"; 061 062 //-------------------------------------------------------------------------- 063 // Protected Variables: 064 //-------------------------------------------------------------------------- 065 066 //-------------------------------------------------------------------------- 067 // Private Variables: 068 //-------------------------------------------------------------------------- 069 private static SimpleDateFormat _sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,S"); 070 private LogBrokerMonitor _monitor; 071 LogFactor5LoadingDialog _loadDialog; 072 private InputStream _in = null; 073 074 //-------------------------------------------------------------------------- 075 // Constructors: 076 //-------------------------------------------------------------------------- 077 public LogFileParser(File file) throws IOException, 078 FileNotFoundException { 079 this(new FileInputStream(file)); 080 } 081 082 public LogFileParser(InputStream stream) throws IOException { 083 _in = stream; 084 } 085 //-------------------------------------------------------------------------- 086 // Public Methods: 087 //-------------------------------------------------------------------------- 088 089 /** 090 * Starts a new thread to parse the log file and create a LogRecord. 091 * See run(). 092 * @param monitor LogBrokerMonitor 093 */ 094 public void parse(LogBrokerMonitor monitor) throws RuntimeException { 095 _monitor = monitor; 096 Thread t = new Thread(this); 097 t.start(); 098 } 099 100 /** 101 * Parses the file and creates new log records and adds the record 102 * to the monitor. 103 */ 104 public void run() { 105 106 int index = 0; 107 int counter = 0; 108 LogRecord temp; 109 boolean isLogFile = false; 110 111 _loadDialog = new LogFactor5LoadingDialog( 112 _monitor.getBaseFrame(), "Loading file..."); 113 114 115 try { 116 String logRecords = loadLogFile(_in); 117 118 while ((counter = logRecords.indexOf(RECORD_DELIMITER, index)) != -1) { 119 temp = createLogRecord(logRecords.substring(index, counter)); 120 isLogFile = true; 121 122 if (temp != null) { 123 _monitor.addMessage(temp); 124 } 125 126 index = counter + RECORD_DELIMITER.length(); 127 } 128 129 if (index < logRecords.length() && isLogFile) { 130 temp = createLogRecord(logRecords.substring(index)); 131 132 if (temp != null) { 133 _monitor.addMessage(temp); 134 } 135 } 136 137 if (isLogFile == false) { 138 throw new RuntimeException("Invalid log file format"); 139 } 140 SwingUtilities.invokeLater(new Runnable() { 141 public void run() { 142 destroyDialog(); 143 } 144 }); 145 146 } catch (RuntimeException e) { 147 destroyDialog(); 148 displayError("Error - Invalid log file format.\nPlease see documentation" 149 + " on how to load log files."); 150 } catch (IOException e) { 151 destroyDialog(); 152 displayError("Error - Unable to load log file!"); 153 } 154 155 _in = null; 156 } 157 158 //-------------------------------------------------------------------------- 159 // Protected Methods: 160 //-------------------------------------------------------------------------- 161 protected void displayError(String message) { 162 LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( 163 _monitor.getBaseFrame(), message); 164 165 } 166 167 //-------------------------------------------------------------------------- 168 // Private Methods: 169 //-------------------------------------------------------------------------- 170 private void destroyDialog() { 171 _loadDialog.hide(); 172 _loadDialog.dispose(); 173 } 174 175 /** 176 * Loads a log file from a web server into the LogFactor5 GUI. 177 */ 178 private String loadLogFile(InputStream stream) throws IOException { 179 BufferedInputStream br = new BufferedInputStream(stream); 180 181 int count = 0; 182 int size = br.available(); 183 184 StringBuffer sb = null; 185 if (size > 0) { 186 sb = new StringBuffer(size); 187 } else { 188 sb = new StringBuffer(1024); 189 } 190 191 while ((count = br.read()) != -1) { 192 sb.append((char) count); 193 } 194 195 br.close(); 196 br = null; 197 return sb.toString(); 198 199 } 200 201 private String parseAttribute(String name, String record) { 202 203 int index = record.indexOf(name); 204 205 if (index == -1) { 206 return null; 207 } 208 209 return getAttribute(index, record); 210 } 211 212 private long parseDate(String record) { 213 try { 214 String s = parseAttribute(DATE_DELIMITER, record); 215 216 if (s == null) { 217 return 0; 218 } 219 220 Date d = _sdf.parse(s); 221 222 return d.getTime(); 223 } catch (ParseException e) { 224 return 0; 225 } 226 } 227 228 private LogLevel parsePriority(String record) { 229 String temp = parseAttribute(PRIORITY_DELIMITER, record); 230 231 if (temp != null) { 232 try { 233 return LogLevel.valueOf(temp); 234 } catch (LogLevelFormatException e) { 235 return LogLevel.DEBUG; 236 } 237 238 } 239 240 return LogLevel.DEBUG; 241 } 242 243 private String parseThread(String record) { 244 return parseAttribute(THREAD_DELIMITER, record); 245 } 246 247 private String parseCategory(String record) { 248 return parseAttribute(CATEGORY_DELIMITER, record); 249 } 250 251 private String parseLocation(String record) { 252 return parseAttribute(LOCATION_DELIMITER, record); 253 } 254 255 private String parseMessage(String record) { 256 return parseAttribute(MESSAGE_DELIMITER, record); 257 } 258 259 private String parseNDC(String record) { 260 return parseAttribute(NDC_DELIMITER, record); 261 } 262 263 private String parseThrowable(String record) { 264 return getAttribute(record.length(), record); 265 } 266 267 private LogRecord createLogRecord(String record) { 268 if (record == null || record.trim().length() == 0) { 269 return null; 270 } 271 272 LogRecord lr = new Log4JLogRecord(); 273 lr.setMillis(parseDate(record)); 274 lr.setLevel(parsePriority(record)); 275 lr.setCategory(parseCategory(record)); 276 lr.setLocation(parseLocation(record)); 277 lr.setThreadDescription(parseThread(record)); 278 lr.setNDC(parseNDC(record)); 279 lr.setMessage(parseMessage(record)); 280 lr.setThrownStackTrace(parseThrowable(record)); 281 282 return lr; 283 } 284 285 286 private String getAttribute(int index, String record) { 287 int start = record.lastIndexOf(ATTRIBUTE_DELIMITER, index - 1); 288 289 if (start == -1) { 290 return record.substring(0, index); 291 } 292 293 start = record.indexOf("]", start); 294 295 return record.substring(start + 1, index).trim(); 296 } 297 //-------------------------------------------------------------------------- 298 // Nested Top-Level Classes or Interfaces 299 //-------------------------------------------------------------------------- 300 301}