Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/log.py: 57%

90 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# sqlalchemy/log.py 

2# Copyright (C) 2006-2022 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# Includes alterations by Vinay Sajip vinay_sajip@yahoo.co.uk 

5# 

6# This module is part of SQLAlchemy and is released under 

7# the MIT License: https://www.opensource.org/licenses/mit-license.php 

8 

9"""Logging control and utilities. 

10 

11Control of logging for SA can be performed from the regular python logging 

12module. The regular dotted module namespace is used, starting at 

13'sqlalchemy'. For class-level logging, the class name is appended. 

14 

15The "echo" keyword parameter, available on SQLA :class:`_engine.Engine` 

16and :class:`_pool.Pool` objects, corresponds to a logger specific to that 

17instance only. 

18 

19""" 

20 

21import logging 

22import sys 

23 

24from .util import py311 

25from .util import py38 

26 

27if py38: 

28 STACKLEVEL = True 

29 # needed as of py3.11.0b1 

30 # #8019 

31 STACKLEVEL_OFFSET = 2 if py311 else 1 

32else: 

33 STACKLEVEL = False 

34 STACKLEVEL_OFFSET = 0 

35 

36# set initial level to WARN. This so that 

37# log statements don't occur in the absence of explicit 

38# logging being enabled for 'sqlalchemy'. 

39rootlogger = logging.getLogger("sqlalchemy") 

40if rootlogger.level == logging.NOTSET: 

41 rootlogger.setLevel(logging.WARN) 

42 

43 

44def _add_default_handler(logger): 

45 handler = logging.StreamHandler(sys.stdout) 

46 handler.setFormatter( 

47 logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s") 

48 ) 

49 logger.addHandler(handler) 

50 

51 

52_logged_classes = set() 

53 

54 

55def _qual_logger_name_for_cls(cls): 

56 return ( 

57 getattr(cls, "_sqla_logger_namespace", None) 

58 or cls.__module__ + "." + cls.__name__ 

59 ) 

60 

61 

62def class_logger(cls): 

63 logger = logging.getLogger(_qual_logger_name_for_cls(cls)) 

64 cls._should_log_debug = lambda self: logger.isEnabledFor(logging.DEBUG) 

65 cls._should_log_info = lambda self: logger.isEnabledFor(logging.INFO) 

66 cls.logger = logger 

67 _logged_classes.add(cls) 

68 return cls 

69 

70 

71class Identified(object): 

72 logging_name = None 

73 

74 def _should_log_debug(self): 

75 return self.logger.isEnabledFor(logging.DEBUG) 

76 

77 def _should_log_info(self): 

78 return self.logger.isEnabledFor(logging.INFO) 

79 

80 

81class InstanceLogger(object): 

82 """A logger adapter (wrapper) for :class:`.Identified` subclasses. 

83 

84 This allows multiple instances (e.g. Engine or Pool instances) 

85 to share a logger, but have its verbosity controlled on a 

86 per-instance basis. 

87 

88 The basic functionality is to return a logging level 

89 which is based on an instance's echo setting. 

90 

91 Default implementation is: 

92 

93 'debug' -> logging.DEBUG 

94 True -> logging.INFO 

95 False -> Effective level of underlying logger ( 

96 logging.WARNING by default) 

97 None -> same as False 

98 """ 

99 

100 # Map echo settings to logger levels 

101 _echo_map = { 

102 None: logging.NOTSET, 

103 False: logging.NOTSET, 

104 True: logging.INFO, 

105 "debug": logging.DEBUG, 

106 } 

107 

108 def __init__(self, echo, name): 

109 self.echo = echo 

110 self.logger = logging.getLogger(name) 

111 

112 # if echo flag is enabled and no handlers, 

113 # add a handler to the list 

114 if self._echo_map[echo] <= logging.INFO and not self.logger.handlers: 

115 _add_default_handler(self.logger) 

116 

117 # 

118 # Boilerplate convenience methods 

119 # 

120 def debug(self, msg, *args, **kwargs): 

121 """Delegate a debug call to the underlying logger.""" 

122 

