mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[enh] Add coments in log part
This commit is contained in:
parent
24bd5f9c83
commit
2f5861a6a5
1 changed files with 86 additions and 1 deletions
|
@ -84,13 +84,17 @@ def log_list(limit=None, full=False):
|
||||||
|
|
||||||
def log_display(file_name, number=50):
|
def log_display(file_name, number=50):
|
||||||
"""
|
"""
|
||||||
Display full log or specific logs listed
|
Display a log file enriched with metadata if any.
|
||||||
|
|
||||||
|
If the file_name is not an absolute path, it will try to search the file in
|
||||||
|
the unit operations log path (see OPERATIONS_PATH).
|
||||||
|
|
||||||
Argument:
|
Argument:
|
||||||
file_name
|
file_name
|
||||||
number
|
number
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Normalize log/metadata paths and filenames
|
||||||
abs_path = file_name
|
abs_path = file_name
|
||||||
log_path = None
|
log_path = None
|
||||||
if not file_name.startswith('/'):
|
if not file_name.startswith('/'):
|
||||||
|
@ -110,11 +114,14 @@ def log_display(file_name, number=50):
|
||||||
m18n.n('log_does_exists', log=file_name))
|
m18n.n('log_does_exists', log=file_name))
|
||||||
|
|
||||||
infos = {}
|
infos = {}
|
||||||
|
|
||||||
|
# If it's a unit operation, display the name and the description
|
||||||
if base_path.startswith(OPERATIONS_PATH):
|
if base_path.startswith(OPERATIONS_PATH):
|
||||||
operation = base_filename.split("-")
|
operation = base_filename.split("-")
|
||||||
infos['description'] = m18n.n("log_" + operation[2], *operation[3:]),
|
infos['description'] = m18n.n("log_" + operation[2], *operation[3:]),
|
||||||
infos['name'] = base_filename
|
infos['name'] = base_filename
|
||||||
|
|
||||||
|
# Display metadata if exist
|
||||||
if os.path.exists(md_path):
|
if os.path.exists(md_path):
|
||||||
with open(md_path, "r") as md_file:
|
with open(md_path, "r") as md_file:
|
||||||
try:
|
try:
|
||||||
|
@ -130,6 +137,7 @@ def log_display(file_name, number=50):
|
||||||
else:
|
else:
|
||||||
raise MoulinetteError(errno.EINVAL, error)
|
raise MoulinetteError(errno.EINVAL, error)
|
||||||
|
|
||||||
|
# Display logs if exist
|
||||||
if os.path.exists(log_path):
|
if os.path.exists(log_path):
|
||||||
from yunohost.service import _tail
|
from yunohost.service import _tail
|
||||||
logs = _tail(log_path, int(number))
|
logs = _tail(log_path, int(number))
|
||||||
|
@ -140,8 +148,34 @@ def log_display(file_name, number=50):
|
||||||
return infos
|
return infos
|
||||||
|
|
||||||
def is_unit_operation(entities='app,domain,service,user', exclude='auth,password', operation_key=None, auto=True):
|
def is_unit_operation(entities='app,domain,service,user', exclude='auth,password', operation_key=None, auto=True):
|
||||||
|
"""
|
||||||
|
Configure quickly a unit operation
|
||||||
|
|
||||||
|
This decorator help you to configure quickly the record of a unit operations.
|
||||||
|
|
||||||
|
Argument:
|
||||||
|
entities A list seperated by coma of entity types related to the unit
|
||||||
|
operation. The entity type is searched inside argument's names of the
|
||||||
|
decorated function. If something match, the argument value is added as
|
||||||
|
related entity.
|
||||||
|
|
||||||
|
exclude Remove some arguments from the context. By default, arguments
|
||||||
|
called 'password' and 'auth' are removed. If an argument is an object, you
|
||||||
|
need to exclude it or create manually the unit operation without this
|
||||||
|
decorator.
|
||||||
|
|
||||||
|
operation_key Key describing the unit operation. If you want to display a
|
||||||
|
well formed description you should add a translation key like this
|
||||||
|
"log_" + operation_key in locales files.
|
||||||
|
|
||||||
|
auto If true, start the recording. If False, the unit operation object
|
||||||
|
created is given to the decorated function as the first argument and you can
|
||||||
|
start recording at the good time.
|
||||||
|
"""
|
||||||
def decorate(func):
|
def decorate(func):
|
||||||
def func_wrapper(*args, **kwargs):
|
def func_wrapper(*args, **kwargs):
|
||||||
|
# For a strange reason we can't use directly the arguments from
|
||||||
|
# is_unit_operation function. We need to store them in a var before.
|
||||||
entities_list = entities.split(',')
|
entities_list = entities.split(',')
|
||||||
exclude_list = exclude.split(',')
|
exclude_list = exclude.split(',')
|
||||||
op_key = operation_key
|
op_key = operation_key
|
||||||
|
@ -150,6 +184,7 @@ def is_unit_operation(entities='app,domain,service,user', exclude='auth,password
|
||||||
if op_key is None:
|
if op_key is None:
|
||||||
op_key = func.__name__
|
op_key = func.__name__
|
||||||
|
|
||||||
|
# Search related entity in arguments of the decorated function
|
||||||
for entity in entities_list:
|
for entity in entities_list:
|
||||||
entity = entity.split(':')
|
entity = entity.split(':')
|
||||||
entity_type = entity[-1]
|
entity_type = entity[-1]
|
||||||
|
@ -162,10 +197,15 @@ def is_unit_operation(entities='app,domain,service,user', exclude='auth,password
|
||||||
related_to.append({entity_type: kwargs[x]})
|
related_to.append({entity_type: kwargs[x]})
|
||||||
|
|
||||||
context = kwargs.copy()
|
context = kwargs.copy()
|
||||||
|
|
||||||
|
# Exclude unappropriate data from the context
|
||||||
for field in exclude_list:
|
for field in exclude_list:
|
||||||
if field in context:
|
if field in context:
|
||||||
context.pop(field, None)
|
context.pop(field, None)
|
||||||
uo = UnitOperation(op_key, related_to, args=context)
|
uo = UnitOperation(op_key, related_to, args=context)
|
||||||
|
|
||||||
|
# Start to record or give the unit operation in argument to let the
|
||||||
|
# developper start the record itself
|
||||||
if auto:
|
if auto:
|
||||||
uo.start()
|
uo.start()
|
||||||
try:
|
try:
|
||||||
|
@ -173,12 +213,22 @@ def is_unit_operation(entities='app,domain,service,user', exclude='auth,password
|
||||||
args = (uo,) + args
|
args = (uo,) + args
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
finally:
|
finally:
|
||||||
|
# Close the unit operation if it hasn't been closed before
|
||||||
uo.close(exc_info()[0])
|
uo.close(exc_info()[0])
|
||||||
return result
|
return result
|
||||||
return func_wrapper
|
return func_wrapper
|
||||||
return decorate
|
return decorate
|
||||||
|
|
||||||
class UnitOperation(object):
|
class UnitOperation(object):
|
||||||
|
"""
|
||||||
|
Instances of this class represents unit operation the yunohost admin as done.
|
||||||
|
|
||||||
|
Each time an action of the yunohost cli/api change the system, one or several
|
||||||
|
unit operations should be registered.
|
||||||
|
|
||||||
|
This class record logs and some metadata like context or start time/end time.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, operation, related_to=None, **kwargs):
|
def __init__(self, operation, related_to=None, **kwargs):
|
||||||
# TODO add a way to not save password on app installation
|
# TODO add a way to not save password on app installation
|
||||||
self.operation = operation
|
self.operation = operation
|
||||||
|
@ -194,12 +244,21 @@ class UnitOperation(object):
|
||||||
os.makedirs(self.path)
|
os.makedirs(self.path)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""
|
||||||
|
Start to record logs that change the system
|
||||||
|
Until this start method is run, no unit operation will be registered.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.started_at is None:
|
if self.started_at is None:
|
||||||
self.started_at = datetime.now()
|
self.started_at = datetime.now()
|
||||||
self.flush()
|
self.flush()
|
||||||
self._register_log()
|
self._register_log()
|
||||||
|
|
||||||
def _register_log(self):
|
def _register_log(self):
|
||||||
|
"""
|
||||||
|
Register log with a handler connected on log system
|
||||||
|
"""
|
||||||
|
|
||||||
# TODO add a way to not save password on app installation
|
# TODO add a way to not save password on app installation
|
||||||
filename = os.path.join(self.path, self.name + LOG_FILE_EXT)
|
filename = os.path.join(self.path, self.name + LOG_FILE_EXT)
|
||||||
self.file_handler = FileHandler(filename)
|
self.file_handler = FileHandler(filename)
|
||||||
|
@ -210,12 +269,20 @@ class UnitOperation(object):
|
||||||
self.logger.addHandler(self.file_handler)
|
self.logger.addHandler(self.file_handler)
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
|
"""
|
||||||
|
Write or rewrite the metadata file with all metadata known
|
||||||
|
"""
|
||||||
|
|
||||||
filename = os.path.join(self.path, self.name + METADATA_FILE_EXT)
|
filename = os.path.join(self.path, self.name + METADATA_FILE_EXT)
|
||||||
with open(filename, 'w') as outfile:
|
with open(filename, 'w') as outfile:
|
||||||
yaml.safe_dump(self.metadata, outfile, default_flow_style=False)
|
yaml.safe_dump(self.metadata, outfile, default_flow_style=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""
|
||||||
|
Name of the operation
|
||||||
|
This name is used as filename, so don't use space
|
||||||
|
"""
|
||||||
name = [self.started_at.strftime("%Y%m%d-%H%M%S")]
|
name = [self.started_at.strftime("%Y%m%d-%H%M%S")]
|
||||||
name += [self.operation]
|
name += [self.operation]
|
||||||
if self.related_to:
|
if self.related_to:
|
||||||
|
@ -224,6 +291,10 @@ class UnitOperation(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
|
"""
|
||||||
|
Dictionnary of all metadata collected
|
||||||
|
"""
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'started_at': self.started_at,
|
'started_at': self.started_at,
|
||||||
'operation': self.operation,
|
'operation': self.operation,
|
||||||
|
@ -240,12 +311,21 @@ class UnitOperation(object):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def success(self):
|
def success(self):
|
||||||
|
"""
|
||||||
|
Declare the success end of the unit operation
|
||||||
|
"""
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def error(self, error):
|
def error(self, error):
|
||||||
|
"""
|
||||||
|
Declare the failure of the unit operation
|
||||||
|
"""
|
||||||
self.close(error)
|
self.close(error)
|
||||||
|
|
||||||
def close(self, error=None):
|
def close(self, error=None):
|
||||||
|
"""
|
||||||
|
Close properly the unit operation
|
||||||
|
"""
|
||||||
if self.ended_at is not None or self.started_at is None:
|
if self.ended_at is not None or self.started_at is None:
|
||||||
return
|
return
|
||||||
self.ended_at = datetime.now()
|
self.ended_at = datetime.now()
|
||||||
|
@ -256,5 +336,10 @@ class UnitOperation(object):
|
||||||
self.flush()
|
self.flush()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
"""
|
||||||
|
Try to close the unit operation, if it's missing.
|
||||||
|
The missing of the message below could help to see an electrical
|
||||||
|
shortage.
|
||||||
|
"""
|
||||||
self.error(m18n.n('log_operation_unit_unclosed_properly'))
|
self.error(m18n.n('log_operation_unit_unclosed_properly'))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue