ClickSpotter 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +167 -0
- data/bin/clickspotter +4 -0
- data/data/worldmap-large.jpg +0 -0
- data/data/worldmap.png +0 -0
- data/lib/About.rb +22 -0
- data/lib/AboutDlg.rb +169 -0
- data/lib/AppConfig.rb +68 -0
- data/lib/AsyncResolverWithCache.rb +207 -0
- data/lib/BrowserLauncher.rb +23 -0
- data/lib/CSListView.rb +118 -0
- data/lib/CSListViewItem.rb +49 -0
- data/lib/ClickableCanvasView.rb +24 -0
- data/lib/Configuration.rb +123 -0
- data/lib/ConfigurationDlg.rb +372 -0
- data/lib/DNSResolver.rb +53 -0
- data/lib/GSServerLogSettings.rb +24 -0
- data/lib/GeoDistView.rb +351 -0
- data/lib/GeoLocator.rb +91 -0
- data/lib/GlobalSettings.rb +237 -0
- data/lib/HitRecord.rb +39 -0
- data/lib/HostNameDlg.rb +83 -0
- data/lib/LogFileSelector.rb +65 -0
- data/lib/LogFileSelectorDlg.rb +97 -0
- data/lib/LogParser.rb +120 -0
- data/lib/MainWindow.rb +627 -0
- data/lib/MainWindowDlg.rb +1054 -0
- data/lib/OccurenceCounter.rb +65 -0
- data/lib/PageInformation.rb +147 -0
- data/lib/PageInformationDlg.rb +159 -0
- data/lib/PageRecord.rb +77 -0
- data/lib/RefererInformation.rb +114 -0
- data/lib/RefererInformationDlg.rb +106 -0
- data/lib/RefererRecord.rb +51 -0
- data/lib/ServerLogSettings.rb +152 -0
- data/lib/ServerLogSettingsDlg.rb +202 -0
- data/lib/StatusCode.rb +72 -0
- data/lib/TraceRouter.rb +53 -0
- data/lib/VisitorInformation.rb +132 -0
- data/lib/VisitorInformationDlg.rb +351 -0
- data/lib/VisitorRecord.rb +94 -0
- data/lib/WorldMap.rb +93 -0
- data/lib/WorldMapDlg.rb +166 -0
- data/lib/clickspotter.rb +38 -0
- metadata +87 -0
data/lib/LogParser.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
#
|
2
|
+
# LogParser.rb - ClickSpotter
|
3
|
+
#
|
4
|
+
# Copyright (c) 2005, 2006 by Chris Schlaeger <cs@kde.org>
|
5
|
+
# This program is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of version 2 of the GNU General Public License as
|
7
|
+
# published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# $Id: LogParser.rb 9 2006-01-23 08:41:35Z cs $
|
10
|
+
#
|
11
|
+
|
12
|
+
class LogParser
|
13
|
+
|
14
|
+
def initialize()
|
15
|
+
@fileName = nil
|
16
|
+
@stream = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def setFileName(fileName)
|
20
|
+
return if fileName == @fileName
|
21
|
+
if @stream != nil
|
22
|
+
@stream.close
|
23
|
+
@progressDlg = nil
|
24
|
+
@stream = nil
|
25
|
+
end
|
26
|
+
@fileName = fileName
|
27
|
+
end
|
28
|
+
|
29
|
+
def getNextEntry(deadline)
|
30
|
+
# Make sure the file is open for reading. If not, open it and setup
|
31
|
+
# a progress dialog in case it's a big file.
|
32
|
+
if @stream == nil
|
33
|
+
@stream = File.new(@fileName, 'r')
|
34
|
+
@progressDlg = Qt::ProgressDialog.new(nil, nil, true)
|
35
|
+
@progressDlg.setLabelText("Loading file #{@fileName} ...")
|
36
|
+
@sizeAtOpen = File.size(@fileName)
|
37
|
+
@progressDlg.setTotalSteps(@sizeAtOpen)
|
38
|
+
@updateCounter = 0
|
39
|
+
end
|
40
|
+
|
41
|
+
# Update the progress dialog (even when it's not shown)
|
42
|
+
if @progressDlg != nil
|
43
|
+
if @stream.pos() >= @sizeAtOpen
|
44
|
+
@progressDlg.setProgress(@sizeAtOpen)
|
45
|
+
@progressDlg = nil
|
46
|
+
else
|
47
|
+
@updateCounter += 1
|
48
|
+
@progressDlg.setProgress(@stream.pos()) if @updateCounter % 100 == 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# read one line from log and process it. Ignore all lines that are older
|
53
|
+
# than the requested deadline
|
54
|
+
begin
|
55
|
+
line = @stream.gets
|
56
|
+
if line == nil
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
tokens = line.chomp().split(' ')
|
60
|
+
if tokens.size < 10
|
61
|
+
puts "Discarding corrupted log line: #{line}"
|
62
|
+
next
|
63
|
+
end
|
64
|
+
ip = tokens[0]
|
65
|
+
uid = tokens[1]
|
66
|
+
timeStr = tokens[3].gsub(/[\[]/, '').gsub(/\//, ' ').sub(/:/, ' ')
|
67
|
+
begin
|
68
|
+
timeStamp = Time.parse(timeStr)
|
69
|
+
rescue
|
70
|
+
puts "Broken timestamp: #{line}"
|
71
|
+
timeStamp = 0
|
72
|
+
next
|
73
|
+
end
|
74
|
+
end while timeStamp < deadline
|
75
|
+
|
76
|
+
request = tokens[5] + ' ' + tokens[6] + ' ' + tokens[7]
|
77
|
+
isPage = false
|
78
|
+
if tokens[5].sub(/"/, '') == 'GET'
|
79
|
+
url = tokens[6]
|
80
|
+
url = '/' if url == nil || url.empty?
|
81
|
+
# Remove trailing slashes except for the base page
|
82
|
+
url = url[0..-2] if url[-1..-1] == '/' && url != '/'
|
83
|
+
|
84
|
+
if not $globals.isAuxFile(url)
|
85
|
+
isPage = true
|
86
|
+
end
|
87
|
+
else
|
88
|
+
url = nil
|
89
|
+
end
|
90
|
+
request.gsub!(/"/, '')
|
91
|
+
code = tokens[8].to_i
|
92
|
+
bytes = tokens[9].to_i
|
93
|
+
referer = tokens[10] ? tokens[10].gsub(/"/, '') : ''
|
94
|
+
# Remove the local server name from local referer URLs
|
95
|
+
$globals.serverNames.each do |name|
|
96
|
+
if referer.length >= name.length &&
|
97
|
+
referer.slice(0 , name.length) == name
|
98
|
+
referer = referer.slice(name.length,
|
99
|
+
referer.length - name.length)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
# Remove trailing slashes except for the base page
|
103
|
+
referer = referer[0..-2] if !referer.empty? && referer[-1..-1] == '/' && \
|
104
|
+
referer != '/'
|
105
|
+
referer = nil if referer == '-'
|
106
|
+
|
107
|
+
if tokens[11]
|
108
|
+
browser = tokens[11..tokens.length() - 1].join(' ').gsub(/"/, '')
|
109
|
+
else
|
110
|
+
browser = ''
|
111
|
+
end
|
112
|
+
|
113
|
+
return {
|
114
|
+
'ip' => ip, 'uid' => uid, 'timeStamp' => timeStamp, 'request' => request,
|
115
|
+
'url' => url, 'code' => code, 'bytes' => bytes,
|
116
|
+
'referer' => referer, 'browser' => browser, 'isPage' => isPage}
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
data/lib/MainWindow.rb
ADDED
@@ -0,0 +1,627 @@
|
|
1
|
+
#
|
2
|
+
# MainWindow.rb - ClickSpotter
|
3
|
+
#
|
4
|
+
# Copyright (c) 2005, 2006 by Chris Schlaeger <cs@kde.org>
|
5
|
+
# This program is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of version 2 of the GNU General Public License as
|
7
|
+
# published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# $Id: MainWindow.rb 10 2006-01-23 14:29:58Z cs $
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'Qt'
|
13
|
+
require 'time'
|
14
|
+
require 'GlobalSettings'
|
15
|
+
require 'GeoLocator'
|
16
|
+
require 'DNSResolver'
|
17
|
+
require 'MainWindowDlg'
|
18
|
+
require 'LogFileSelector'
|
19
|
+
require 'About'
|
20
|
+
require 'ServerLogSettings'
|
21
|
+
require 'VisitorInformation'
|
22
|
+
require 'PageInformation'
|
23
|
+
require 'RefererInformation'
|
24
|
+
require 'LogParser'
|
25
|
+
require 'HitRecord'
|
26
|
+
require 'VisitorRecord'
|
27
|
+
require 'PageRecord'
|
28
|
+
require 'RefererRecord'
|
29
|
+
require 'StatusCode'
|
30
|
+
require 'OccurenceCounter'
|
31
|
+
require 'Configuration'
|
32
|
+
require 'GeoDistView'
|
33
|
+
require 'BrowserLauncher'
|
34
|
+
require 'CSListView'
|
35
|
+
|
36
|
+
PurgeInterval = 20
|
37
|
+
|
38
|
+
class MainWindow < MainWindowDlg
|
39
|
+
|
40
|
+
include BrowserLauncher
|
41
|
+
|
42
|
+
slots 'update()', 'updateTab(QWidget*)', 'clearVisitorFilter()',
|
43
|
+
'clearPageFilter()', 'clearRefererFilter()',
|
44
|
+
'recentHostClicked(QListViewItem*)',
|
45
|
+
'topPageClicked(QListViewItem*)',
|
46
|
+
'topRefererClicked(QListViewItem*)',
|
47
|
+
'hostClicked(QListViewItem*)',
|
48
|
+
'pageClicked(QListViewItem*)',
|
49
|
+
'refererClicked(QListViewItem*)',
|
50
|
+
'tabChanged(QWidget*)'
|
51
|
+
|
52
|
+
def initialize(parent = nil, name = nil)
|
53
|
+
super
|
54
|
+
init
|
55
|
+
$windowList = []
|
56
|
+
$windowList.push(self)
|
57
|
+
@log = LogParser.new
|
58
|
+
@nextPurge = Time.now + PurgeInterval
|
59
|
+
|
60
|
+
@currentGDV = GeoDistView.new(@currentMapFrame, currentCountryList)
|
61
|
+
|
62
|
+
@hostListCSLV = CSListView.new(@hostList)
|
63
|
+
# Setup the columns of the "Current Status" list view
|
64
|
+
@hostList.setSorting(2, false)
|
65
|
+
@hostListCSLV.setColumnWidth(0, 300)
|
66
|
+
@hostListCSLV.setColumnWidth(3, 450)
|
67
|
+
@hostListCSLV.setColumnWidth(4, 120)
|
68
|
+
@hostListCSLV.setColumnWidth(5, 200)
|
69
|
+
|
70
|
+
@topPageListCSLV = CSListView.new(topPageList)
|
71
|
+
# Setup the columns of the "Top Pages" list view
|
72
|
+
@topPageList.setSorting(1, false)
|
73
|
+
@topPageListCSLV.setColumnWidth(0, 500)
|
74
|
+
|
75
|
+
@topRefererListCSLV = CSListView.new(topRefererList)
|
76
|
+
# Setup the columns of the "Top Referers" list view
|
77
|
+
@topRefererList.setSorting(1, false)
|
78
|
+
@topRefererListCSLV.setColumnWidth(0, 500)
|
79
|
+
|
80
|
+
@visitorGDV = GeoDistView.new(@visitorMapFrame, @visitorCountryList)
|
81
|
+
@fullHostListCSLV = CSListView.new(@fullHostList)
|
82
|
+
# Setup the columns of the "Visitors" list view
|
83
|
+
@fullHostList.setSorting(2, false)
|
84
|
+
@fullHostListCSLV.setColumnWidth(0, 300)
|
85
|
+
@fullHostListCSLV.setColumnWidth(3, 450)
|
86
|
+
|
87
|
+
@visitorWorldMap = nil
|
88
|
+
|
89
|
+
@pageListCSLV = CSListView.new(@pageList)
|
90
|
+
# Setup the columns of the "Pages" list view
|
91
|
+
@pageList.setSorting(1, false)
|
92
|
+
@pageListCSLV.setColumnWidth(0, 500)
|
93
|
+
|
94
|
+
@refererListCSLV = CSListView.new(@refererList)
|
95
|
+
# Setup the columns on the "Referer" list view
|
96
|
+
@refererList.setSorting(1, false)
|
97
|
+
@refererListCSLV.setColumnWidth(0, 500)
|
98
|
+
|
99
|
+
@statusCodeListCSLV = CSListView.new(@statusCodeList)
|
100
|
+
# Setup the columns of the "Status Code" list view
|
101
|
+
@statusCodeList.setSorting(0, true)
|
102
|
+
|
103
|
+
@hitListCSLV = CSListView.new(@hitList)
|
104
|
+
# Setup the columns of the "Hit" list view
|
105
|
+
@hitList.setSorting(0, false)
|
106
|
+
|
107
|
+
connect(@tabWidget, SIGNAL('currentChanged(QWidget*)'),
|
108
|
+
SLOT('tabChanged(QWidget*)'))
|
109
|
+
connect(@hostList, SIGNAL('clicked(QListViewItem*)'),
|
110
|
+
SLOT('recentHostClicked(QListViewItem*)'))
|
111
|
+
connect(@topPageList, SIGNAL('clicked(QListViewItem*)'),
|
112
|
+
SLOT('topPageClicked(QListViewItem*)'))
|
113
|
+
connect(@topRefererList, SIGNAL('clicked(QListViewItem*)'),
|
114
|
+
SLOT('topRefererClicked(QListViewItem*)'))
|
115
|
+
connect(@fullHostList, SIGNAL('clicked(QListViewItem*)'),
|
116
|
+
SLOT('hostClicked(QListViewItem*)'))
|
117
|
+
connect(@pageList, SIGNAL('clicked(QListViewItem*)'),
|
118
|
+
SLOT('pageClicked(QListViewItem*)'))
|
119
|
+
connect(@refererList, SIGNAL('clicked(QListViewItem*)'),
|
120
|
+
SLOT('refererClicked(QListViewItem*)'))
|
121
|
+
|
122
|
+
connect(@clearVisitorFilterBtn, SIGNAL('clicked()'),
|
123
|
+
SLOT('clearVisitorFilter()'))
|
124
|
+
connect(@clearPageFilterBtn, SIGNAL('clicked()'),
|
125
|
+
SLOT('clearPageFilter()'))
|
126
|
+
connect(@clearRefererFilterBtn, SIGNAL('clicked()'),
|
127
|
+
SLOT('clearRefererFilter()'))
|
128
|
+
connect(@tabWidget, SIGNAL('currentChanged(QWidget*)'),
|
129
|
+
SLOT('updateTab(QWidget*)'))
|
130
|
+
|
131
|
+
@updateTimer = Qt::Timer.new(self)
|
132
|
+
connect(@updateTimer, SIGNAL('timeout()'), SLOT('update()'))
|
133
|
+
@updateTimer.start(500)
|
134
|
+
|
135
|
+
if $globals.getSetting("MainWindowW") > 0 &&
|
136
|
+
$globals.getSetting("MainWindowH") > 0
|
137
|
+
resize($globals.getSetting("MainWindowW"),
|
138
|
+
$globals.getSetting("MainWindowH"))
|
139
|
+
end
|
140
|
+
|
141
|
+
setMonitorWindow($globals.getSetting("MonitorWindow"))
|
142
|
+
@showRegularPages.setOn($globals.getSetting("ShowRegularPages") != 0)
|
143
|
+
@showAuxPages.setOn($globals.getSetting("ShowAuxPages") != 0)
|
144
|
+
@showSurfers.setOn($globals.getSetting("ShowSurfers") != 0)
|
145
|
+
@showRobots.setOn($globals.getSetting("ShowRobots") != 0)
|
146
|
+
|
147
|
+
updateWindow()
|
148
|
+
show
|
149
|
+
end
|
150
|
+
|
151
|
+
def init
|
152
|
+
$hitRecords = []
|
153
|
+
$visitors = {}
|
154
|
+
$pages = {}
|
155
|
+
$referers = {}
|
156
|
+
$statusCodes = {}
|
157
|
+
$recentVisitors = OccurenceCounter.new(50)
|
158
|
+
$recentPages = OccurenceCounter.new(30)
|
159
|
+
$recentReferers = OccurenceCounter.new(15)
|
160
|
+
end
|
161
|
+
|
162
|
+
def close(dummy)
|
163
|
+
@visitorWorldMap.close(true) if @visitorWorldMap
|
164
|
+
@visitorWorldMap = nil
|
165
|
+
|
166
|
+
$resolver.stopAndSave
|
167
|
+
$geoLocator.stopAndSave
|
168
|
+
$traceRouter.stopAndSave
|
169
|
+
|
170
|
+
$windowList.delete(self)
|
171
|
+
$globals.setSetting("MainWindowW", width())
|
172
|
+
$globals.setSetting("MainWindowH", height())
|
173
|
+
#$globals.currentStatusSplitter = @currentStatusSplitter.sizes()
|
174
|
+
$globals.saveSettings()
|
175
|
+
super(true)
|
176
|
+
end
|
177
|
+
|
178
|
+
def tabChanged(dummy)
|
179
|
+
# Some pages generate excessive requests when started with a large
|
180
|
+
# fresh log file. These requests are discarded when the requesting
|
181
|
+
# page is no longer viewed.
|
182
|
+
$resolver.flushQueue
|
183
|
+
$geoLocator.flushQueue
|
184
|
+
$traceRouter.flushQueue
|
185
|
+
end
|
186
|
+
|
187
|
+
def fileExit()
|
188
|
+
close(true)
|
189
|
+
end
|
190
|
+
|
191
|
+
def setMonitorWindow(duration)
|
192
|
+
actionList = { 1 => @mw_1, 2 => @mw_2, 5 => @mw_5, 10 => @mw_10,
|
193
|
+
20 => @mw_20, 30 => @mw_30, 60 => @mw_60, 120 => @mw_120 }
|
194
|
+
actionList.each_value { |a| a.setOn(false) }
|
195
|
+
actionList[duration].setOn(true)
|
196
|
+
$globals.setSetting("MonitorWindow", duration)
|
197
|
+
end
|
198
|
+
|
199
|
+
def newLog()
|
200
|
+
settings = GSServerLogSettings.new
|
201
|
+
if ServerLogSettings.new(settings, self).exec() == Qt::Dialog.Accepted
|
202
|
+
$globals.addServerLogFile(settings)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def openLog()
|
207
|
+
$windowList.each { |w| w.close unless w == self }
|
208
|
+
init
|
209
|
+
$globals.currentLogFile = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
def mw_1()
|
213
|
+
setMonitorWindow(1)
|
214
|
+
end
|
215
|
+
|
216
|
+
def mw_2()
|
217
|
+
setMonitorWindow(2)
|
218
|
+
end
|
219
|
+
|
220
|
+
def mw_5()
|
221
|
+
setMonitorWindow(5)
|
222
|
+
end
|
223
|
+
|
224
|
+
def mw_10()
|
225
|
+
setMonitorWindow(10)
|
226
|
+
end
|
227
|
+
|
228
|
+
def mw_20()
|
229
|
+
setMonitorWindow(20)
|
230
|
+
end
|
231
|
+
|
232
|
+
def mw_30()
|
233
|
+
setMonitorWindow(30)
|
234
|
+
end
|
235
|
+
|
236
|
+
def mw_60()
|
237
|
+
setMonitorWindow(60)
|
238
|
+
end
|
239
|
+
|
240
|
+
def mw_120()
|
241
|
+
setMonitorWindow(120)
|
242
|
+
end
|
243
|
+
|
244
|
+
def showRegularPages()
|
245
|
+
$globals.setSetting("ShowRegularPages", @showRegularPages.isOn() ? 1 : 0)
|
246
|
+
end
|
247
|
+
|
248
|
+
def showAuxPages()
|
249
|
+
$globals.setSetting("ShowAuxPages", @showAuxPages.isOn() ? 1 : 0)
|
250
|
+
end
|
251
|
+
|
252
|
+
def showSurfers()
|
253
|
+
$globals.setSetting("ShowSurfers", @showSurfers.isOn() ? 1 : 0)
|
254
|
+
end
|
255
|
+
|
256
|
+
def showRobots()
|
257
|
+
$globals.setSetting("ShowRobots", @showRobots.isOn() ? 1 : 0)
|
258
|
+
end
|
259
|
+
|
260
|
+
def configureClickSpotter()
|
261
|
+
Configuration.new(self).exec
|
262
|
+
end
|
263
|
+
|
264
|
+
def about()
|
265
|
+
dlg = About.new
|
266
|
+
dlg.aboutVersion.text = dlg.aboutVersion.text.gsub(/X\_X\_X/, AppConfig.version)
|
267
|
+
dlg.aboutVersion.text = dlg.aboutVersion.text.gsub(/APP\_NAME/, AppConfig.name)
|
268
|
+
dlg.caption = dlg.caption.sub(/APP\_NAME/, AppConfig.name)
|
269
|
+
dlg.exec
|
270
|
+
end
|
271
|
+
|
272
|
+
def update()
|
273
|
+
# Avoid recursive calls in case this function takes a little
|
274
|
+
# longer
|
275
|
+
@updateTimer.stop()
|
276
|
+
|
277
|
+
# Delete outdated records
|
278
|
+
purgeRecords
|
279
|
+
|
280
|
+
# Before we process the log file we need to make sure that we have any log
|
281
|
+
# file definitions. If not, we open the appropriate configuration dialog.
|
282
|
+
newLog if $globals.serverLogFiles.empty?
|
283
|
+
|
284
|
+
until $globals.logFile
|
285
|
+
dlg = LogFileSelector.new(self)
|
286
|
+
if dlg.exec == Qt::Dialog.Rejected
|
287
|
+
close(true)
|
288
|
+
return
|
289
|
+
end
|
290
|
+
$globals.currentLogFile = dlg.selectedLogFile
|
291
|
+
end
|
292
|
+
@log.setFileName($globals.logFile)
|
293
|
+
setCaption("#{$globals.currentLogFile.name} - #{AppConfig.name} " +
|
294
|
+
"#{AppConfig.version}")
|
295
|
+
|
296
|
+
processLog
|
297
|
+
updateWindows
|
298
|
+
updateStatusBar
|
299
|
+
@updateTimer.start(5000)
|
300
|
+
end
|
301
|
+
|
302
|
+
def processLog
|
303
|
+
deadline = Time.now - $globals.getSetting('LogFileDataTimeout') * 60 * 60
|
304
|
+
|
305
|
+
begin
|
306
|
+
while (rec = @log.getNextEntry(deadline)) != nil
|
307
|
+
next if rec['url'] == nil
|
308
|
+
|
309
|
+
# Store hit record
|
310
|
+
hr = HitRecord.new(rec)
|
311
|
+
$hitRecords.push(hr)
|
312
|
+
|
313
|
+
# Create or update status code record
|
314
|
+
code = rec['code']
|
315
|
+
if $statusCodes[code] == nil
|
316
|
+
sc = StatusCode.new(code)
|
317
|
+
$statusCodes[code] = sc
|
318
|
+
else
|
319
|
+
sc = $statusCodes[code]
|
320
|
+
end
|
321
|
+
sc.addHit(hr)
|
322
|
+
|
323
|
+
# Create or update page record
|
324
|
+
url = rec['url']
|
325
|
+
if $pages[url] == nil
|
326
|
+
p = PageRecord.new(url, rec['isPage'])
|
327
|
+
$pages[url] = p
|
328
|
+
else
|
329
|
+
p = $pages[url]
|
330
|
+
end
|
331
|
+
p.addHit(hr)
|
332
|
+
$recentPages.push(p)
|
333
|
+
|
334
|
+
# Create or update referer record
|
335
|
+
referer = rec['referer']
|
336
|
+
if referer
|
337
|
+
if $referers[referer] == nil
|
338
|
+
r = RefererRecord.new(referer)
|
339
|
+
$referers[referer] = r
|
340
|
+
else
|
341
|
+
r = $referers[referer]
|
342
|
+
end
|
343
|
+
r.addHit(hr)
|
344
|
+
if r.external?
|
345
|
+
$recentReferers.push(r)
|
346
|
+
else
|
347
|
+
# For internal pages we track the number of referrals to
|
348
|
+
# other internal pages
|
349
|
+
if $pages.has_key?(referer)
|
350
|
+
$pages[referer].addReferral(rec['url'])
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# Create or update visitor record
|
356
|
+
key = rec['ip'] + "|" + rec['browser']
|
357
|
+
if $visitors[key] == nil
|
358
|
+
v = VisitorRecord.new(rec['ip'], rec['browser'])
|
359
|
+
$visitors[key] = v
|
360
|
+
else
|
361
|
+
v = $visitors[key]
|
362
|
+
end
|
363
|
+
v.addHit(hr)
|
364
|
+
v.robot = $globals.isRobot(rec['ip'], rec['browser'], rec['url'])
|
365
|
+
$recentVisitors.push(v)
|
366
|
+
|
367
|
+
end
|
368
|
+
rescue SystemCallError
|
369
|
+
Qt::MessageBox.warning(self, "Opening log file failed!", "#{$!}",
|
370
|
+
Qt::MessageBox.Ok, 0)
|
371
|
+
openLog
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def purgeRecords
|
376
|
+
return unless Time.now > @nextPurge
|
377
|
+
|
378
|
+
deadline = Time.now - $globals.getSetting("LogFileDataTimeout") * 60 * 60
|
379
|
+
$visitors.each do |id, v|
|
380
|
+
if v.purge(deadline)
|
381
|
+
$recentVisitors.delete(v)
|
382
|
+
$visitors.delete(id)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
$referers.each do |url, r|
|
387
|
+
if r.purge(deadline)
|
388
|
+
$recentReferers.delete(r)
|
389
|
+
$referers.delete(url)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
deletedPages = {}
|
394
|
+
$pages.each do |url, p|
|
395
|
+
deletedPages[url] = p if p.purge(deadline)
|
396
|
+
end
|
397
|
+
$pages.each do |url, p|
|
398
|
+
p.purgeNextPages(deletedPages.values)
|
399
|
+
end
|
400
|
+
deletedPages.each_key do |u|
|
401
|
+
$recentPages.delete($pages.delete(u))
|
402
|
+
end
|
403
|
+
|
404
|
+
$statusCodes.each do |c, h|
|
405
|
+
$statusCodes.delete(c) if h.purge(deadline)
|
406
|
+
end
|
407
|
+
|
408
|
+
$hitRecords.delete_if { |h| h.timeStamp < deadline }
|
409
|
+
|
410
|
+
@nextPurge = Time.now + PurgeInterval
|
411
|
+
end
|
412
|
+
|
413
|
+
def updateWindows
|
414
|
+
$windowList.each do |win|
|
415
|
+
win.updateWindow
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def updateTab(w)
|
420
|
+
updateWindow
|
421
|
+
end
|
422
|
+
|
423
|
+
def updateWindow
|
424
|
+
funcs = [ method(:updateRecent), method(:updateVisitors),
|
425
|
+
method(:updatePages), method(:updateReferers),
|
426
|
+
method(:updateCodes) ]
|
427
|
+
funcs[@tabWidget.currentPageIndex()].call()
|
428
|
+
|
429
|
+
@visitorWorldMap.update($visitors) if @visitorWorldMap
|
430
|
+
end
|
431
|
+
|
432
|
+
def updateRecent
|
433
|
+
|
434
|
+
@hostListCSLV.startUpdate
|
435
|
+
deadline = Time.now().localtime() -
|
436
|
+
$globals.getSetting("MonitorWindow") * 60
|
437
|
+
showSurfers = $globals.getSetting("ShowSurfers") != 0
|
438
|
+
showRobots = $globals.getSetting("ShowRobots") != 0
|
439
|
+
visitors = {}
|
440
|
+
$recentVisitors.each do |v, count|
|
441
|
+
if v == nil ||
|
442
|
+
(v.robot != nil && !showRobots) ||
|
443
|
+
(v.robot == nil && !showSurfers) ||
|
444
|
+
v.lastHit.timeStamp < deadline
|
445
|
+
next
|
446
|
+
end
|
447
|
+
|
448
|
+
visitors[v.ip] = v
|
449
|
+
|
450
|
+
if (!@currentGDV.selectedItems.empty? &&
|
451
|
+
@currentGDV.selectedItems[v.country] == nil)
|
452
|
+
next
|
453
|
+
end
|
454
|
+
@hostListCSLV.insertItem(v, v.host(), v.hitCount(), v.pageCount(),
|
455
|
+
v.lastPage ? v.lastPage.url() : '', v.ip(),
|
456
|
+
v.browser())
|
457
|
+
end
|
458
|
+
@hostListCSLV.finishUpdate
|
459
|
+
|
460
|
+
@currentGDV.update(visitors)
|
461
|
+
@topPageListCSLV.startUpdate
|
462
|
+
showRegularPages = $globals.getSetting("ShowRegularPages") != 0
|
463
|
+
showAuxPages = $globals.getSetting("ShowAuxPages") != 0
|
464
|
+
$recentPages.each do |p, count|
|
465
|
+
if p == nil || p.lastHit == nil || p.lastHit.timeStamp < deadline ||
|
466
|
+
(p.isPage && !showRegularPages) ||
|
467
|
+
(!p.isPage && !showAuxPages)
|
468
|
+
next
|
469
|
+
end
|
470
|
+
@topPageListCSLV.insertItem(p, p.url, count, p.lastHit.bytes * count)
|
471
|
+
end
|
472
|
+
@topPageListCSLV.finishUpdate
|
473
|
+
|
474
|
+
@topRefererListCSLV.startUpdate
|
475
|
+
$recentReferers.each do |r, count|
|
476
|
+
next if r == nil || r.lastHit == nil || r.lastHit.timeStamp < deadline
|
477
|
+
@topRefererListCSLV.insertItem(r, r.url, count, r.lastHit.bytes * count)
|
478
|
+
end
|
479
|
+
@topRefererListCSLV.finishUpdate
|
480
|
+
end
|
481
|
+
|
482
|
+
def updateVisitors
|
483
|
+
@visitorGDV.update($visitors)
|
484
|
+
@fullHostListCSLV.startUpdate
|
485
|
+
showSurfers = $globals.getSetting("ShowSurfers") != 0
|
486
|
+
showRobots = $globals.getSetting("ShowRobots") != 0
|
487
|
+
$visitors.each_value do |visitor|
|
488
|
+
if (!@visitorFilter.text().empty? &&
|
489
|
+
!(Regexp.new(@visitorFilter.text()) =~ visitor.host)) ||
|
490
|
+
(visitor.robot != nil && !showRobots) ||
|
491
|
+
(visitor.robot == nil && !showSurfers) ||
|
492
|
+
(!@visitorGDV.selectedItems.empty? &&
|
493
|
+
@visitorGDV.selectedItems[visitor.country] == nil)
|
494
|
+
next
|
495
|
+
end
|
496
|
+
@fullHostListCSLV.insertItem(visitor, visitor.host, visitor.hitCount,
|
497
|
+
visitor.pageCount, visitor.lastPage ? visitor.lastPage.page.url : '',
|
498
|
+
visitor.lastHit.timeStamp, visitor.ip, visitor.browser)
|
499
|
+
end
|
500
|
+
@fullHostListCSLV.finishUpdate
|
501
|
+
end
|
502
|
+
|
503
|
+
def updatePages
|
504
|
+
@pageListCSLV.startUpdate
|
505
|
+
showRegularPages = $globals.getSetting("ShowRegularPages") != 0
|
506
|
+
showAuxPages = $globals.getSetting("ShowAuxPages") != 0
|
507
|
+
$pages.each do |url, page|
|
508
|
+
if !@pageFilter.text().empty? &&
|
509
|
+
!(Regexp.new(@pageFilter.text()) =~ url) ||
|
510
|
+
(page.isPage && !showRegularPages) ||
|
511
|
+
(!page.isPage && !showAuxPages)
|
512
|
+
next
|
513
|
+
end
|
514
|
+
@pageListCSLV.insertItem(page, url, page.hitCount, page.entryCount,
|
515
|
+
page.exitCount);
|
516
|
+
end
|
517
|
+
@pageListCSLV.finishUpdate
|
518
|
+
end
|
519
|
+
|
520
|
+
def updateReferers
|
521
|
+
@refererListCSLV.startUpdate
|
522
|
+
$referers.each do |url, referer|
|
523
|
+
if !referer.external? ||
|
524
|
+
(!@refererFilter.text().empty? &&
|
525
|
+
!(Regexp.new(@refererFilter.text()) =~ url))
|
526
|
+
next
|
527
|
+
end
|
528
|
+
@refererListCSLV.insertItem(referer, url, referer.hitCount)
|
529
|
+
end
|
530
|
+
@refererListCSLV.finishUpdate
|
531
|
+
end
|
532
|
+
|
533
|
+
def updateCodes
|
534
|
+
@statusCodeListCSLV.startUpdate
|
535
|
+
$statusCodes.each do |code, rec|
|
536
|
+
@statusCodeListCSLV.insertItem(code, code, rec.description,
|
537
|
+
rec.hitCount, 0)
|
538
|
+
end
|
539
|
+
@statusCodeListCSLV.finishUpdate
|
540
|
+
|
541
|
+
@hitListCSLV.startUpdate
|
542
|
+
$hitRecords.each do |hit|
|
543
|
+
next if @statusCodeListCSLV.selectedItems[hit.code] == nil
|
544
|
+
@hitListCSLV.insertItem(hit, hit.timeStamp, hit.code,
|
545
|
+
hit.visitor.host, hit.request,
|
546
|
+
hit.referer ? hit.referer.url : '')
|
547
|
+
end
|
548
|
+
@hitListCSLV.finishUpdate
|
549
|
+
end
|
550
|
+
|
551
|
+
def updateStatusBar
|
552
|
+
now = Time.now()
|
553
|
+
i = $hitRecords.length() - 1
|
554
|
+
pages = 0
|
555
|
+
bytes = 0
|
556
|
+
while (i > 0) and ($hitRecords[i].timeStamp > Time.now().localtime() - 60)
|
557
|
+
i -= 1
|
558
|
+
bytes += $hitRecords[i].bytes
|
559
|
+
pages += 1 if $hitRecords[i].page.isPage
|
560
|
+
end
|
561
|
+
statusBar().message("Hits: #{$hitRecords.length() - 1 - i}/min " +
|
562
|
+
"Pages: #{pages}/min " +
|
563
|
+
"Traffic: #{bytes / 60} kBytes/s " +
|
564
|
+
"Queues (D/G/T): #{$resolver.queueSize}/" +
|
565
|
+
"#{$geoLocator.queueSize}/" +
|
566
|
+
"#{$traceRouter.queueSize}")
|
567
|
+
end
|
568
|
+
|
569
|
+
def clearVisitorFilter()
|
570
|
+
@visitorFilter.setText("")
|
571
|
+
updateVisitors
|
572
|
+
end
|
573
|
+
|
574
|
+
def clearPageFilter()
|
575
|
+
@pageFilter.setText("")
|
576
|
+
updatePages
|
577
|
+
end
|
578
|
+
|
579
|
+
def clearRefererFilter()
|
580
|
+
@refererFilter.setText("")
|
581
|
+
updateReferers
|
582
|
+
end
|
583
|
+
|
584
|
+
def recentHostClicked(item)
|
585
|
+
return if item == nil
|
586
|
+
visitor = $visitors[item.text(4) + "|" + item.text(5)]
|
587
|
+
VisitorInformation.new(visitor)
|
588
|
+
end
|
589
|
+
|
590
|
+
def topPageClicked(item)
|
591
|
+
return if item == nil
|
592
|
+
PageInformation.new($pages[item.text(0)])
|
593
|
+
end
|
594
|
+
|
595
|
+
def topRefererClicked(item)
|
596
|
+
return if item == nil
|
597
|
+
RefererInformation.new($referers[item.text(0)])
|
598
|
+
end
|
599
|
+
|
600
|
+
def hostClicked(item)
|
601
|
+
return if item == nil
|
602
|
+
visitor = $visitors[item.text(5) + "|" + item.text(6)]
|
603
|
+
VisitorInformation.new(visitor)
|
604
|
+
end
|
605
|
+
|
606
|
+
def largeVisitorMapBtnClicked
|
607
|
+
@visitorWorldMap.close(true) if @visitorWorldMap
|
608
|
+
|
609
|
+
@visitorWorldMap = WorldMap.new(self)
|
610
|
+
end
|
611
|
+
|
612
|
+
def worldMapClosed
|
613
|
+
@visitorWorldMap = nil
|
614
|
+
end
|
615
|
+
|
616
|
+
def pageClicked(item)
|
617
|
+
return if item == nil
|
618
|
+
PageInformation.new($pages[item.text(0)])
|
619
|
+
end
|
620
|
+
|
621
|
+
def refererClicked(item)
|
622
|
+
return if item == nil
|
623
|
+
RefererInformation.new($referers[item.text(0)])
|
624
|
+
end
|
625
|
+
|
626
|
+
end
|
627
|
+
|