123 self.log(logging.DEBUG, msg, *args, **kwargs) 

124 

125 def info(self, msg, *args, **kwargs): 

126 """Delegate an info call to the underlying logger.""" 

127 

128 self.log(logging.INFO, msg, *args, **kwargs) 

129 

130 def warning(self, msg, *args, **kwargs): 

131 """Delegate a warning call to the underlying logger.""" 

132 

133 self.log(logging.WARNING, msg, *args, **kwargs) 

134 

135 warn = warning 

136 

137 def error(self, msg, *args, **kwargs): 

138 """ 

139 Delegate an error call to the underlying logger. 

140 """ 

141 self.log(logging.ERROR, msg, *args, **kwargs) 

142 

143 def exception(self, msg, *args, **kwargs): 

144 """Delegate an exception call to the underlying logger.""" 

145 

146 kwargs["exc_info"] = 1 

147 self.log(logging.ERROR, msg, *args, **kwargs) 

148 

149 def critical(self, msg, *args, **kwargs): 

150 """Delegate a critical call to the underlying logger.""" 

151 

152 self.log(logging.CRITICAL, msg, *args, **kwargs) 

153 

154 def log(self, level, msg, *args, **kwargs): 

155 """Delegate a log call to the underlying logger. 

156 

157 The level here is determined by the echo 

158 flag as well as that of the underlying logger, and 

159 logger._log() is called directly. 

160 

161 """ 

162 

163 # inline the logic from isEnabledFor(), 

164 # getEffectiveLevel(), to avoid overhead. 

165 

166 if self.logger.manager.disable >= level: 

167 return 

168 

169 selected_level = self._echo_map[self.echo] 

170 if selected_level == logging.NOTSET: 

171 selected_level = self.logger.getEffectiveLevel() 

172 

173 if level >= selected_level: 

174 if STACKLEVEL: 

175 kwargs["stacklevel"] = ( 

176 kwargs.get("stacklevel", 1) + STACKLEVEL_OFFSET 

177 ) 

178 

179 self.logger._log(level, msg, args, **kwargs) 

180 

181 def isEnabledFor(self, level): 

182 """Is this logger enabled for level 'level'?""" 

183 

184 if self.logger.manager.disable >= level: 

185 return False 

186 return level >= self.getEffectiveLevel() 

187 

188 def getEffectiveLevel(self): 

189 """What's the effective level for this logger?""" 

190 

191 level = self._echo_map[self.echo] 

192 if level == logging.NOTSET: 

193 level = self.logger.getEffectiveLevel() 

194 return level 

195 

196 

197def instance_logger(instance, echoflag=None): 

198 """create a logger for an instance that implements :class:`.Identified`.""" 

199 

200 if instance.logging_name: 

201 name = "%s.%s" % ( 

202 _qual_logger_name_for_cls(instance.__class__), 

203 instance.logging_name, 

204 ) 

205 else: 

206 name = _qual_logger_name_for_cls(instance.__class__) 

207 

208 instance._echo = echoflag 

209 

210 if echoflag in (False, None): 

211 # if no echo setting or False, return a Logger directly, 

212 # avoiding overhead of filtering 

213 logger = logging.getLogger(name) 

214 else: 

215 # if a specified echo flag, return an EchoLogger, 

216 # which checks the flag, overrides normal log 

217 # levels by calling logger._log() 

218 logger = InstanceLogger(echoflag, name) 

219 

220 instance.logger = logger 

221 

222 

223class echo_property(object): 

224 __doc__ = """\ 

225 When ``True``, enable log output for this element. 

226 

227 This has the effect of setting the Python logging level for the namespace 

228 of this element's class and object reference. A value of boolean ``True`` 

229 indicates that the loglevel ``logging.INFO`` will be set for the logger, 

230 whereas the string value ``debug`` will set the loglevel to 

231 ``logging.DEBUG``. 

232 """ 

233 

234 def __get__(self, instance, owner): 

235 if instance is None: 

236 return self 

237 else: 

238 return instance._echo 

239 

240 def __set__(self, instance, value): 

241 instance_logger(instance, echoflag=value)