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/>.
25 from subprocess
import Popen
, PIPE
27 from signal
import SIGTERM
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
56 pos
= endpos
- chunk_size
62 while pos
>= 0 and backlog_size
<= lines
:
64 s
= lfile
.read(chunk_size
)
65 pos
= pos
- chunk_size
66 backlog_size
+= s
.count("\n")
68 backlog
= backlog
.strip().split("\n")
69 if len(backlog
) > lines
:
70 backlog
= backlog
[-lines
:]
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
}
83 LadiApp
.__init
__(self
)
88 'config_name': 'jackdbus_log',
89 'config_default': os
.sep
.join([os
.environ
['HOME'], ".log", "jack", "jackdbus.log"])
93 'config_name': 'ladish_log',
94 'config_default': os
.sep
.join([os
.environ
['HOME'], ".log", "ladish", "ladish.log"])
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
)
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'])
120 sys
.stderr
.write( _("Watching %s\n") % log
['logfile_path'])
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"))
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
)
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"],
157 # Make it do something...
158 for log
in self
.log_files
:
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
)
164 line
= line
.strip('\r\n') + '\r\n'
165 log
["term"].feed(line
, -1)
167 sys
.stderr
.write( _("You called Popen with invalid arguments... dumbass\n") )
169 sys
.stderr
.write( _("Unexpected error: %s\n") % (sys
.exc_info ()[0]))
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()
180 log
["term"].feed(line
+ '\r', -1)
181 line
= log
['log_file'].readline()
182 log
['log_file'].seek(log
['log_file'].tell())
185 def on_delete(self
, widget
=None, data
=None):
188 def quit (self
, *args
, **kwargs
):
189 self
.global_config
.set_config_section (self
.appname
, self
.param_dict
)
190 self
.global_config
.save ()
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)
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())
213 LadiSystemLog().run()