diff --git a/data/interfaces/default/logs.html b/data/interfaces/default/logs.html
index b0731c8d..56e6a4dc 100644
--- a/data/interfaces/default/logs.html
+++ b/data/interfaces/default/logs.html
@@ -33,6 +33,19 @@
+ % if plex_log_files:
+
+
+
+ % endif
-
@@ -59,7 +71,6 @@
Tautulli Logs
Tautulli API Logs
Plex Media Server Logs
- Plex Media Scanner Logs
Plex Websocket Logs
Notification Logs
Newsletter Logs
@@ -114,18 +125,6 @@
-
-
-
-
- Timestamp |
- Level |
- Message |
-
-
-
-
-
@@ -268,24 +267,18 @@
});
}
- function loadPlexLogs() {
+ function loadPlexLogs(logfile) {
plex_log_table_options.ajax = {
- url: 'get_plex_log?log_type=server',
- type: 'POST'
+ url: 'get_plex_log',
+ type: 'POST',
+ data: {
+ logfile: logfile
+ }
};
plex_log_table_options.initComplete = bindLogLevelFilter;
plex_log_table = $('#plex_log_table').DataTable(plex_log_table_options);
}
- function loadPlexScannerLogs() {
- plex_log_table_options.ajax = {
- url: 'get_plex_log?log_type=scanner',
- type: 'POST'
- };
- plex_log_table_options.initComplete = bindLogLevelFilter;
- plex_scanner_log_table = $('#plex_scanner_log_table').DataTable(plex_log_table_options);
- }
-
function loadNotificationLogs() {
notification_log_table_options.ajax = {
url: 'get_notification_log',
@@ -327,11 +320,11 @@
$("#tautulli-logs-btn").click(function () {
$("#tautulli-log-levels").show();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").show();
$("#download-tautullilog").show();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").hide();
@@ -341,11 +334,11 @@
$("#tautulli-api-logs-btn").click(function () {
$("#tautulli-log-levels").show();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").show();
$("#download-tautullilog").show();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").hide();
@@ -355,11 +348,11 @@
$("#plex-websocket-logs-btn").click(function () {
$("#tautulli-log-levels").show();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").show();
$("#download-tautullilog").show();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").hide();
@@ -369,11 +362,11 @@
$("#plex-logs-btn").click(function () {
$("#tautulli-log-levels").hide();
+ $("#plex-log-files").show();
$("#plex-log-levels").show();
$("#clear-logs").hide();
$("#download-tautullilog").hide();
$("#download-plexserverlog").show();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").hide();
@@ -381,27 +374,13 @@
clearSearchButton('plex_log_table', plex_log_table);
});
- $("#plex-scanner-logs-btn").click(function () {
- $("#tautulli-log-levels").hide();
- $("#plex-log-levels").show();
- $("#clear-logs").hide();
- $("#download-tautullilog").hide();
- $("#download-plexserverlog").hide();
- $("#download-plexscannerlog").show();
- $("#clear-notify-logs").hide();
- $("#clear-newsletter-logs").hide();
- $("#clear-login-logs").hide();
- loadPlexScannerLogs();
- clearSearchButton('plex_scanner_log_table', plex_scanner_log_table);
- });
-
$("#notification-logs-btn").click(function () {
$("#tautulli-log-levels").hide();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").hide();
$("#download-tautullilog").hide();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").show();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").hide();
@@ -411,11 +390,11 @@
$("#newsletter-logs-btn").click(function () {
$("#tautulli-log-levels").hide();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").hide();
$("#download-tautullilog").hide();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").show();
$("#clear-login-logs").hide();
@@ -425,11 +404,11 @@
$("#login-logs-btn").click(function () {
$("#tautulli-log-levels").hide();
+ $("#plex-log-files").hide();
$("#plex-log-levels").hide();
$("#clear-logs").hide();
$("#download-tautullilog").hide();
$("#download-plexserverlog").hide();
- $("#download-plexscannerlog").hide();
$("#clear-notify-logs").hide();
$("#clear-newsletter-logs").hide();
$("#clear-login-logs").show();
@@ -437,6 +416,12 @@
clearSearchButton('login_log_table', notification_log_table);
});
+ $("#plex-log-files").on('change', function () {
+ var logfile = $("option:selected", this).val();
+ loadPlexLogs(logfile);
+ clearSearchButton('plex_log_table', plex_log_table);
+ });
+
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
@@ -472,11 +457,8 @@
});
$("#download-plexserverlog").click(function () {
- window.location.href = "download_plex_log?log_type=server";
- });
-
- $("#download-plexscannerlog").click(function () {
- window.location.href = "download_plex_log?log_type=scanner";
+ var logfile = $("option:selected", "#plex-log-files").val();
+ window.location.href = "download_plex_log?logfile=" + logfile;
});
$("#clear-notify-logs").click(function () {
@@ -559,8 +541,6 @@
log_table.ajax.reload();
} else if ($("#tabs-plex_log").hasClass("active")) {
plex_log_table.ajax.reload();
- } else if ($("#tabs-plex_scanner_log").hasClass("active")) {
- plex_scanner_log_table.ajax.reload();
} else if ($("#tabs-notificaiton_log").hasClass("active")) {
notification_log_table.ajax.reload();
} else if ($("#tabs-login_log").hasClass("active")) {
diff --git a/plexpy/log_reader.py b/plexpy/log_reader.py
index 76d8b23c..358ca86c 100644
--- a/plexpy/log_reader.py
+++ b/plexpy/log_reader.py
@@ -29,19 +29,34 @@ else:
from plexpy import logger
-def get_log_tail(window=20, parsed=True, log_type="server"):
+def list_plex_logs():
+ logs_dir = plexpy.CONFIG.PMS_LOGS_FOLDER
- if plexpy.CONFIG.PMS_LOGS_FOLDER:
- log_file = ""
- if log_type == "server":
- log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, 'Plex Media Server.log')
- elif log_type == "scanner":
- log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, 'Plex Media Scanner.log')
- else:
+ if not logs_dir or logs_dir and not os.path.exists(logs_dir):
return []
+ log_files = []
+ for file in os.listdir(logs_dir):
+ if file.startswith('Plex Transcoder Statistics'):
+ # Plex Transcoder Statistics is an XML file
+ continue
+ if os.path.isfile(os.path.join(logs_dir, file)):
+ name, ext = os.path.splitext(file)
+ if ext == '.log' and not name[-1].isdigit():
+ log_files.append(name)
+
+ return log_files
+
+
+def get_log_tail(window=20, parsed=True, log_file=''):
+ if not plexpy.CONFIG.PMS_LOGS_FOLDER:
+ return []
+
+ log_file = (log_file or 'Plex Media Server') + '.log'
+ log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, log_file)
+
try:
- logfile = open(log_file, "r", encoding="utf-8")
+ logfile = open(log_file, 'r', encoding='utf-8')
except IOError as e:
logger.error('Unable to open Plex Log file. %s' % e)
return []
@@ -52,6 +67,8 @@ def get_log_tail(window=20, parsed=True, log_type="server"):
line_error = False
clean_lines = []
for i in log_lines:
+ if not i.strip():
+ continue
try:
log_time = i.split(' [')[0]
log_level = i.split('] ', 1)[1].split(' - ', 1)[0]
@@ -74,7 +91,6 @@ def get_log_tail(window=20, parsed=True, log_type="server"):
return raw_lines
- return log_lines
# http://stackoverflow.com/a/13790289/2405162
def tail(f, lines=1, _buffer=4098):
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 879b89f7..78a69063 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -2748,7 +2748,8 @@ class WebInterface(object):
@cherrypy.expose
@requireAuth(member_of("admin"))
def logs(self, **kwargs):
- return serve_template(templatename="logs.html", title="Log")
+ plex_log_files = log_reader.list_plex_logs()
+ return serve_template(templatename="logs.html", title="Log", plex_log_files=plex_log_files)
@cherrypy.expose
@requireAuth(member_of("admin"))
@@ -2821,7 +2822,7 @@ class WebInterface(object):
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
@addtoapi()
- def get_plex_log(self, **kwargs):
+ def get_plex_log(self, logfile='', **kwargs):
""" Get the PMS logs.
```
@@ -2830,7 +2831,8 @@ class WebInterface(object):
Optional parameters:
window (int): The number of tail lines to return
- log_type (str): "server" or "scanner"
+ logfile (int): The name of the Plex log file,
+ e.g. "Plex Media Server", "Plex Media Scanner"
Returns:
json:
@@ -2843,16 +2845,16 @@ class WebInterface(object):
]
```
"""
+ if kwargs.get('log_type'):
+ logfile = 'Plex Media ' + kwargs['log_type'].capitalize()
+
window = int(kwargs.get('window', plexpy.CONFIG.PMS_LOGS_LINE_CAP))
- log_lines = []
- log_type = kwargs.get('log_type', 'server')
try:
- log_lines = {'data': log_reader.get_log_tail(window=window, parsed=True, log_type=log_type)}
+ return {'data': log_reader.get_log_tail(window=window, parsed=True, log_file=logfile)}
except:
- logger.warn("Unable to retrieve Plex Logs.")
-
- return log_lines
+ logger.warn("Unable to retrieve Plex log file '%'." % logfile)
+ return []
@cherrypy.expose
@cherrypy.tools.json_out()
@@ -4952,7 +4954,20 @@ class WebInterface(object):
@requireAuth(member_of("admin"))
@addtoapi()
def download_log(self, logfile='', **kwargs):
- """ Download the Tautulli log file. """
+ """ Download the Tautulli log file.
+
+ ```
+ Required parameters:
+ None
+
+ Optional parameters:
+ logfile (str): The name of the Tautulli log file,
+ "tautulli", "tautulli_api", "plex_websocket"
+
+ Returns:
+ download
+ ```
+ """
if logfile == "tautulli_api":
filename = logger.FILENAME_API
log = logger.logger_api
@@ -4973,26 +4988,35 @@ class WebInterface(object):
@cherrypy.expose
@requireAuth(member_of("admin"))
@addtoapi()
- def download_plex_log(self, **kwargs):
- """ Download the Plex log file. """
- log_type = kwargs.get('log_type', 'server')
+ def download_plex_log(self, logfile='', **kwargs):
+ """ Download the Plex log file.
- log_file = ""
- if plexpy.CONFIG.PMS_LOGS_FOLDER:
- if log_type == "server":
- log_file = 'Plex Media Server.log'
- log_file_path = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, log_file)
- elif log_type == "scanner":
- log_file = 'Plex Media Scanner.log'
- log_file_path = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, log_file)
- else:
+ ```
+ Required parameters:
+ None
+
+ Optional parameters:
+ logfile (int): The name of the Plex log file,
+ e.g. "Plex Media Server", "Plex Media Scanner"
+
+ Returns:
+ download
+ ```
+ """
+ if not plexpy.CONFIG.PMS_LOGS_FOLDER:
return "Plex log folder not set in the settings."
+ if kwargs.get('log_type'):
+ logfile = 'Plex Media ' + kwargs['log_type'].capitalize()
+
+ log_file = (logfile or 'Plex Media Server') + '.log'
+ log_file_path = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, log_file)
if log_file and os.path.isfile(log_file_path):
- return serve_download(log_file_path, name=log_file)
+ log_file_name = os.path.basename(log_file_path)
+ return serve_download(log_file_path, name=log_file_name)
else:
- return "Plex %s log file not found." % log_type
+ return "Plex log file '%s' not found." % log_file
@cherrypy.expose
@cherrypy.tools.json_out()