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