mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[enh] Allow to display all operation logs with one command
This commit is contained in:
parent
60727e5fab
commit
ba01168684
4 changed files with 69 additions and 59 deletions
|
@ -1603,26 +1603,27 @@ hook:
|
||||||
help: The directory from where the script will be executed
|
help: The directory from where the script will be executed
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
# Journals #
|
# Log #
|
||||||
#############################
|
#############################
|
||||||
journals:
|
log:
|
||||||
category_help: Manage debug journals
|
category_help: Manage debug logs
|
||||||
actions:
|
actions:
|
||||||
|
|
||||||
### journals_list()
|
### log_list()
|
||||||
list:
|
list:
|
||||||
action_help: List journals
|
action_help: List logs
|
||||||
api: GET /journals
|
api: GET /logs
|
||||||
arguments:
|
arguments:
|
||||||
-l:
|
-l:
|
||||||
full: --limit
|
full: --limit
|
||||||
help: Maximum number of journals per categories
|
help: Maximum number of logs per categories
|
||||||
type: int
|
type: int
|
||||||
|
|
||||||
### journals_display()
|
### log_display()
|
||||||
display:
|
display:
|
||||||
action_help: Display a journal content
|
action_help: Display a log content
|
||||||
api: GET /journals/<file_name>
|
api: GET /logs/<file_name_list>
|
||||||
arguments:
|
arguments:
|
||||||
file_name:
|
file_name_list:
|
||||||
help: Journal file name
|
help: Log filenames for which to display the content
|
||||||
|
nargs: "*"
|
||||||
|
|
|
@ -205,7 +205,7 @@
|
||||||
"invalid_url_format": "Invalid URL format",
|
"invalid_url_format": "Invalid URL format",
|
||||||
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
|
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
|
||||||
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
|
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
|
||||||
"journal_does_exists": "There is not journal with the name '{journal}', use 'yunohost journal list to see all available journals'",
|
"log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'",
|
||||||
"ldap_init_failed_to_create_admin": "LDAP initialization failed to create admin user",
|
"ldap_init_failed_to_create_admin": "LDAP initialization failed to create admin user",
|
||||||
"ldap_initialized": "LDAP has been initialized",
|
"ldap_initialized": "LDAP has been initialized",
|
||||||
"license_undefined": "undefined",
|
"license_undefined": "undefined",
|
||||||
|
|
|
@ -433,7 +433,7 @@ def app_change_url(auth, app, domain, path):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_exec, hook_callback
|
from yunohost.hook import hook_exec, hook_callback
|
||||||
from yunohost.journals import Journal
|
from yunohost.log import Journal
|
||||||
|
|
||||||
installed = _is_installed(app)
|
installed = _is_installed(app)
|
||||||
if not installed:
|
if not installed:
|
||||||
|
@ -542,7 +542,7 @@ def app_upgrade(auth, app=[], url=None, file=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||||
from yunohost.journals import Journal
|
from yunohost.log import Journal
|
||||||
|
|
||||||
# Retrieve interface
|
# Retrieve interface
|
||||||
is_api = msettings.get('interface') == 'api'
|
is_api = msettings.get('interface') == 'api'
|
||||||
|
@ -673,7 +673,7 @@ def app_install(auth, app, label=None, args=None, no_remove_on_failure=False):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||||
from yunohost.journals import Journal
|
from yunohost.log import Journal
|
||||||
|
|
||||||
# Fetch or extract sources
|
# Fetch or extract sources
|
||||||
try:
|
try:
|
||||||
|
@ -837,7 +837,7 @@ def app_remove(auth, app):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_exec, hook_remove, hook_callback
|
from yunohost.hook import hook_exec, hook_remove, hook_callback
|
||||||
from yunohost.journals import Journal
|
from yunohost.log import Journal
|
||||||
|
|
||||||
if not _is_installed(app):
|
if not _is_installed(app):
|
||||||
raise MoulinetteError(errno.EINVAL,
|
raise MoulinetteError(errno.EINVAL,
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
""" yunohost_journals.py
|
""" yunohost_log.py
|
||||||
|
|
||||||
Manage debug journals
|
Manage debug logs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -34,90 +34,98 @@ from moulinette import m18n
|
||||||
from moulinette.core import MoulinetteError
|
from moulinette.core import MoulinetteError
|
||||||
from moulinette.utils.log import getActionLogger
|
from moulinette.utils.log import getActionLogger
|
||||||
|
|
||||||
JOURNALS_PATH = '/var/log/yunohost/journals/'
|
OPERATIONS_PATH = '/var/log/yunohost/operation/'
|
||||||
|
OPERATION_FILE_EXT = '.yml'
|
||||||
|
|
||||||
logger = getActionLogger('yunohost.journals')
|
logger = getActionLogger('yunohost.log')
|
||||||
|
|
||||||
|
|
||||||
def journals_list(limit=None):
|
def log_list(limit=None):
|
||||||
"""
|
"""
|
||||||
List available journals
|
List available logs
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
limit -- Maximum number of journals per categories
|
limit -- Maximum number of logs per categories
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = {"categories": []}
|
result = {"categories": []}
|
||||||
|
|
||||||
if not os.path.exists(JOURNALS_PATH):
|
if not os.path.exists(OPERATIONS_PATH):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
for category in sorted(os.listdir(JOURNALS_PATH)):
|
for category in sorted(os.listdir(OPERATIONS_PATH)):
|
||||||
result["categories"].append({"name": category, "journals": []})
|
result["categories"].append({"name": category, "logs": []})
|
||||||
for journal in filter(lambda x: x.endswith(".journal"), os.listdir(os.path.join(JOURNALS_PATH, category))):
|
for operation in filter(lambda x: x.endswith(OPERATION_FILE_EXT), os.listdir(os.path.join(OPERATIONS_PATH, category))):
|
||||||
|
|
||||||
file_name = journal
|
file_name = operation
|
||||||
|
|
||||||
journal = journal[:-len(".journal")]
|
operation = operation[:-len(OPERATION_FILE_EXT)]
|
||||||
journal = journal.split("_")
|
operation = operation.split("_")
|
||||||
|
|
||||||
journal_datetime = datetime.strptime(" ".join(journal[-2:]), "%Y-%m-%d %H-%M-%S")
|
operation_datetime = datetime.strptime(" ".join(operation[-2:]), "%Y-%m-%d %H-%M-%S")
|
||||||
|
|
||||||
result["categories"][-1]["journals"].append({
|
result["categories"][-1]["logs"].append({
|
||||||
"started_at": journal_datetime,
|
"started_at": operation_datetime,
|
||||||
"name": " ".join(journal[:-2]),
|
"name": " ".join(operation[:-2]),
|
||||||
"file_name": file_name,
|
"file_name": file_name,
|
||||||
"path": os.path.join(JOURNALS_PATH, category, file_name),
|
"path": os.path.join(OPERATIONS_PATH, category, file_name),
|
||||||
})
|
})
|
||||||
|
|
||||||
result["categories"][-1]["journals"] = list(reversed(sorted(result["categories"][-1]["journals"], key=lambda x: x["started_at"])))
|
result["categories"][-1]["logs"] = list(reversed(sorted(result["categories"][-1]["logs"], key=lambda x: x["started_at"])))
|
||||||
|
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
result["categories"][-1]["journals"] = result["categories"][-1]["journals"][:limit]
|
result["categories"][-1]["logs"] = result["categories"][-1]["logs"][:limit]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def journals_display(file_name):
|
def log_display(file_name_list):
|
||||||
"""
|
"""
|
||||||
Display a journal content
|
Display full log or specific logs listed
|
||||||
|
|
||||||
Argument:
|
Argument:
|
||||||
file_name
|
file_name_list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not os.path.exists(JOURNALS_PATH):
|
if not os.path.exists(OPERATIONS_PATH):
|
||||||
raise MoulinetteError(errno.EINVAL,
|
raise MoulinetteError(errno.EINVAL,
|
||||||
m18n.n('journal_does_exists', journal=file_name))
|
m18n.n('log_does_exists', log=" ".join(file_name_list)))
|
||||||
|
|
||||||
for category in os.listdir(JOURNALS_PATH):
|
result = {"logs": []}
|
||||||
for journal in filter(lambda x: x.endswith(".journal"), os.listdir(os.path.join(JOURNALS_PATH, category))):
|
|
||||||
if journal != file_name:
|
for category in os.listdir(OPERATIONS_PATH):
|
||||||
|
for operation in filter(lambda x: x.endswith(OPERATION_FILE_EXT), os.listdir(os.path.join(OPERATIONS_PATH, category))):
|
||||||
|
if operation not in file_name_list and file_name_list:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with open(os.path.join(JOURNALS_PATH, category, file_name), "r") as content:
|
file_name = operation
|
||||||
|
|
||||||
|
with open(os.path.join(OPERATIONS_PATH, category, file_name), "r") as content:
|
||||||
content = content.read()
|
content = content.read()
|
||||||
|
|
||||||
journal = journal[:-len(".journal")]
|
operation = operation[:-len(OPERATION_FILE_EXT)]
|
||||||
journal = journal.split("_")
|
operation = operation.split("_")
|
||||||
journal_datetime = datetime.strptime(" ".join(journal[-2:]), "%Y-%m-%d %H-%M-%S")
|
operation_datetime = datetime.strptime(" ".join(operation[-2:]), "%Y-%m-%d %H-%M-%S")
|
||||||
|
|
||||||
infos, logs = content.split("\n---\n", 1)
|
infos, logs = content.split("\n---\n", 1)
|
||||||
infos = yaml.safe_load(infos)
|
infos = yaml.safe_load(infos)
|
||||||
logs = [{"datetime": x.split(": ", 1)[0].replace("_", " "), "line": x.split(": ", 1)[1]} for x in logs.split("\n") if x]
|
logs = [{"datetime": x.split(": ", 1)[0].replace("_", " "), "line": x.split(": ", 1)[1]} for x in logs.split("\n") if x]
|
||||||
|
|
||||||
return {
|
result['logs'].append({
|
||||||
"started_at": journal_datetime,
|
"started_at": operation_datetime,
|
||||||
"name": " ".join(journal[:-2]),
|
"name": " ".join(operation[:-2]),
|
||||||
"file_name": file_name,
|
"file_name": file_name,
|
||||||
"path": os.path.join(JOURNALS_PATH, category, file_name),
|
"path": os.path.join(OPERATIONS_PATH, category, file_name),
|
||||||
"metadata": infos,
|
"metadata": infos,
|
||||||
"logs": logs,
|
"logs": logs,
|
||||||
}
|
})
|
||||||
|
|
||||||
raise MoulinetteError(errno.EINVAL,
|
logger.debug("====> %s", len(file_name_list), exc_info=1)
|
||||||
m18n.n('journal_does_exists', journal=file_name))
|
if len(file_name_list) > 0 and len(result['logs']) < len(file_name_list):
|
||||||
|
logger.error(m18n.n('log_does_exists', log="', '".join(file_name_list)))
|
||||||
|
|
||||||
|
if len(result['logs']) > 0:
|
||||||
|
return result
|
||||||
|
|
||||||
class Journal(object):
|
class Journal(object):
|
||||||
def __init__(self, name, category, on_stdout=None, on_stderr=None, on_write=None, **kwargs):
|
def __init__(self, name, category, on_stdout=None, on_stderr=None, on_write=None, **kwargs):
|
||||||
|
@ -129,7 +137,7 @@ class Journal(object):
|
||||||
# this help uniformise file name and avoir threads concurrency errors
|
# this help uniformise file name and avoir threads concurrency errors
|
||||||
self.started_at = datetime.now()
|
self.started_at = datetime.now()
|
||||||
|
|
||||||
self.path = os.path.join(JOURNALS_PATH, category)
|
self.path = os.path.join(OPERATIONS_PATH, category)
|
||||||
|
|
||||||
self.fd = None
|
self.fd = None
|
||||||
|
|
||||||
|
@ -157,7 +165,8 @@ class Journal(object):
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
os.makedirs(self.path)
|
os.makedirs(self.path)
|
||||||
|
|
||||||
file_name = "%s_%s.journal" % (self.name if isinstance(self.name, basestring) else "_".join(self.name), self.started_at.strftime("%F_%X").replace(":", "-"))
|
file_name = "%s_%s" % (self.name if isinstance(self.name, basestring) else "_".join(self.name), self.started_at.strftime("%F_%X").replace(":", "-"))
|
||||||
|
file_name += OPERATION_FILE_EXT
|
||||||
|
|
||||||
serialized_additional_information = yaml.safe_dump(self.additional_information, default_flow_style=False)
|
serialized_additional_information = yaml.safe_dump(self.additional_information, default_flow_style=False)
|
||||||
|
|
Loading…
Add table
Reference in a new issue