X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FN2VC.git;a=blobdiff_plain;f=n2vc%2Floggable.py;fp=n2vc%2Floggable.py;h=40efa2417659b6917d0db0176d62d048ae2b07e0;hp=0000000000000000000000000000000000000000;hb=a049b7ce5d1606440447a88a98dd70548a1a0c74;hpb=630a806b8f85c5d6c2eeadccfbae536d7ea21e2b diff --git a/n2vc/loggable.py b/n2vc/loggable.py new file mode 100644 index 0000000..40efa24 --- /dev/null +++ b/n2vc/loggable.py @@ -0,0 +1,167 @@ +## +# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U. +# This file is part of OSM +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## + + +import logging +import asyncio +import time +import inspect +import datetime +import threading # only for logging purposes (not for using threads) + + +class Loggable: + + def __init__( + self, + log, + log_to_console: bool = False, + prefix: str = '' + ): + + self._last_log_time = None # used for time increment in logging + self._log_to_console = log_to_console + self._prefix = prefix + if log is not None: + self.log = log + else: + self.log = logging.getLogger(__name__) + + def debug(self, msg: str): + self._log_msg(log_level='DEBUG', msg=msg) + + def info(self, msg: str): + self._log_msg(log_level='INFO', msg=msg) + + def warning(self, msg: str): + self._log_msg(log_level='WARNING', msg=msg) + + def error(self, msg: str): + self._log_msg(log_level='ERROR', msg=msg) + + def critical(self, msg: str): + self._log_msg(log_level='CRITICAL', msg=msg) + + ################################################################################################## + + def _log_msg(self, log_level: str, msg: str): + """Generic log method""" + msg = self._format_log( + log_level=log_level, + msg=msg, + obj=self, + level=3, + include_path=False, + include_thread=False, + include_coroutine=True + ) + if self._log_to_console: + print(msg) + else: + if self.log is not None: + if log_level == 'DEBUG': + self.log.debug(msg) + elif log_level == 'INFO': + self.log.info(msg) + elif log_level == 'WARNING': + self.log.warning(msg) + elif log_level == 'ERROR': + self.log.error(msg) + elif log_level == 'CRITICAL': + self.log.critical(msg) + + def _format_log( + self, + log_level: str, + msg: str = '', + obj: object = None, + level: int = None, + include_path: bool = False, + include_thread: bool = False, + include_coroutine: bool = True + ) -> str: + + # time increment from last log + now = time.perf_counter() + if self._last_log_time is None: + time_str = ' (+0.000)' + else: + diff = round(now - self._last_log_time, 3) + time_str = ' (+{})'.format(diff) + self._last_log_time = now + + if level is None: + level = 1 + + # stack info + fi = inspect.stack()[level] + filename = fi.filename + func = fi.function + lineno = fi.lineno + # filename without path + if not include_path: + i = filename.rfind('/') + if i > 0: + filename = filename[i+1:] + + # datetime + dt = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + dt = dt + time_str + dt = time_str # logger already shows datetime + + # current thread + if include_thread: + thread_name = 'th:{}'.format(threading.current_thread().getName()) + else: + thread_name = '' + + # current coroutine + + coroutine_id = '' + if include_coroutine: + try: + if asyncio.Task.current_task() is not None: + def print_cor_name(c): + import inspect + try: + for m in inspect.getmembers(c): + if m[0] == '__name__': + return m[1] + except Exception: + pass + coro = asyncio.Task.current_task()._coro + coroutine_id = 'coro-{} {}()'.format(hex(id(coro))[2:], print_cor_name(coro)) + except Exception: + coroutine_id = '' + + # classname + if obj is not None: + obj_type = obj.__class__.__name__ # type: str + log_msg = \ + '{} {} {} {} {}::{}.{}():{}\n{}'\ + .format(self._prefix, dt, thread_name, coroutine_id, filename, obj_type, func, lineno, str(msg)) + else: + log_msg = \ + '{} {} {} {} {}::{}():{}\n{}'\ + .format(self._prefix, dt, thread_name, coroutine_id, filename, func, lineno, str(msg)) + + return log_msg