From fc95de82f475ae1fb8818107d94d92bf064cdfe0 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Wed, 9 Dec 2009 21:58:56 +0000 Subject: [PATCH] logviewer: avoid memory-mapped files, make it work when opened a second time --- pyfpdb/GuiLogView.py | 57 +++++++++++++++++++------------------------- pyfpdb/fpdb.py | 35 +++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/pyfpdb/GuiLogView.py b/pyfpdb/GuiLogView.py index 8f4cd834..29385fa0 100755 --- a/pyfpdb/GuiLogView.py +++ b/pyfpdb/GuiLogView.py @@ -17,8 +17,8 @@ #agpl-3.0.txt in the docs folder of the package. -import mmap -import threading +import os +import Queue import pygtk pygtk.require('2.0') @@ -30,13 +30,16 @@ import Configuration 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: - def __init__(self, config, mainwin): + def __init__(self, config, mainwin, closeq): self.config = config self.main_window = mainwin + self.closeq = closeq self.dia = gtk.Dialog(title="Log Messages" ,parent=None @@ -88,6 +91,7 @@ class GuiLogView: def dialog_response_cb(self, dialog, response_id): # this is called whether close button is pressed or window is closed + self.closeq.put(self.__class__) dialog.destroy() def get_dialog(self): @@ -109,39 +113,28 @@ class GuiLogView: 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.listcols = [] - # count number of lines in file - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - lines = 0 - while readline(): - lines += 1 - f.close() + # guesstimate number of lines in file + if os.path.exists(logfile): + stat_info = os.stat(logfile) + lines = stat_info.st_size / EST_CHARS_PER_LINE + print "logview: size =", stat_info.st_size, "lines =", lines - startline = 0 - if lines > MAX_LINES: - # only display from startline if log file is large - startline = lines - MAX_LINES + # set startline to line number to start display from + startline = 0 + if lines > MAX_LINES: + # only display from startline if log file is large + startline = lines - MAX_LINES - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - l = 0 - line = readline() - while line: - # eg line: - # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised - l = l + 1 - if l > startline and len(line) > 49: - iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) - line = readline() - f.close() + l = 0 + for line in open(logfile): + # eg line: + # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised + l = l + 1 + if l > startline and len(line) > 49: + iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) def sortCols(self, col, n): try: diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 8b21a8c7..b7013a92 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -18,6 +18,7 @@ import os import sys import re +import Queue # if path is set to use an old version of python look for a new one: # (does this work in linux?) @@ -485,14 +486,22 @@ class fpdb: #if self.obtain_global_lock(): # 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): if str(t.__class__) == 'GuiLogView.GuiLogView': - # show existing log window - t.get_dialog().present() - return + viewer = t + break - new_thread = GuiLogView.GuiLogView(self.config, self.window) - self.threads.append(new_thread) + if viewer is None: + #print "creating new log viewer" + new_thread = GuiLogView.GuiLogView(self.config, self.window, self.closeq) + self.threads.append(new_thread) + else: + #print "showing existing log viewer" + viewer.get_dialog().present() #if lock_set: # self.release_global_lock() @@ -502,6 +511,21 @@ class fpdb: self.logbuffer.insert(end_iter, text) 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): self.dia_confirm.set_modal(False) 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 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.set_show_tabs(True)