2 # -*- coding: utf-8 -*-
4 #Copyright 2008-2011 Carl Gherardi
5 #This program is free software: you can redistribute it and/or modify
6 #it under the terms of the GNU Affero General Public License as published by
7 #the Free Software Foundation, version 3 of the License.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU Affero General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #In the "official" distribution you can find the license in agpl-3.0.txt.
19 _
= L10n
.get_translation()
33 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
34 log
= logging
.getLogger("logview")
36 MAX_LINES
= 100000 # max lines to display in window
37 EST_CHARS_PER_LINE
= 150 # used to guesstimate number of lines in log file
38 LOGFILES
= [ [ _('Fpdb Errors'), 'fpdb-errors.txt', False ] # label, filename, start value
39 , [ _('Fpdb Log'), 'fpdb-log.txt', True ]
40 , [ _('HUD Errors'), 'HUD-errors.txt', False ]
41 , [ _('HUD Log'), 'HUD-log.txt', False ]
46 def __init__(self
, config
, mainwin
, closeq
):
48 self
.main_window
= mainwin
51 self
.logfile
= os
.path
.join(self
.config
.dir_log
, LOGFILES
[1][1])
52 self
.dia
= gtk
.Dialog(title
=_("Log Messages")
54 ,flags
=gtk
.DIALOG_DESTROY_WITH_PARENT
55 ,buttons
=(gtk
.STOCK_CLOSE
,gtk
.RESPONSE_OK
))
56 self
.dia
.set_modal(False)
58 self
.vbox
= self
.dia
.vbox
59 gtk
.Widget
.set_size_request(self
.vbox
, 700, 400);
61 self
.liststore
= gtk
.ListStore(str, str, str, str, gobject
.TYPE_BOOLEAN
) # date, module, level, text
62 # this is how to add a filter:
64 # # Creation of the filter, from the model
65 # filter = self.liststore.filter_new()
66 # filter.set_visible_column(1)
68 # # The TreeView gets the filter as model
69 # self.listview = gtk.TreeView(filter)
70 self
.listview
= gtk
.TreeView(model
=self
.liststore
)
71 self
.listview
.set_grid_lines(gtk
.TREE_VIEW_GRID_LINES_NONE
)
74 scrolledwindow
= gtk
.ScrolledWindow()
75 scrolledwindow
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
76 scrolledwindow
.add(self
.listview
)
77 self
.vbox
.pack_start(scrolledwindow
, expand
=True, fill
=True, padding
=0)
79 hb
= gtk
.HBox(False, 0)
82 rb
= gtk
.RadioButton(group
=grp
, label
=logf
[0], use_underline
=True)
83 if grp
is None: grp
= rb
84 rb
.set_active(logf
[2])
85 rb
.connect('clicked', self
.__set
_logfile
, logf
[0])
86 hb
.pack_start(rb
, False, False, 3)
87 refreshbutton
= gtk
.Button(_("Refresh"))
88 refreshbutton
.connect("clicked", self
.refresh
, None)
89 hb
.pack_start(refreshbutton
, False, False, 3)
91 self
.vbox
.pack_start(hb
, False, False, 0)
96 self
.dia
.set_focus(self
.listview
)
98 col
= self
.addColumn(_("Date/Time"), 0)
99 col
= self
.addColumn(_("Module"), 1)
100 col
= self
.addColumn(_("Level"), 2)
101 col
= self
.addColumn(_("Text"), 3)
107 self
.dia
.connect('response', self
.dialog_response_cb
)
109 def __set_logfile(self
, w
, file):
110 #print "w is", w, "file is", file, "active is", w.get_active()
112 for logf
in LOGFILES
:
114 self
.logfile
= os
.path
.join(self
.config
.dir_log
, logf
[1])
115 self
.refresh(w
, file) # params are not used
117 def dialog_response_cb(self
, dialog
, response_id
):
118 # this is called whether close button is pressed or window is closed
119 self
.closeq
.put(self
.__class
__)
122 def get_dialog(self
):
125 def addColumn(self
, title
, n
):
126 col
= gtk
.TreeViewColumn(title
)
127 self
.listview
.append_column(col
)
128 cRender
= gtk
.CellRendererText()
129 cRender
.set_property("wrap-mode", pango
.WRAP_WORD_CHAR
)
130 col
.pack_start(cRender
, True)
131 col
.add_attribute(cRender
, 'text', n
)
132 col
.set_max_width(1000)
133 col
.set_spacing(0) # no effect
134 self
.listcols
.append(col
)
135 col
.set_clickable(True)
136 col
.connect("clicked", self
.sortCols
, n
)
141 self
.liststore
.clear()
142 # self.listcols = [] blanking listcols causes sortcols() to fail with index out of range
144 # guesstimate number of lines in file
145 if os
.path
.exists(self
.logfile
):
146 stat_info
= os
.stat(self
.logfile
)
147 lines
= stat_info
.st_size
/ EST_CHARS_PER_LINE
148 #print "logview: size =", stat_info.st_size, "lines =", lines
150 # set startline to line number to start display from
152 if lines
> MAX_LINES
:
153 # only display from startline if log file is large
154 startline
= lines
- MAX_LINES
157 for line
in open(self
.logfile
):
158 # example line in logfile format:
159 # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
162 # NOTE selecting a sort column and then switching to a log file
163 # with several thousand rows will send cpu 100% for a prolonged period.
164 # reason is that the append() method seems to sort every record as it goes, rather than
165 # pulling in the whole file and sorting at the end.
166 # one fix is to check if a column sort has been selected, reset to date/time asc
167 # append all the rows and then reselect the required sort order.
168 # Note: there is no easy method available to revert the list to an "unsorted" state.
169 # always defaulting to date/time asc doesn't work, because some rows do not have date/time info
170 # and would end up sorted out of context.
171 if len(line
) > 49 and line
[23:26] == ' - ' and line
[34:39] == ' ':
172 iter = self
.liststore
.append( (line
[0:23], line
[26:32], line
[39:46], line
[48:].strip(), True) )
174 iter = self
.liststore
.append( ('', '', '', line
.strip(), True) )
176 def sortCols(self
, col
, n
):
178 if not col
.get_sort_indicator() or col
.get_sort_order() == gtk
.SORT_ASCENDING
:
179 col
.set_sort_order(gtk
.SORT_DESCENDING
)
181 col
.set_sort_order(gtk
.SORT_ASCENDING
)
182 self
.liststore
.set_sort_column_id(n
, col
.get_sort_order())
183 #self.liststore.set_sort_func(n, self.sortnums, (n,grid))
184 for i
in xrange(len(self
.listcols
)):
185 self
.listcols
[i
].set_sort_indicator(False)
186 self
.listcols
[n
].set_sort_indicator(True)
187 # use this listcols[col].set_sort_indicator(True)
188 # to turn indicator off for other cols
190 err
= traceback
.extract_tb(sys
.exc_info()[2])
191 print _("***sortCols error: ") + str(sys
.exc_info()[1])
192 print "\n".join( [e
[0]+':'+str(e
[1])+" "+e
[2] for e
in err
] )
194 def refresh(self
, widget
, data
):
199 if __name__
=="__main__":
201 config
= Configuration
.Config()
203 win
= gtk
.Window(gtk
.WINDOW_TOPLEVEL
)
204 win
.set_title(_("Log Viewer"))
205 win
.set_border_width(1)
206 win
.set_default_size(600, 500)
207 win
.set_resizable(True)
209 dia
= gtk
.Dialog(_("Log Viewer"),
211 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
212 (gtk
.STOCK_CLOSE
, gtk
.RESPONSE_OK
))
213 dia
.set_default_size(500, 500)
214 log
= GuiLogView(config
, win
, dia
.vbox
)
216 if response
== gtk
.RESPONSE_ACCEPT
: