logviewer: avoid memory-mapped files, make it work when opened a second time

This commit is contained in:
sqlcoder 2009-12-09 21:58:56 +00:00
parent bbaecc1697
commit fc95de82f4
2 changed files with 55 additions and 37 deletions

View File

@ -17,8 +17,8 @@
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
import mmap import os
import threading import Queue
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
@ -30,13 +30,16 @@ import Configuration
log = Configuration.get_logger("logging.conf", "logview") log = Configuration.get_logger("logging.conf", "logview")
MAX_LINES = 100000 MAX_LINES = 100000 # max lines to display in window
EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
logfile = 'logging.out' # name of logfile
class GuiLogView: class GuiLogView:
def __init__(self, config, mainwin): def __init__(self, config, mainwin, closeq):
self.config = config self.config = config
self.main_window = mainwin self.main_window = mainwin
self.closeq = closeq
self.dia = gtk.Dialog(title="Log Messages" self.dia = gtk.Dialog(title="Log Messages"
,parent=None ,parent=None
@ -88,6 +91,7 @@ class GuiLogView:
def dialog_response_cb(self, dialog, response_id): def dialog_response_cb(self, dialog, response_id):
# this is called whether close button is pressed or window is closed # this is called whether close button is pressed or window is closed
self.closeq.put(self.__class__)
dialog.destroy() dialog.destroy()
def get_dialog(self): def get_dialog(self):
@ -109,39 +113,28 @@ class GuiLogView:
def loadLog(self): def loadLog(self):
#self.configStore = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING)
#self.configView = gtk.TreeView(self.configStore)
#self.configView.set_enable_tree_lines(True)
self.liststore.clear() self.liststore.clear()
self.listcols = [] self.listcols = []
# count number of lines in file # guesstimate number of lines in file
f = open('logging.out', "r+") if os.path.exists(logfile):
buf = mmap.mmap(f.fileno(), 0) stat_info = os.stat(logfile)
readline = buf.readline lines = stat_info.st_size / EST_CHARS_PER_LINE
lines = 0 print "logview: size =", stat_info.st_size, "lines =", lines
while readline():
lines += 1
f.close()
# set startline to line number to start display from
startline = 0 startline = 0
if lines > MAX_LINES: if lines > MAX_LINES:
# only display from startline if log file is large # only display from startline if log file is large
startline = lines - MAX_LINES startline = lines - MAX_LINES
f = open('logging.out', "r+")
buf = mmap.mmap(f.fileno(), 0)
readline = buf.readline
l = 0 l = 0
line = readline() for line in open(logfile):
while line:
# eg line: # eg line:
# 2009-12-02 15:23:21,716 - config DEBUG config logger initialised # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
l = l + 1 l = l + 1
if l > startline and len(line) > 49: if l > startline and len(line) > 49:
iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) )
line = readline()
f.close()
def sortCols(self, col, n): def sortCols(self, col, n):
try: try:

View File

@ -18,6 +18,7 @@
import os import os
import sys import sys
import re import re
import Queue
# if path is set to use an old version of python look for a new one: # if path is set to use an old version of python look for a new one:
# (does this work in linux?) # (does this work in linux?)
@ -485,14 +486,22 @@ class fpdb:
#if self.obtain_global_lock(): #if self.obtain_global_lock():
# lock_set = True # lock_set = True
# remove members from self.threads if close messages received
self.process_close_messages()
viewer = None
for i, t in enumerate(self.threads): for i, t in enumerate(self.threads):
if str(t.__class__) == 'GuiLogView.GuiLogView': if str(t.__class__) == 'GuiLogView.GuiLogView':
# show existing log window viewer = t
t.get_dialog().present() break
return
new_thread = GuiLogView.GuiLogView(self.config, self.window) if viewer is None:
#print "creating new log viewer"
new_thread = GuiLogView.GuiLogView(self.config, self.window, self.closeq)
self.threads.append(new_thread) self.threads.append(new_thread)
else:
#print "showing existing log viewer"
viewer.get_dialog().present()
#if lock_set: #if lock_set:
# self.release_global_lock() # self.release_global_lock()
@ -502,6 +511,21 @@ class fpdb:
self.logbuffer.insert(end_iter, text) self.logbuffer.insert(end_iter, text)
self.logview.scroll_to_mark(self.logbuffer.get_insert(), 0) self.logview.scroll_to_mark(self.logbuffer.get_insert(), 0)
def process_close_messages(self):
# check for close messages
try:
while True:
name = self.closeq.get(False)
for i, t in enumerate(self.threads):
if str(t.__class__) == str(name):
# thread has ended so remove from list:
del self.threads[i]
break
except Queue.Empty:
# no close messages on queue, do nothing
pass
def __calendar_dialog(self, widget, entry): def __calendar_dialog(self, widget, entry):
self.dia_confirm.set_modal(False) self.dia_confirm.set_modal(False)
d = gtk.Window(gtk.WINDOW_TOPLEVEL) d = gtk.Window(gtk.WINDOW_TOPLEVEL)
@ -846,6 +870,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
#done menubar #done menubar
self.threads = [] # objects used by tabs - no need for threads, gtk handles it self.threads = [] # objects used by tabs - no need for threads, gtk handles it
self.closeq = Queue.Queue(20) # used to signal ending of a thread (only logviewer for now)
self.nb = gtk.Notebook() self.nb = gtk.Notebook()
self.nb.set_show_tabs(True) self.nb.set_show_tabs(True)