Coverage for n2vc/loggable.py: 26%

82 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2024-06-29 09:03 +0000

1## 

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 

24import asyncio 

25import datetime 

26import inspect 

27import logging 

28import threading # only for logging purposes (not for using threads) 

29import time 

30 

31 

32class Loggable: 

33 def __init__(self, log, log_to_console: bool = False, prefix: str = ""): 

34 self._last_log_time = None # used for time increment in logging 

35 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): 

43 self._log_msg(log_level="DEBUG", msg=msg) 

44 

45 def info(self, msg: str): 

46 self._log_msg(log_level="INFO", msg=msg) 

47 

48 def warning(self, msg: str): 

49 self._log_msg(log_level="WARNING", msg=msg) 

50 

51 def error(self, msg: str): 

52 self._log_msg(log_level="ERROR", msg=msg) 

53 

54 def critical(self, msg: str): 

55 self._log_msg(log_level="CRITICAL", msg=msg) 

56 

57 #################################################################################### 

58 

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, 

68 include_coroutine=True, 

69 ) 

70 if self._log_to_console: 

71 print(msg) 

72 else: 

73 if self.log is not None: 

74 if log_level == "DEBUG": 

75 self.log.debug(msg) 

76 elif log_level == "INFO": 

77 self.log.info(msg) 

78 elif log_level == "WARNING": 

79 self.log.warning(msg) 

80 elif log_level == "ERROR": 

81 self.log.error(msg) 

82 elif log_level == "CRITICAL": 

83 self.log.critical(msg) 

84 

85 def _format_log( 

86 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, 

94 ) -> str: 

95 # time increment from last log 

96 now = time.perf_counter() 

97 if self._last_log_time is None: 

98 time_str = " (+0.000)" 

99 else: 

100 diff = round(now - self._last_log_time, 3) 

101 time_str = " (+{})".format(diff) 

102 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: 

114 i = filename.rfind("/") 

115 if i > 0: 

116 filename = filename[i + 1 :] 

117 

118 # datetime 

119 dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") 

120 dt = dt + time_str 

121 # dt = time_str # logger already shows datetime 

122 

123 # current thread 

124 if include_thread: 

125 thread_name = "th:{}".format(threading.current_thread().getName()) 

126 else: 

127 thread_name = "" 

128 

129 # current coroutine 

130 

131 coroutine_id = "" 

132 if include_coroutine: 

133 try: 

134 if asyncio.current_task() is not None: 

135 

136 def print_cor_name(c): 

137 import inspect 

138 

139 try: 

140 for m in inspect.getmembers(c): 

141 if m[0] == "__name__": 

142 return m[1] 

143 except Exception: 

144 pass 

145 

146 coro = asyncio.current_task()._coro 

147 coroutine_id = "coro-{} {}()".format( 

148 hex(id(coro))[2:], print_cor_name(coro) 

149 ) 

150 except Exception: 

151 coroutine_id = "" 

152 

153 # classname 

154 if obj is not None: 

155 obj_type = obj.__class__.__name__ # type: str 

156 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 ) 

167 else: 

168 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 ) 

178 

179 return log_msg