blob: d129b4b6757b4ef64a4a3d069a0dd7487c6e7e18 [file] [log] [blame]
quilesja049b7c2019-10-28 18:08:00 +01001##
2# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
3# This file is part of OSM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# For those usages not covered by the Apache License, Version 2.0 please
20# contact with: nfvlabs@tid.es
21##
22
23
quilesja049b7c2019-10-28 18:08:00 +010024import asyncio
quilesja049b7c2019-10-28 18:08:00 +010025import datetime
beierlmf52cb7c2020-04-21 16:36:35 -040026import inspect
27import logging
28import threading # only for logging purposes (not for using threads)
29import time
quilesja049b7c2019-10-28 18:08:00 +010030
31
32class Loggable:
beierlmf52cb7c2020-04-21 16:36:35 -040033 def __init__(self, log, log_to_console: bool = False, prefix: str = ""):
beierlmf52cb7c2020-04-21 16:36:35 -040034 self._last_log_time = None # used for time increment in logging
quilesja049b7c2019-10-28 18:08:00 +010035 self._log_to_console = log_to_console
36 self._prefix = prefix
37 if log is not None:
38 self.log = log
39 else:
40 self.log = logging.getLogger(__name__)
41
42 def debug(self, msg: str):
beierlmf52cb7c2020-04-21 16:36:35 -040043 self._log_msg(log_level="DEBUG", msg=msg)
quilesja049b7c2019-10-28 18:08:00 +010044
45 def info(self, msg: str):
beierlmf52cb7c2020-04-21 16:36:35 -040046 self._log_msg(log_level="INFO", msg=msg)
quilesja049b7c2019-10-28 18:08:00 +010047
48 def warning(self, msg: str):
beierlmf52cb7c2020-04-21 16:36:35 -040049 self._log_msg(log_level="WARNING", msg=msg)
quilesja049b7c2019-10-28 18:08:00 +010050
51 def error(self, msg: str):
beierlmf52cb7c2020-04-21 16:36:35 -040052 self._log_msg(log_level="ERROR", msg=msg)
quilesja049b7c2019-10-28 18:08:00 +010053
54 def critical(self, msg: str):
beierlmf52cb7c2020-04-21 16:36:35 -040055 self._log_msg(log_level="CRITICAL", msg=msg)
quilesja049b7c2019-10-28 18:08:00 +010056
beierlmf52cb7c2020-04-21 16:36:35 -040057 ####################################################################################
quilesja049b7c2019-10-28 18:08:00 +010058
59 def _log_msg(self, log_level: str, msg: str):
60 """Generic log method"""
61 msg = self._format_log(
62 log_level=log_level,
63 msg=msg,
64 obj=self,
65 level=3,
66 include_path=False,
67 include_thread=False,
beierlmf52cb7c2020-04-21 16:36:35 -040068 include_coroutine=True,
quilesja049b7c2019-10-28 18:08:00 +010069 )
70 if self._log_to_console:
71 print(msg)
72 else:
73 if self.log is not None:
beierlmf52cb7c2020-04-21 16:36:35 -040074 if log_level == "DEBUG":
quilesja049b7c2019-10-28 18:08:00 +010075 self.log.debug(msg)
beierlmf52cb7c2020-04-21 16:36:35 -040076 elif log_level == "INFO":
quilesja049b7c2019-10-28 18:08:00 +010077 self.log.info(msg)
beierlmf52cb7c2020-04-21 16:36:35 -040078 elif log_level == "WARNING":
quilesja049b7c2019-10-28 18:08:00 +010079 self.log.warning(msg)
beierlmf52cb7c2020-04-21 16:36:35 -040080 elif log_level == "ERROR":
quilesja049b7c2019-10-28 18:08:00 +010081 self.log.error(msg)
beierlmf52cb7c2020-04-21 16:36:35 -040082 elif log_level == "CRITICAL":
quilesja049b7c2019-10-28 18:08:00 +010083 self.log.critical(msg)
84
85 def _format_log(
beierlmf52cb7c2020-04-21 16:36:35 -040086 self,
87 log_level: str,
88 msg: str = "",
89 obj: object = None,
90 level: int = None,
91 include_path: bool = False,
92 include_thread: bool = False,
93 include_coroutine: bool = True,
quilesja049b7c2019-10-28 18:08:00 +010094 ) -> str:
quilesja049b7c2019-10-28 18:08:00 +010095 # time increment from last log
96 now = time.perf_counter()
97 if self._last_log_time is None:
beierlmf52cb7c2020-04-21 16:36:35 -040098 time_str = " (+0.000)"
quilesja049b7c2019-10-28 18:08:00 +010099 else:
100 diff = round(now - self._last_log_time, 3)
beierlmf52cb7c2020-04-21 16:36:35 -0400101 time_str = " (+{})".format(diff)
quilesja049b7c2019-10-28 18:08:00 +0100102 self._last_log_time = now
103
104 if level is None:
105 level = 1
106
107 # stack info
108 fi = inspect.stack()[level]
109 filename = fi.filename
110 func = fi.function
111 lineno = fi.lineno
112 # filename without path
113 if not include_path:
beierlmf52cb7c2020-04-21 16:36:35 -0400114 i = filename.rfind("/")
quilesja049b7c2019-10-28 18:08:00 +0100115 if i > 0:
David Garcia4ae527e2021-07-26 16:04:59 +0200116 filename = filename[i + 1 :]
quilesja049b7c2019-10-28 18:08:00 +0100117
118 # datetime
beierlmf52cb7c2020-04-21 16:36:35 -0400119 dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
quilesja049b7c2019-10-28 18:08:00 +0100120 dt = dt + time_str
quilesjac4e0de2019-11-27 16:12:02 +0000121 # dt = time_str # logger already shows datetime
quilesja049b7c2019-10-28 18:08:00 +0100122
123 # current thread
124 if include_thread:
beierlmf52cb7c2020-04-21 16:36:35 -0400125 thread_name = "th:{}".format(threading.current_thread().getName())
quilesja049b7c2019-10-28 18:08:00 +0100126 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400127 thread_name = ""
quilesja049b7c2019-10-28 18:08:00 +0100128
129 # current coroutine
130
beierlmf52cb7c2020-04-21 16:36:35 -0400131 coroutine_id = ""
quilesja049b7c2019-10-28 18:08:00 +0100132 if include_coroutine:
133 try:
Guillermo Calvino474fd952023-04-28 11:51:43 +0200134 if asyncio.current_task() is not None:
beierlmf52cb7c2020-04-21 16:36:35 -0400135
quilesja049b7c2019-10-28 18:08:00 +0100136 def print_cor_name(c):
137 import inspect
beierlmf52cb7c2020-04-21 16:36:35 -0400138
quilesja049b7c2019-10-28 18:08:00 +0100139 try:
140 for m in inspect.getmembers(c):
beierlmf52cb7c2020-04-21 16:36:35 -0400141 if m[0] == "__name__":
quilesja049b7c2019-10-28 18:08:00 +0100142 return m[1]
143 except Exception:
144 pass
beierlmf52cb7c2020-04-21 16:36:35 -0400145
Guillermo Calvino474fd952023-04-28 11:51:43 +0200146 coro = asyncio.current_task()._coro
beierlmf52cb7c2020-04-21 16:36:35 -0400147 coroutine_id = "coro-{} {}()".format(
148 hex(id(coro))[2:], print_cor_name(coro)
149 )
quilesja049b7c2019-10-28 18:08:00 +0100150 except Exception:
beierlmf52cb7c2020-04-21 16:36:35 -0400151 coroutine_id = ""
quilesja049b7c2019-10-28 18:08:00 +0100152
153 # classname
154 if obj is not None:
155 obj_type = obj.__class__.__name__ # type: str
beierlmf52cb7c2020-04-21 16:36:35 -0400156 log_msg = "{} {} {} {} {}::{}.{}():{}\n{}".format(
157 self._prefix,
158 dt,
159 thread_name,
160 coroutine_id,
161 filename,
162 obj_type,
163 func,
164 lineno,
165 str(msg),
166 )
quilesja049b7c2019-10-28 18:08:00 +0100167 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400168 log_msg = "{} {} {} {} {}::{}():{}\n{}".format(
169 self._prefix,
170 dt,
171 thread_name,
172 coroutine_id,
173 filename,
174 func,
175 lineno,
176 str(msg),
177 )
quilesja049b7c2019-10-28 18:08:00 +0100178
179 return log_msg