mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 07:46:07 -07:00
Add option in settings to view PMS logs, shows last 1000 lines.
Table styling fix.
This commit is contained in:
parent
349a850451
commit
0810584b46
9 changed files with 212 additions and 24 deletions
|
@ -145,11 +145,22 @@
|
||||||
<p class="help-block">Set your preferred time format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
<p class="help-block">Set your preferred time format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div class="wellheader">
|
||||||
|
<h3>Plex Logs</h3>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pms_logs_folder">Logs Folder</label>
|
||||||
|
<input type="text" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change">
|
||||||
|
<p class="help-block">Set the folder where your Plex Server logs are. <br/><a href="https://support.plex.tv/hc/en-us/articles/200250417-Plex-Media-Server-Log-Files" target="_blank">Click here</a> for help.</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="wellheader">
|
<div class="wellheader">
|
||||||
<h3>PMS Settings</h3>
|
<h3>Plex Media Server</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_ip">PMS IP</label>
|
<label for="pms_ip">PMS IP</label>
|
||||||
|
@ -171,7 +182,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
<div class="wellheader">
|
<div class="wellheader">
|
||||||
<h3>PlexWatch Settings</h3>
|
<h3>PlexWatch</h3>
|
||||||
</div>
|
</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
clear: both;
|
clear: both;
|
||||||
zoom: 1; /* Feeling sorry for IE */
|
zoom: 1; /* Feeling sorry for IE */
|
||||||
margin-left: -20px;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8182,7 +8182,7 @@ ol.test >li {
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-card-back {
|
.table-card-back {
|
||||||
padding: 20px 20px 20px 40px;
|
padding: 20px;
|
||||||
background-color: #282828;
|
background-color: #282828;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
$('#log_table').dataTable( {
|
var log_table_options = {
|
||||||
|
"destroy": true,
|
||||||
"responsive": {
|
"responsive": {
|
||||||
details: false
|
details: false
|
||||||
},
|
},
|
||||||
"processing": false,
|
|
||||||
"serverSide": true,
|
"serverSide": true,
|
||||||
"ajax": {
|
"processing": false,
|
||||||
"url": "getLog"
|
|
||||||
},
|
|
||||||
"sPaginationType": "bootstrap",
|
"sPaginationType": "bootstrap",
|
||||||
"order": [ 0, 'desc'],
|
"order": [ 0, 'desc'],
|
||||||
"pageLength": 10,
|
"pageLength": 10,
|
||||||
"stateSave": true,
|
"stateSave": false,
|
||||||
"language": {
|
"language": {
|
||||||
"search":"Search: ",
|
"search":"Search: ",
|
||||||
"lengthMenu":"Show _MENU_ lines per page",
|
"lengthMenu":"Show _MENU_ lines per page",
|
||||||
|
@ -41,4 +39,4 @@ $('#log_table').dataTable( {
|
||||||
$('#ajaxMsg').html("<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>");
|
$('#ajaxMsg').html("<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>");
|
||||||
$('#ajaxMsg').addClass('success').fadeIn();
|
$('#ajaxMsg').addClass('success').fadeIn();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
33
data/interfaces/default/js/tables/plex_logs.js
Normal file
33
data/interfaces/default/js/tables/plex_logs.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
var plex_log_table_options = {
|
||||||
|
"destroy": true,
|
||||||
|
"responsive": {
|
||||||
|
details: false
|
||||||
|
},
|
||||||
|
"processing": false,
|
||||||
|
"serverSide": false,
|
||||||
|
"sPaginationType": "bootstrap",
|
||||||
|
"order": [ 0, 'desc'],
|
||||||
|
"pageLength": 10,
|
||||||
|
"stateSave": false,
|
||||||
|
"language": {
|
||||||
|
"search":"Search: ",
|
||||||
|
"lengthMenu":"Show _MENU_ lines per page",
|
||||||
|
"emptyTable": "No log information available. Have you set your logs folder in the <a href='config'>settings</a>?",
|
||||||
|
"info":"Showing _START_ to _END_ of _TOTAL_ lines",
|
||||||
|
"infoEmpty":"Showing 0 to 0 of 0 lines",
|
||||||
|
"infoFiltered":"(filtered from _MAX_ total lines)"},
|
||||||
|
"columnDefs": [
|
||||||
|
{
|
||||||
|
"targets": [0],
|
||||||
|
"width": "15%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"targets": [1],
|
||||||
|
"width": "10%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"targets": [2],
|
||||||
|
"width": "75%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -31,21 +31,46 @@ from plexpy import helpers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<div class='row-fluid'>
|
<div class='row-fluid'>
|
||||||
<div class='span12'>
|
<div class='span12'>
|
||||||
<div class='table-card-back'>
|
<div class='table-card-back'>
|
||||||
<table class="display" id="log_table" width="100%">
|
<div role="tabpanel">
|
||||||
<thead>
|
<ul class="nav nav-pills" role="tablist">
|
||||||
<tr>
|
<li role="presentation" class="active"><a id="plexpy-logs-btn" href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">PlexPy Logs</a></li>
|
||||||
<th align='left' id="timestamp">Timestamp</th>
|
<li role="presentation"><a id="plex-logs-btn" href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Plex Media Server Logs</a></li>
|
||||||
<th align='left' id="level">Level</th>
|
</ul>
|
||||||
<th align='left' id="message">Message</th>
|
|
||||||
</tr>
|
<div class="tab-content">
|
||||||
</thead>
|
<div role="tabpanel" class="tab-pane active" id="tabs-1">
|
||||||
<tbody>
|
<table class="display" id="log_table" width="100%">
|
||||||
</tbody>
|
<thead>
|
||||||
</table>
|
<tr>
|
||||||
|
<th align='left' id="timestamp">Timestamp</th>
|
||||||
|
<th align='left' id="level">Level</th>
|
||||||
|
<th align='left' id="message">Message</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||||
|
<table class="display" id="plex_log_table" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align='left' id="plex_timestamp">Timestamp</th>
|
||||||
|
<th align='left' id="plex_level">Level</th>
|
||||||
|
<th align='left' id="plex_message">Message</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,7 +96,36 @@ from plexpy import helpers
|
||||||
<script src="interfaces/default/js/dataTables.responsive.js"></script>
|
<script src="interfaces/default/js/dataTables.responsive.js"></script>
|
||||||
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
|
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
|
||||||
<script src="interfaces/default/js/tables/logs.js"></script>
|
<script src="interfaces/default/js/tables/logs.js"></script>
|
||||||
|
<script src="interfaces/default/js/tables/plex_logs.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
LoadPlexPyLogs();
|
||||||
|
});
|
||||||
|
|
||||||
|
function LoadPlexPyLogs() {
|
||||||
|
log_table_options.ajax = {
|
||||||
|
"url": "getLog"
|
||||||
|
}
|
||||||
|
log_table = $('#log_table').DataTable(log_table_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadPlexLogs() {
|
||||||
|
plex_log_table_options.ajax = {
|
||||||
|
"url": "get_plex_log"
|
||||||
|
}
|
||||||
|
plex_log_table = $('#plex_log_table').DataTable(plex_log_table_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#plexpy-logs-btn").click(function() {
|
||||||
|
LoadPlexPyLogs();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#plex-logs-btn").click(function() {
|
||||||
|
console.log('clicked da button');
|
||||||
|
LoadPlexLogs();
|
||||||
|
});
|
||||||
|
|
||||||
var timer;
|
var timer;
|
||||||
function setRefresh()
|
function setRefresh()
|
||||||
{
|
{
|
||||||
|
@ -84,7 +138,13 @@ from plexpy import helpers
|
||||||
}
|
}
|
||||||
if(refreshrate.value != 0)
|
if(refreshrate.value != 0)
|
||||||
{
|
{
|
||||||
timer = setInterval("$('#log_table').dataTable().fnDraw()",1000*refreshrate.value);
|
timer = setInterval(function() {
|
||||||
|
if ($("#tabs-1").hasClass("active")) {
|
||||||
|
log_table.ajax.reload();
|
||||||
|
} else {
|
||||||
|
plex_log_table.ajax.reload();
|
||||||
|
}
|
||||||
|
}, 1000*refreshrate.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ _CONFIG_DEFINITIONS = {
|
||||||
'GROUPING_CHARTS': (int, 'PlexWatch', 0),
|
'GROUPING_CHARTS': (int, 'PlexWatch', 0),
|
||||||
'PLEXWATCH_DATABASE': (str, 'PlexWatch', ''),
|
'PLEXWATCH_DATABASE': (str, 'PlexWatch', ''),
|
||||||
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
||||||
|
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
||||||
'PMS_PORT': (int, 'PMS', 32400),
|
'PMS_PORT': (int, 'PMS', 32400),
|
||||||
'PMS_PASSWORD': (str, 'PMS', ''),
|
'PMS_PASSWORD': (str, 'PMS', ''),
|
||||||
'PMS_TOKEN': (str, 'PMS', ''),
|
'PMS_TOKEN': (str, 'PMS', ''),
|
||||||
|
|
74
plexpy/log_reader.py
Normal file
74
plexpy/log_reader.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is part of PlexPy.
|
||||||
|
#
|
||||||
|
# PlexPy 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.
|
||||||
|
#
|
||||||
|
# PlexPy 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 PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from plexpy import helpers, logger
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import plexpy
|
||||||
|
|
||||||
|
def get_log_tail(window=20):
|
||||||
|
|
||||||
|
if plexpy.CONFIG.PMS_LOGS_FOLDER:
|
||||||
|
log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, 'Plex Media Server.log')
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
try:
|
||||||
|
logfile = open(log_file, "r")
|
||||||
|
except IOError, e:
|
||||||
|
logger.error('Unable to open Plex Log file. %s' % e)
|
||||||
|
return []
|
||||||
|
|
||||||
|
log_lines = tail(logfile, window)
|
||||||
|
clean_lines = []
|
||||||
|
for i in log_lines:
|
||||||
|
log_time = i.split(' [')[0]
|
||||||
|
log_level = i.split('] ', 1)[1].split(' - ',1)[0]
|
||||||
|
log_msg = i.split('] ', 1)[1].split(' - ',1)[1]
|
||||||
|
full_line = [log_time, log_level, log_msg]
|
||||||
|
clean_lines.append(full_line)
|
||||||
|
|
||||||
|
return clean_lines
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail
|
||||||
|
def tail(f, window=20):
|
||||||
|
"""
|
||||||
|
Returns the last `window` lines of file `f` as a list.
|
||||||
|
"""
|
||||||
|
if window == 0:
|
||||||
|
return []
|
||||||
|
BUFSIZ = 1024
|
||||||
|
f.seek(0, 2)
|
||||||
|
bytes = f.tell()
|
||||||
|
size = window + 1
|
||||||
|
block = -1
|
||||||
|
data = []
|
||||||
|
while size > 0 and bytes > 0:
|
||||||
|
if bytes - BUFSIZ > 0:
|
||||||
|
# Seek back one whole BUFSIZ
|
||||||
|
f.seek(block * BUFSIZ, 2)
|
||||||
|
# read BUFFER
|
||||||
|
data.insert(0, f.read(BUFSIZ))
|
||||||
|
else:
|
||||||
|
# file too small, start from begining
|
||||||
|
f.seek(0,0)
|
||||||
|
# only read what was not read
|
||||||
|
data.insert(0, f.read(bytes))
|
||||||
|
linesFound = data[0].count('\n')
|
||||||
|
size -= linesFound
|
||||||
|
bytes -= BUFSIZ
|
||||||
|
block -= 1
|
||||||
|
return ''.join(data).splitlines()[-window:]
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, notifiers, plextv, pmsconnect, plexwatch, db, common
|
from plexpy import logger, notifiers, plextv, pmsconnect, plexwatch, db, common, log_reader
|
||||||
from plexpy.helpers import checked, radio
|
from plexpy.helpers import checked, radio
|
||||||
|
|
||||||
from mako.lookup import TemplateLookup
|
from mako.lookup import TemplateLookup
|
||||||
|
@ -229,6 +229,17 @@ class WebInterface(object):
|
||||||
'data': rows,
|
'data': rows,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
def get_plex_log(self, window=1000, **kwargs):
|
||||||
|
log_lines = []
|
||||||
|
try:
|
||||||
|
log_lines = {'data': log_reader.get_log_tail(window=window)}
|
||||||
|
except:
|
||||||
|
logger.warn("Unable to retrieve Plex Logs.")
|
||||||
|
|
||||||
|
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||||
|
return json.dumps(log_lines)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def generateAPI(self):
|
def generateAPI(self):
|
||||||
apikey = hashlib.sha224(str(random.getrandbits(256))).hexdigest()[0:32]
|
apikey = hashlib.sha224(str(random.getrandbits(256))).hexdigest()[0:32]
|
||||||
|
@ -305,6 +316,7 @@ class WebInterface(object):
|
||||||
"email_smtp_port": int(plexpy.CONFIG.EMAIL_SMTP_PORT),
|
"email_smtp_port": int(plexpy.CONFIG.EMAIL_SMTP_PORT),
|
||||||
"email_tls": checked(plexpy.CONFIG.EMAIL_TLS),
|
"email_tls": checked(plexpy.CONFIG.EMAIL_TLS),
|
||||||
"pms_ip": plexpy.CONFIG.PMS_IP,
|
"pms_ip": plexpy.CONFIG.PMS_IP,
|
||||||
|
"pms_logs_folder": plexpy.CONFIG.PMS_LOGS_FOLDER,
|
||||||
"pms_port": plexpy.CONFIG.PMS_PORT,
|
"pms_port": plexpy.CONFIG.PMS_PORT,
|
||||||
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
||||||
"pms_use_bif": checked(plexpy.CONFIG.PMS_USE_BIF),
|
"pms_use_bif": checked(plexpy.CONFIG.PMS_USE_BIF),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue