#!/usr/bin/python # LADITools - Linux Audio Desktop Integration Tools # ladi-system-log - A log viewer for your Linux Audio Desktop # Copyright (C) 2011-2012 Alessio Treglia # Copyright (C) 2007-2010, Marc-Olivier Barre # Copyright (C) 2007-2009, Nedko Arnaudov # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import sys import signal from subprocess import Popen, PIPE import pty import termios import tty import gettext import argparse from laditools import _gettext_domain gettext.install(_gettext_domain) from laditools import get_version_string from laditools import LadiConfiguration from laditools import LadiApp import gi gi.require_version('Gtk', '3.0') from gi.repository import GLib from gi.repository import Gtk from gi.repository import GObject gi.require_version('Vte', '2.91') from gi.repository import Vte from laditools.gtk import find_data_file timeout_add = GLib.timeout_add # Default configuration max_lines_default = 100 # Output the last lines def read_last(lfile, lines): chunk_size = lines * 60 lfile.seek(0, 2) endpos = lfile.tell() pos = endpos - chunk_size if pos < 0: pos = 0 backlog = '' backlog_size = 0 lines += 1 while pos >= 0 and backlog_size <= lines: lfile.seek(pos, 0) s = lfile.read(chunk_size) pos = pos - chunk_size backlog_size += s.count("\n") backlog = s + backlog backlog = backlog.strip().split("\n") if len(backlog) > lines: backlog = backlog[-lines:] lfile.seek(endpos, 0) return backlog class LadiSystemLog(LadiApp): _appname = 'ladi-system-log' _appname_long = _("LADI Log Viewer") _appid = 'org.linuxaudio.ladi.logviewer' _default_config = { 'max_lines' : max_lines_default } def __init__ (self): LadiApp.__init__(self) self.log_files = [ { 'name': 'JACK', 'config_name': 'jackdbus_log', 'config_default': os.sep.join([os.environ['HOME'], ".log", "jack", "jackdbus.log"]) }, { 'name': 'LADISH', 'config_name': 'ladish_log', 'config_default': os.sep.join([os.environ['HOME'], ".log", "ladish", "ladish.log"]) }, { 'name': 'A2J', 'config_name': 'a2j_log', 'config_default': os.sep.join([os.environ['HOME'], ".log", "a2j", "a2j.log"]) }, ] # Handle the configuration for log in self.log_files: self._default_config[log['config_name']] = log['config_default'] self.global_config = LadiConfiguration (self.appname, self._default_config) self.param_dict = self.global_config.get_config_section (self.appname) self.connect_signals_quit() for log in self.log_files[:]: log['logfile_path'] = self.param_dict[log['config_name']] # skip logfiles that dont exist if not os.access(log['logfile_path'], os.R_OK): self.log_files.remove(log) sys.stderr.write( _("Skipping '%s' because it does not exist\n") % log['logfile_path']) else: sys.stderr.write( _("Watching %s\n") % log['logfile_path']) sys.stderr.flush() max_lines_text = self.param_dict['max_lines'] self.max_lines = int (max_lines_text) # Load the glade file uifile = find_data_file('ladi-system-log.ui') builder = Gtk.Builder() builder.add_from_file(uifile) sys.stderr.write( _("Loading interface from %s\n") % uifile) sys.stderr.flush() # Get the ui ready for action self.event_dict = {"on_ladilog_ui_destroy" : self.quit, "on_ladilog_ui_delete" : self.on_delete, "on_close_button_clicked" : self.quit, "on_clear_button_clicked" : self.on_clear_text, "on_purge_button_clicked" : self.on_purge} builder.connect_signals(self.event_dict) self.ui = ui = builder.get_object("ladilog_ui") self.logview_notebook = builder.get_object ("ladilog_notebook") # Create our terminal and display it for log in self.log_files: log['scrolled_window'] = sw = Gtk.ScrolledWindow() log['term'] = term = Vte.Terminal.new () sw.set_policy(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC, vscrollbar_policy=Gtk.PolicyType.ALWAYS) sw.add(term) sw.show() term.set_scroll_on_output(True) log["tab_label"] = Gtk.Label(label=log["name"]) self.logview_notebook.append_page(log["scrolled_window"], log["tab_label"]) # Make it do something... for log in self.log_files: try: log['log_file'] = open(log['logfile_path'], "r") sys.stderr.write (_("Opening %s...\n") % log['logfile_path']) lines = read_last(log['log_file'], self.max_lines) for line in lines: line = line.strip('\r\n') + '\r\n'; log["term"].feed(bytes(line, "utf-8")) except ValueError: sys.stderr.write( _("You called Popen with invalid arguments... dumbass\n") ) except: sys.stderr.write( _("Unexpected error: %s\n") % (sys.exc_info ()[0])) finally: sys.stderr.flush() self.auto_updater = timeout_add(250, self.update, None) def update(self, user_data = None): # Append latest output to the buffer for log in self.log_files: line = log['log_file'].readline() while line: log["term"].feed(bytes(line + '\r', "utf-8")) line = log['log_file'].readline() log['log_file'].seek(log['log_file'].tell()) return True def on_delete(self, widget=None, data=None): return False def quit (self, *args, **kwargs): self.global_config.set_config_section (self.appname, self.param_dict) self.global_config.save () Gtk.main_quit () def on_clear_text (self, data=None): current_view = self.logview_notebook.get_current_page () self.log_files[current_view]["term"].feed(bytes("\033[2J\033[;f", "utf-8")) def on_purge (self, data=None): current_view = self.logview_notebook.get_current_page () # Opens the file in write anew mode thus clearing the file and close it right away open (self.log_files[current_view]['logfile_path'], "w+") self.log_files[current_view]["term"].feed(bytes("\033[2J\033[;f", "utf-8")) def run (self): self.ui.show_all() Gtk.main () if __name__ == '__main__': parser = argparse.ArgumentParser(description=_('JACK, ladish and a2jmidid log viewer'), epilog=_('This program is part of the LADITools suite.')) parser.add_argument('--version', action='version', version="%(prog)s " + get_version_string()) parser.parse_args() Gtk.init(None) LadiSystemLog().run() sys.exit(0)