Refactoring.
[laditools.git] / ladi-system-log
blobcb45e4de020fcd1b1a8016225258dda8e5f577e6
1 #!/usr/bin/python
3 # LADITools - Linux Audio Desktop Integration Tools
4 # ladi-system-log - A log viewer for your Linux Audio Desktop
5 # Copyright (C) 2011-2012 Alessio Treglia <quadrispro@ubuntu.com>
6 # Copyright (C) 2007-2010, Marc-Olivier Barre <marco@marcochapeau.org>
7 # Copyright (C) 2007-2009, Nedko Arnaudov <nedko@arnaudov.name>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import os
23 import sys
24 import signal
25 from subprocess import Popen, PIPE
26 import pty
27 from signal import SIGTERM
28 import termios
29 import tty
30 import gettext
31 import argparse
33 from laditools import _gettext_domain
34 gettext.install(_gettext_domain)
36 from laditools import get_version_string
37 from laditools import LadiConfiguration
38 from laditools import LadiApp
40 from gi.repository import Gtk
41 from gi.repository import GObject
42 from gi.repository import Vte
44 from laditools.gtk import find_data_file
46 timeout_add = GObject.timeout_add
48 # Default configuration
49 max_lines_default = 100
51 # Output the last <lines> lines
52 def read_last(lfile, lines):
53 chunk_size = lines * 60
54 lfile.seek(0, 2)
55 endpos = lfile.tell()
56 pos = endpos - chunk_size
57 if pos < 0:
58 pos = 0
59 backlog = ''
60 backlog_size = 0
61 lines += 1
62 while pos >= 0 and backlog_size <= lines:
63 lfile.seek(pos, 0)
64 s = lfile.read(chunk_size)
65 pos = pos - chunk_size
66 backlog_size += s.count("\n")
67 backlog = s + backlog
68 backlog = backlog.strip().split("\n")
69 if len(backlog) > lines:
70 backlog = backlog[-lines:]
71 lfile.seek(endpos, 0)
72 return backlog
74 class LadiSystemLog(LadiApp):
76 _appname = 'ladi-system-log'
77 _appname_long = _("LADI Log Viewer")
78 _appid = 'org.linuxaudio.ladi.logviewer'
80 _default_config = { 'max_lines' : max_lines_default }
82 def __init__ (self):
83 LadiApp.__init__(self)
85 self.log_files = [
87 'name': 'JACK',
88 'config_name': 'jackdbus_log',
89 'config_default': os.sep.join([os.environ['HOME'], ".log", "jack", "jackdbus.log"])
92 'name': 'LADISH',
93 'config_name': 'ladish_log',
94 'config_default': os.sep.join([os.environ['HOME'], ".log", "ladish", "ladish.log"])
97 'name': 'A2J',
98 'config_name': 'a2j_log',
99 'config_default': os.sep.join([os.environ['HOME'], ".log", "a2j", "a2j.log"])
103 # Handle the configuration
104 for log in self.log_files:
105 self._default_config[log['config_name']] = log['config_default']
106 self.global_config = LadiConfiguration (self.appname, self._default_config)
107 self.param_dict = self.global_config.get_config_section (self.appname)
109 # Handle signals
110 signal.signal(signal.SIGTERM, self.quit)
111 signal.signal(signal.SIGINT, self.quit)
113 for log in self.log_files[:]:
114 log['logfile_path'] = self.param_dict[log['config_name']]
115 # skip logfiles that dont exist
116 if not os.access(log['logfile_path'], os.R_OK):
117 self.log_files.remove(log)
118 sys.stderr.write( _("Skipping '%s' because it does not exist\n") % log['logfile_path'])
119 else:
120 sys.stderr.write( _("Watching %s\n") % log['logfile_path'])
121 sys.stderr.flush()
123 max_lines_text = self.param_dict['max_lines']
124 self.max_lines = int (max_lines_text)
126 # Load the glade file
127 builder = Gtk.Builder()
128 builder.add_from_file(find_data_file("ladi-system-log.ui"))
129 sys.stderr.write( _("Loading interface from %s\n") % find_data_file("ladilog_ui.ui"))
130 sys.stderr.flush()
132 # Get the ui ready for action
133 self.event_dict = {"on_ladilog_ui_destroy" : self.quit,
134 "on_ladilog_ui_delete" : self.on_delete,
135 "on_close_button_clicked" : self.quit,
136 "on_clear_button_clicked" : self.on_clear_text,
137 "on_purge_button_clicked" : self.on_purge}
138 builder.connect_signals(self.event_dict)
140 self.ui = ui = builder.get_object("ladilog_ui")
141 self.logview_notebook = builder.get_object ("ladilog_notebook")
143 # Create our terminal and display it
144 for log in self.log_files:
145 log['scrolled_window'] = sw = Gtk.ScrolledWindow()
146 log['term'] = term = Vte.Terminal.new ()
147 sw.set_policy(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
148 vscrollbar_policy=Gtk.PolicyType.ALWAYS)
149 sw.add(term)
150 sw.show()
151 term.set_scroll_on_output(True)
152 log["tab_label"] = Gtk.Label(label=log["name"])
154 self.logview_notebook.append_page(log["scrolled_window"],
155 log["tab_label"])
157 # Make it do something...
158 for log in self.log_files:
159 try:
160 log['log_file'] = open(log['logfile_path'], "rb")
161 sys.stderr.write (_("Opening %s...\n") % log['logfile_path'])
162 lines = read_last(log['log_file'], self.max_lines)
163 for line in lines:
164 line = line.strip('\r\n') + '\r\n'
165 log["term"].feed(line, -1)
166 except ValueError:
167 sys.stderr.write( _("You called Popen with invalid arguments... dumbass\n") )
168 except:
169 sys.stderr.write( _("Unexpected error: %s\n") % (sys.exc_info ()[0]))
170 finally:
171 sys.stderr.flush()
173 self.auto_updater = timeout_add(250, self.update, None)
175 def update(self, user_data = None):
176 # Append latest output to the buffer
177 for log in self.log_files:
178 line = log['log_file'].readline()
179 while line:
180 log["term"].feed(line + '\r', -1)
181 line = log['log_file'].readline()
182 log['log_file'].seek(log['log_file'].tell())
183 return True
185 def on_delete(self, widget=None, data=None):
186 return False
188 def quit (self, *args, **kwargs):
189 self.global_config.set_config_section (self.appname, self.param_dict)
190 self.global_config.save ()
191 Gtk.main_quit ()
193 def on_clear_text (self, data=None):
194 current_view = self.logview_notebook.get_current_page ()
195 self.log_files[current_view]["term"].feed ("\033[2J\033[;f", -1)
197 def on_purge (self, data=None):
198 current_view = self.logview_notebook.get_current_page ()
199 # Opens the file in write anew mode thus clearing the file and close it right away
200 open (self.log_files[current_view]['logfile_path'], "w+")
201 self.log_files[current_view]["term"].feed ("\033[2J\033[;f", -1)
203 def run (self):
204 self.ui.show_all()
205 Gtk.main ()
207 if __name__ == '__main__':
208 parser = argparse.ArgumentParser(description=_('JACK, ladish and a2jmidid log viewer'),
209 epilog=_('This program is part of the LADITools suite.'))
210 parser.add_argument('--version', action='version', version="%(prog)s " + get_version_string())
211 parser.parse_args()
213 LadiSystemLog().run()
214 sys.exit(0)