source: trunk/EventBenchConsole/src/de/ugoe/cs/eventbench/web/WeblogParser.java @ 224

Last change on this file since 224 was 224, checked in by sherbold, 13 years ago
  • added parameter maxLength to command loadSessionsFromClickstream and the WeblogParser? for pruning of unexplained very long sequences
  • Property svn:mime-type set to text/plain
File size: 8.0 KB
Line 
1package de.ugoe.cs.eventbench.web;
2
3import java.io.FileNotFoundException;
4import java.io.IOException;
5import java.net.URI;
6import java.net.URISyntaxException;
7import java.text.ParseException;
8import java.text.SimpleDateFormat;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.HashMap;
12import java.util.LinkedList;
13import java.util.List;
14import java.util.Map;
15
16import de.ugoe.cs.eventbench.web.data.WebEvent;
17import de.ugoe.cs.util.FileTools;
18import de.ugoe.cs.util.console.Console;
19
20/**
21 * <p>
22 * Provides functionality to parse log files with web request.
23 * </p>
24 *
25 * @author Steffen Herbold
26 * @version 1.0
27 */
28public class WeblogParser {
29
30        /**
31         * <p>
32         * Timeout between two sessions in milliseconds.
33         * </p>
34         */
35        private long timeout;
36
37        /**
38         * <p>
39         * Minimal length of a session. All shorter sessions will be pruned.
40         * Default: 2
41         * </p>
42         */
43        private int minLength = 2;
44
45        /**
46         * <p>
47         * Maximal length of a session. All longer sessions will be prunde. Default:
48         * 100
49         * </p>
50         */
51        private int maxLength = 100;
52
53        /**
54         * <p>
55         * Collection of generated sequences.
56         * </p>
57         */
58        private List<List<WebEvent>> sequences;
59
60        /**
61         * <p>
62         * Name and path of the robot filter.
63         * </p>
64         */
65        private static final String ROBOTFILTERFILE = "misc/robotfilter.txt";
66
67        /**
68         * <p>
69         * Field that contains a regular expression that matches all robots
70         * contained in {@link #ROBOTFILTERFILE}.
71         * </p>
72         */
73        private String robotRegex = null;
74
75        /**
76         * <p>
77         * Constructor. Creates a new WeblogParser with a default timeout of
78         * 3,600,000 milliseconds (1 hour).
79         * </p>
80         */
81        public WeblogParser() {
82                this(3600000);
83        }
84
85        /**
86         * <p>
87         * Constructor. Creates a new WeblogParser.
88         * </p>
89         *
90         * @param timeout
91         *            session timeout
92         */
93        public WeblogParser(long timeout) {
94                this.timeout = timeout;
95        }
96
97        /**
98         * <p>
99         * Returns the generated event sequences.
100         * </p>
101         *
102         * @return generated event sequences
103         */
104        public Collection<List<WebEvent>> getSequences() {
105                return sequences;
106        }
107
108        /**
109         * <p>
110         * Sets the session timeout.
111         * </p>
112         *
113         * @param timeout
114         *            new session timeout
115         */
116        public void setTimeout(long timeout) {
117                this.timeout = timeout;
118        }
119
120        /**
121         * <p>
122         * Sets the minimal length of a session. All sessions that contain less
123         * events will be pruned.
124         * </p>
125         *
126         * @param minLength
127         *            new minimal length
128         */
129        public void setMinLength(int minLength) {
130                this.minLength = minLength;
131        }
132
133        /**
134         * <p>
135         * Sets the maximal length of a session. All sessions that contain more
136         * events will be pruned.
137         * </p>
138         *
139         * @param maxLength
140         *            new maximal length
141         */
142        public void setMaxLength(int maxLength) {
143                this.maxLength = maxLength;
144        }
145
146        /**
147         * <p>
148         * Parses a web log file.
149         * </p>
150         *
151         * @param filename
152         *            name and path of the log file
153         * @throws IOException
154         *             thrown if there is a problem with reading the log file
155         * @throws FileNotFoundException
156         *             thrown if the log file is not found
157         * @throws ParseException
158         *             thrown the date format is invalid
159         */
160        public void parseFile(String filename) throws IOException,
161                        FileNotFoundException, ParseException {
162                String[] lines = FileTools.getLinesFromFile(filename);
163
164                Map<String, List<Integer>> cookieSessionMap = new HashMap<String, List<Integer>>();
165                int lastId = -1;
166
167                SimpleDateFormat dateFormat = new SimpleDateFormat(
168                                "yyyy-MM-dd HH:mm:ss");
169                loadRobotRegex();
170
171                sequences = new ArrayList<List<WebEvent>>();
172
173                int lineCounter = 0;
174                for (String line : lines) {
175                        lineCounter++;
176                        String[] values = line.substring(1, line.length() - 1).split(
177                                        "\" \"");
178
179                        // use cookie as session identifier
180                        int cookieStart = values[0].lastIndexOf('.');
181                        String cookie = values[0].substring(cookieStart + 1);
182                        String dateString = values[1];
183                        long timestamp = dateFormat.parse(dateString).getTime();
184                        String uriString = values[2];
185                        // String ref = values[3]; // referer is not yet used!
186                        String agent;
187                        if (values.length > 4) {
188                                agent = values[4];
189                        } else {
190                                agent = "noagent";
191                        }
192
193                        List<String> postedVars = new ArrayList<String>();
194                        if (values.length == 6) { // post vars found
195                                for (String postVar : values[5].trim().split(" ")) {
196                                        postedVars.add(postVar);
197                                }
198                        }
199                        if (!isRobot(agent)) {
200                                try {
201                                        URI uri = new URI(uriString);
202                                        String path = uri.getPath();
203                                        List<String> getVars = extractGetVarsFromUri(uri);
204
205                                        WebEvent event = new WebEvent(path, timestamp, postedVars,
206                                                        getVars);
207
208                                        // find session and add event
209                                        List<Integer> sessionIds = cookieSessionMap.get(cookie);
210                                        if (sessionIds == null) {
211                                                sessionIds = new ArrayList<Integer>();
212                                                // start new session
213                                                sessionIds.add(++lastId);
214                                                cookieSessionMap.put(cookie, sessionIds);
215                                                sequences.add(new LinkedList<WebEvent>());
216                                        }
217                                        Integer lastSessionIndex = sessionIds
218                                                        .get(sessionIds.size() - 1);
219                                        List<WebEvent> lastSession = sequences
220                                                        .get(lastSessionIndex);
221                                        long lastEventTime = timestamp;
222                                        if (!lastSession.isEmpty()) {
223                                                lastEventTime = lastSession.get(lastSession.size() - 1)
224                                                                .getTimestamp();
225                                        }
226                                        if (timestamp - lastEventTime > timeout) {
227                                                sessionIds.add(++lastId);
228                                                List<WebEvent> newSession = new LinkedList<WebEvent>();
229                                                newSession.add(event);
230                                                sequences.add(newSession);
231                                        } else {
232                                                lastSession.add(event);
233                                        }
234                                } catch (URISyntaxException e) {
235                                        Console.traceln("Ignored line " + lineCounter + ": "
236                                                        + e.getMessage());
237                                }
238                        }
239                }
240                pruneSequences();
241        }
242
243        /**
244         * <p>
245         * Prunes sequences shorter than {@link #minLength}.
246         * </p>
247         */
248        private void pruneSequences() {
249                Console.traceln("" + sequences.size() + " user sequences found");
250                // prune sequences shorter than min-length and longer than maxLength
251                int i = 0;
252                while (i < sequences.size()) {
253                        if ((sequences.get(i).size() < minLength)
254                                        || sequences.get(i).size() > maxLength) {
255                                sequences.remove(i);
256                        } else {
257                                i++;
258                        }
259                }
260                Console.traceln("" + sequences.size()
261                                + " remaining after pruning of sequences shorter than "
262                                + minLength);
263        }
264
265        /**
266         * <p>
267         * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
268         * matches all the robots defined in the file. The regular expression is
269         * stored in the field {@link #robotRegex}.
270         * </p>
271         *
272         * @throws IOException
273         *             thrown if there is a problem reading the robot filter
274         * @throws FileNotFoundException
275         *             thrown if the robot filter is not found
276         */
277        private void loadRobotRegex() throws IOException, FileNotFoundException {
278                String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
279                StringBuilder regex = new StringBuilder();
280                for (int i = 0; i < lines.length; i++) {
281                        regex.append("(.*" + lines[i] + ".*)");
282                        if (i != lines.length - 1) {
283                                regex.append('|');
284                        }
285                }
286                robotRegex = regex.toString();
287        }
288
289        /**
290         * <p>
291         * Checks whether an agent is a robot.
292         * </p>
293         *
294         * @param agent
295         *            agent that is checked
296         * @return true, if the agent is a robot; false otherwise
297         */
298        private boolean isRobot(String agent) {
299                return agent.matches(robotRegex);
300        }
301
302        /**
303         * <p>
304         * Parses the URI and extracts the GET variables that have been passed.
305         * </p>
306         *
307         * @param uri
308         *            URI that is parsed
309         * @return a list with all GET variables
310         */
311        private List<String> extractGetVarsFromUri(URI uri) {
312                List<String> getVars = new ArrayList<String>();
313                String query = uri.getQuery();
314                if (query != null) {
315                        String[] paramPairs = query.split("&");
316                        for (String paramPair : paramPairs) {
317                                String[] paramSplit = paramPair.split("=");
318                                getVars.add(paramSplit[0]);
319                        }
320                }
321                return getVars;
322        }
323}
Note: See TracBrowser for help on using the repository browser.