212 lines
7.3 KiB
Python
212 lines
7.3 KiB
Python
from typing import Dict, Any, Optional
|
|
from json import dumps, loads
|
|
from asyncpg.exceptions import UndefinedTableError
|
|
from datetime import datetime
|
|
from asyncio import Lock
|
|
|
|
from .context import Context
|
|
from .utils import _
|
|
from .config import get_config
|
|
|
|
database_lock = Lock()
|
|
|
|
|
|
LEVELS = ['Error', 'Info', 'Success', 'Started', 'Failure']
|
|
|
|
|
|
class Logger:
|
|
""" An object to manager log
|
|
"""
|
|
def __init__(self) -> None:
|
|
self.log_connection = None
|
|
|
|
async def get_connection(self,
|
|
risotto_context: Context,
|
|
):
|
|
if not self.log_connection:
|
|
self.log_connection = await risotto_context.pool.acquire()
|
|
await self.log_connection.set_type_codec(
|
|
'json',
|
|
encoder=dumps,
|
|
decoder=loads,
|
|
schema='pg_catalog'
|
|
)
|
|
return self.log_connection
|
|
|
|
async def insert(self,
|
|
msg: str,
|
|
risotto_context: Context,
|
|
level: str,
|
|
kwargs: Any=None,
|
|
start: bool=False,
|
|
) -> None:
|
|
uri = self._get_last_uri(risotto_context)
|
|
uris = " ".join(risotto_context.paths)
|
|
insert = 'INSERT INTO RisottoLog(Msg, URI, URIS, UserLogin, Status'
|
|
values = 'VALUES($1,$2,$3,$4,$5'
|
|
args = [msg, uri, uris, risotto_context.username, LEVELS.index(level)]
|
|
if kwargs:
|
|
insert += ', Kwargs'
|
|
values += ',$6'
|
|
args.append(dumps(kwargs))
|
|
context_id = risotto_context.context_id
|
|
if context_id is not None:
|
|
insert += ', ContextId'
|
|
if kwargs:
|
|
values += ',$7'
|
|
else:
|
|
values += ',$6'
|
|
args.append(context_id)
|
|
|
|
sql = insert + ') ' + values + ') RETURNING LogId'
|
|
try:
|
|
async with database_lock:
|
|
connection = await self.get_connection(risotto_context)
|
|
log_id = await connection.fetchval(sql, *args)
|
|
if context_id is None and start:
|
|
risotto_context.context_id = log_id
|
|
if start:
|
|
risotto_context.start_id = log_id
|
|
except UndefinedTableError as err:
|
|
raise Exception(_(f'cannot access to database ({err}), was the database really created?'))
|
|
|
|
def _get_last_uri(self,
|
|
risotto_context: Context,
|
|
) -> str:
|
|
if risotto_context.paths:
|
|
return risotto_context.paths[-1]
|
|
return ''
|
|
|
|
def _get_message_paths(self,
|
|
risotto_context: Context,
|
|
) -> str:
|
|
if not risotto_context.paths:
|
|
return ''
|
|
paths = risotto_context.paths
|
|
if risotto_context.type:
|
|
paths_msg = f' {risotto_context.type} '
|
|
else:
|
|
paths_msg = ' '
|
|
if len(paths) == 1:
|
|
paths_msg += f'message: {paths[0]}'
|
|
else:
|
|
paths_msg += f'sub-messages: '
|
|
paths_msg += ' > '.join(paths)
|
|
return paths_msg
|
|
|
|
async def error_msg(self,
|
|
risotto_context: Context,
|
|
arguments,
|
|
error: str,
|
|
msg: str='',
|
|
):
|
|
""" send message when an error append
|
|
"""
|
|
paths_msg = self._get_message_paths(risotto_context)
|
|
print(_(f'{risotto_context.username}: ERROR: {error} ({paths_msg} with arguments "{arguments}": {msg})'))
|
|
await self.insert(msg,
|
|
risotto_context,
|
|
'Error',
|
|
arguments,
|
|
)
|
|
|
|
async def info_msg(self,
|
|
risotto_context: Context,
|
|
arguments: Dict,
|
|
msg: str='',
|
|
) -> None:
|
|
""" send message with common information
|
|
"""
|
|
paths_msg = self._get_message_paths(risotto_context)
|
|
if get_config()['global']['debug']:
|
|
print(_(f'{risotto_context.username}: INFO:{paths_msg}: {msg}'))
|
|
await self.insert(msg,
|
|
risotto_context,
|
|
'Info',
|
|
arguments,
|
|
)
|
|
|
|
async def start(self,
|
|
risotto_context: Context,
|
|
arguments: dict,
|
|
msg: str,
|
|
) -> None:
|
|
paths_msg = self._get_message_paths(risotto_context)
|
|
if get_config()['global']['debug']:
|
|
if risotto_context.context_id != None:
|
|
context = f'({risotto_context.context_id})'
|
|
else:
|
|
context = ''
|
|
print(_(f'{risotto_context.username}: START{context}:{paths_msg}: {msg}'))
|
|
await self.insert(msg,
|
|
risotto_context,
|
|
'Started',
|
|
arguments,
|
|
start=True,
|
|
)
|
|
|
|
async def success(self,
|
|
risotto_context: Context,
|
|
returns: Optional[dict]=None,
|
|
) -> None:
|
|
if get_config()['global']['debug']:
|
|
paths_msg = self._get_message_paths(risotto_context)
|
|
print(_(f'{risotto_context.username}: SUCCESS({risotto_context.context_id}):{paths_msg}'))
|
|
sql = """UPDATE RisottoLog
|
|
SET StopDate = $2,
|
|
Status = $3
|
|
"""
|
|
args = [datetime.now(), LEVELS.index('Success')]
|
|
if returns:
|
|
sql += """, Returns = $4
|
|
"""
|
|
args.append(dumps(returns))
|
|
sql += """WHERE LogId = $1
|
|
"""
|
|
async with database_lock:
|
|
connection = await self.get_connection(risotto_context)
|
|
await connection.execute(sql,
|
|
risotto_context.start_id,
|
|
*args,
|
|
)
|
|
|
|
async def failed(self,
|
|
risotto_context: Context,
|
|
err: str,
|
|
) -> None:
|
|
if get_config()['global']['debug']:
|
|
paths_msg = self._get_message_paths(risotto_context)
|
|
if risotto_context.context_id != None:
|
|
context = f'({risotto_context.context_id})'
|
|
else:
|
|
context = ''
|
|
print(_(f'{risotto_context.username}: FAILED({risotto_context.context_id}):{paths_msg}: {err}'))
|
|
sql = """UPDATE RisottoLog
|
|
SET StopDate = $2,
|
|
Status = $4,
|
|
Msg = $3
|
|
WHERE LogId = $1
|
|
"""
|
|
async with database_lock:
|
|
connection = await self.get_connection(risotto_context)
|
|
await connection.execute(sql,
|
|
risotto_context.start_id,
|
|
datetime.now(),
|
|
err,
|
|
LEVELS.index('Failure'),
|
|
)
|
|
|
|
async def info(self,
|
|
risotto_context,
|
|
msg,
|
|
):
|
|
if get_config()['global']['debug']:
|
|
print(msg)
|
|
await self.insert(msg,
|
|
risotto_context,
|
|
'Info',
|
|
)
|
|
|
|
|
|
log = Logger()
|