1 /**
2 *
3 * Copyright 2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.geronimo.tomcat;
18
19 import org.apache.geronimo.gbean.GBeanInfo;
20 import org.apache.geronimo.gbean.GBeanInfoBuilder;
21 import org.apache.geronimo.system.serverinfo.ServerInfo;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.catalina.valves.AccessLogValve;
25
26 import java.util.*;
27 import java.util.regex.Pattern;
28 import java.util.regex.Matcher;
29 import java.io.File;
30 import java.io.FilenameFilter;
31 import java.io.RandomAccessFile;
32 import java.nio.channels.FileChannel;
33 import java.nio.MappedByteBuffer;
34 import java.nio.CharBuffer;
35 import java.nio.charset.Charset;
36 import java.text.SimpleDateFormat;
37 import java.text.ParseException;
38
39 /**
40 * Tomcat implementation of the WebAccessLog management interface.
41 *
42 * @version $Rev: 437623 $ $Date: 2006-08-28 02:48:23 -0700 (Mon, 28 Aug 2006) $
43 */
44 public class TomcatLogManagerImpl implements TomcatLogManager {
45 private final static Log log = LogFactory.getLog(TomcatLogManagerImpl.class);
46
47
48 private final static Pattern FILENAME_DATE_PATTERN = Pattern.compile("[-_ /.](((19|20)\\d\\d)[-_ /.](0[1-9]|1[012])[-_ /.](0[1-9]|[12][0-9]|3[01]))");
49 private final static int GROUP_FILENAME_FULL_DATE = 1;
50 private final static int GROUP_FILENAME_YEAR = 2;
51 private final static int GROUP_FILENAME_MONTH = 4;
52 private final static int GROUP_FILENAME_DAY = 5;
53
54
55
56 private final static String FILE_SEPARATOR_UNIX_STYLE = "/";
57 private final static String FILE_SEPARATOR_WIN_STYLE = "\\";
58
59
60 private final static Pattern FULL_LINE_PATTERN = Pattern.compile("^.*", Pattern.MULTILINE);
61 private final static Pattern ACCESS_LOG_PATTERN = Pattern.compile("(\\S*) (\\S*) (\\S*) \\[(.*)\\] \\\"(\\S*) (\\S*).*?\\\" (\\S*) (\\S*).*");
62 private final static int GROUP_HOST = 1;
63 private final static int GROUP_USER = 3;
64 private final static int GROUP_DATE = 4;
65 private final static int GROUP_METHOD = 5;
66 private final static int GROUP_URI = 6;
67 private final static int GROUP_RESPONSE_CODE = 7;
68 private final static int GROUP_RESPONSE_LENGTH = 8;
69 private final static String ACCESS_LOG_DATE_FORMAT = "dd/MMM/yyyy:HH:mm:ss ZZZZ";
70 private final static String LOG_FILE_NAME_FORMAT = "yyyy-MM-dd";
71 private final Collection logGbeans;
72 private final ServerInfo serverInfo;
73
74 public TomcatLogManagerImpl(ServerInfo serverInfo, Collection logGbeans) {
75 this.serverInfo = serverInfo;
76 this.logGbeans = logGbeans;
77 }
78
79 /**
80 * Gets the name of all logs used by this system. Typically there
81 * is only one, but specialized cases may use more.
82 *
83 * @return An array of all log names
84 *
85 */
86 public String[] getLogNames() {
87 List logNames = new ArrayList();
88 for (Iterator it = logGbeans.iterator(); it.hasNext();) {
89 ValveGBean logGBean = (ValveGBean) it.next();
90 AccessLogValve logFile = (AccessLogValve) logGBean.getInternalObject();
91 if(logFile != null) {
92 logNames.add( "var/catalina/logs/"+logFile.getPrefix()+LOG_FILE_NAME_FORMAT+logFile.getSuffix());
93 }
94 }
95 return (String[]) logNames.toArray(new String[logNames.size()]);
96 }
97
98 /**
99 * Gets the names of all log files for this log name.
100 *
101 * @param logName The name of the log for which to return the specific file names.
102 *
103 * @return An array of log file names
104 *
105 */
106 public String[] getLogFileNames(String logName) {
107 List names = new ArrayList();
108
109
110 File[] logFiles = getLogFiles(logName);
111
112 if (logFiles !=null) {
113 for (int i = 0; i < logFiles.length; i++) {
114 names.add(logFiles[i].getName());
115 }
116 }
117 return (String[]) names.toArray(new String[names.size()]);
118 }
119
120 /**
121 * Gets the name of all log files used by this log. Typically there
122 * is only one, but specialized cases may use more.
123 *
124 * @param logName The name of the log for which to return the specific files.
125 *
126 * @return An array of all log file names
127 *
128 */
129 private File[] getLogFiles(String logName) {
130 File[] logFiles = null;
131
132 try {
133 String fileNamePattern = logName;
134 if (fileNamePattern.indexOf(FILE_SEPARATOR_UNIX_STYLE) > -1) {
135 fileNamePattern = fileNamePattern.substring(fileNamePattern.lastIndexOf(FILE_SEPARATOR_UNIX_STYLE) + 1);
136 } else if (fileNamePattern.indexOf(FILE_SEPARATOR_WIN_STYLE) > -1) {
137 fileNamePattern = fileNamePattern.substring(fileNamePattern.lastIndexOf(FILE_SEPARATOR_WIN_STYLE) + 1);
138 }
139
140 String logFile = serverInfo.resolvePath(logName);
141
142 File parent = new File(logFile).getParentFile();
143
144 if (parent != null) {
145 logFiles = parent.listFiles(new PatternFilenameFilter(fileNamePattern));
146 }
147 } catch (Exception e) {
148 log.error("Exception attempting to locate Tomcat log files", e);
149 logFiles = new File[0];
150 }
151 return logFiles;
152 }
153
154 /**
155 * Searches the log for records matching the specified parameters. The
156 * maximum results returned will be the lesser of 1000 and the
157 * provided maxResults argument.
158 *
159 * @see #MAX_SEARCH_RESULTS
160 */
161 public SearchResults getMatchingItems(String logName, String host, String user, String method, String uri,
162 Date startDate, Date endDate, Integer skipResults, Integer maxResults) {
163
164
165 if(host != null && host.equals("")) host = null;
166 if(user != null && user.equals("")) user = null;
167 if(method != null && method.equals("")) method = null;
168 if(uri != null && uri.equals("")) uri = null;
169
170 long start = startDate == null ? 0 : startDate.getTime();
171 long end = endDate == null ? 0 : endDate.getTime();
172
173 List list = new LinkedList();
174 boolean capped = false;
175 int lineCount = 0, fileLineCount = 0;
176
177
178 File logFiles[] = getLogFiles(logName);
179
180 if (logFiles !=null) {
181 for (int i = 0; i < logFiles.length; i++) {
182 fileLineCount = 0;
183 try {
184
185 String fileName = logFiles[i].getName();
186 Matcher fileDate = FILENAME_DATE_PATTERN.matcher(fileName);
187 fileDate.find();
188 SimpleDateFormat simpleFileDate = new SimpleDateFormat(LOG_FILE_NAME_FORMAT);
189 long logFileTime = simpleFileDate.parse(fileDate.group(GROUP_FILENAME_FULL_DATE)).getTime();
190 Date logFileDate = new Date(logFileTime);
191
192
193 if ( (start==0 && end==0)
194 || (start>0 && start<=logFileTime && end>0 && end>=logFileTime)) {
195
196
197 RandomAccessFile raf = new RandomAccessFile(logFiles[i], "r");
198 FileChannel fc = raf.getChannel();
199 MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
200 CharBuffer cb = Charset.forName("US-ASCII").decode(bb);
201 Matcher lines = FULL_LINE_PATTERN.matcher(cb);
202 Matcher target = ACCESS_LOG_PATTERN.matcher("");
203 SimpleDateFormat format = (start == 0 && end == 0) ? null : new SimpleDateFormat(ACCESS_LOG_DATE_FORMAT);
204 int max = maxResults == null ? MAX_SEARCH_RESULTS : Math.min(maxResults.intValue(), MAX_SEARCH_RESULTS);
205
206 while(lines.find()) {
207 ++lineCount;
208 ++fileLineCount;
209 if(capped) {
210 continue;
211 }
212 CharSequence line = cb.subSequence(lines.start(), lines.end());
213 target.reset(line);
214 if(target.find()) {
215 if(host != null && !host.equals(target.group(GROUP_HOST))) {
216 continue;
217 }
218 if(user != null && !user.equals(target.group(GROUP_USER))) {
219 continue;
220 }
221 if(method != null && !method.equals(target.group(GROUP_METHOD))) {
222 continue;
223 }
224 if(uri != null && !target.group(GROUP_URI).startsWith(uri)) {
225 continue;
226 }
227 if(format != null) {
228 try {
229 long entry = format.parse(target.group(GROUP_DATE)).getTime();
230 if(start > entry) {
231 continue;
232 }
233 if(end > 0 && end < entry) {
234 continue;
235 }
236 } catch (ParseException e) {
237
238 }
239 }
240 if(skipResults != null && skipResults.intValue() > lineCount) {
241 continue;
242 }
243 if(list.size() > max) {
244 capped = true;
245 continue;
246 }
247 list.add(new LogMessage(fileLineCount,line.toString()));
248 }
249 }
250 fc.close();
251 raf.close();
252 }
253 } catch (Exception e) {
254 log.error("Unexpected error processing logs", e);
255 }
256 }
257 }
258 return new SearchResults(lineCount, (LogMessage[]) list.toArray(new LogMessage[list.size()]), capped);
259 }
260
261
262 public static final GBeanInfo GBEAN_INFO;
263
264 static {
265 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("Tomcat Log Manager", TomcatLogManagerImpl.class);
266 infoFactory.addReference("LogGBeans", ValveGBean.class);
267 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
268 infoFactory.addInterface(TomcatLogManager.class);
269
270 infoFactory.setConstructor(new String[]{"ServerInfo","LogGBeans"});
271 GBEAN_INFO = infoFactory.getBeanInfo();
272 }
273
274 public static GBeanInfo getGBeanInfo() {
275 return GBEAN_INFO;
276 }
277
278
279
280
281
282 static class PatternFilenameFilter implements FilenameFilter {
283 Pattern pattern;
284
285 PatternFilenameFilter(String fileNamePattern) {
286 fileNamePattern = fileNamePattern.replaceAll("yyyy", "\\\\d{4}");
287 fileNamePattern = fileNamePattern.replaceAll("yy", "\\\\d{2}");
288 fileNamePattern = fileNamePattern.replaceAll("mm", "\\\\d{2}");
289 fileNamePattern = fileNamePattern.replaceAll("MM", "\\\\d{2}");
290 fileNamePattern = fileNamePattern.replaceAll("dd", "\\\\d{2}")
291 + ".*";
292 this.pattern = Pattern.compile(fileNamePattern);
293 }
294
295 public boolean accept(File file, String fileName) {
296 return pattern.matcher(fileName).matches();
297 }
298 }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339 }