Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/local/bin/python 

2# encoding: utf-8 

3""" 

4*Logger setup for python projects* 

5 

6:Author: 

7 David Young 

8""" 

9from builtins import object 

10import os 

11import sys 

12import logging 

13import coloredlogs 

14from logging import handlers 

15 

16 

17def console_logger( 

18 level="WARNING" 

19): 

20 """ 

21 *Setup and return a console logger* 

22 

23 **Key Arguments** 

24 

25 - ``level`` -- the level of logging required 

26 

27 

28 **Return** 

29 

30 - ``logger`` -- the console logger 

31 

32 

33 **Usage** 

34 

35 ```python 

36 from fundamentals import logs 

37 log = logs.console_logger( 

38 level="DEBUG" 

39 ) 

40 log.debug("Testing console logger") 

41 ``` 

42 

43 """ 

44 ## STANDARD LIB ## 

45 

46 import logging 

47 import logging.config 

48 import coloredlogs 

49 ## THIRD PARTY ## 

50 import yaml 

51 try: 

52 yaml.warnings({'YAMLLoadWarning': False}) 

53 except: 

54 pass 

55 ## LOCAL APPLICATION ## 

56 

57 # SETUP LOGGING 

58 loggerConfig = """ 

59 version: 1 

60 formatters: 

61 console_style: 

62 format: '* %(asctime)s - %(levelname)s: %(pathname)s:%(funcName)s:%(lineno)d > %(message)s' 

63 datefmt: '%H:%M:%S' 

64 handlers: 

65 console: 

66 class: logging.StreamHandler 

67 level: """ + level + """ 

68 formatter: console_style 

69 stream: ext://sys.stdout 

70 root: 

71 level: """ + level + """ 

72 handlers: [console]""" 

73 

74 logging.config.dictConfig(yaml.load(loggerConfig)) 

75 logger = logging.getLogger(__name__) 

76 

77 return logger 

78 

79 

80def setup_dryx_logging(yaml_file): 

81 """ 

82 *setup dryx style python logging* 

83 

84 **Key Arguments** 

85 

86 - ``level`` -- the level of logging required 

87 

88 

89 **Return** 

90 

91 - ``logger`` -- the console logger 

92 

93 

94 **Usage** 

95 

96 ```python 

97 from fundamentals import logs 

98 log = logs.setup_dryx_logging( 

99 yaml_file="/Users/Dave/.config/fundamentals/fundamentals.yaml" 

100 ) 

101 log.error("test error") 

102 ``` 

103 

104 Here is an example of the settings in the yaml file: 

105 

106 ```yaml 

107 version: 1 

108 

109 logging settings: 

110 formatters: 

111 file_style: 

112 format: '* %(asctime)s - %(name)s - %(levelname)s (%(pathname)s > %(funcName)s > %(lineno)d) - %(message)s ' 

113 datefmt: '%Y/%m/%d %H:%M:%S' 

114 console_style: 

115 format: '* %(asctime)s - %(levelname)s: %(pathname)s:%(funcName)s:%(lineno)d > %(message)s' 

116 datefmt: '%H:%M:%S' 

117 html_style: 

118 format: '<div id="row" class="%(levelname)s"><span class="date">%(asctime)s</span> <span class="label">file:</span><span class="filename">%(filename)s</span> <span class="label">method:</span><span class="funcName">%(funcName)s</span> <span class="label">line#:</span><span class="lineno">%(lineno)d</span> <span class="pathname">%(pathname)s</span> <div class="right"><span class="message">%(message)s</span><span class="levelname">%(levelname)s</span></div></div>' 

119 datefmt: '%Y-%m-%d <span class= "time">%H:%M <span class= "seconds">%Ss</span></span>' 

120 handlers: 

121 console: 

122 class: logging.StreamHandler 

123 level: DEBUG 

124 formatter: console_style 

125 stream: ext://sys.stdout 

126 file: 

127 class: logging.handlers.GroupWriteRotatingFileHandler 

128 level: WARNING 

129 formatter: file_style 

130 filename: /Users/Dave/.config/fundamentals/fundamentals.log 

131 mode: w+ 

132 maxBytes: 102400 

133 backupCount: 1 

134 root: 

135 level: WARNING 

136 handlers: [file,console] 

137 ``` 

138 

139 """ 

140 import logging 

141 import coloredlogs 

142 import logging.config 

143 import yaml 

144 try: 

145 from StringIO import StringIO 

146 except ImportError: 

147 from io import StringIO 

148 from os.path import expanduser 

149 

150 # IMPORT CUSTOM HANDLER THAT ALLOWS GROUP WRITING 

151 handlers.GroupWriteRotatingFileHandler = GroupWriteRotatingFileHandler 

152 

153 # GET CONTENT OF YAML FILE AND REPLACE ~ WITH HOME DIRECTORY PATH 

154 with open(yaml_file) as f: 

155 content = f.read() 

156 home = expanduser("~") 

157 content = content.replace("~/", home + "/") 

158 stream = StringIO(content) 

159 yamlContent = yaml.load(stream) 

160 stream.close() 

161 

162 # USE THE LOGGING SETTINGS SECTION OF THE SETTINGS DICTIONARY FILE IF THERE IS ONE 

163 # OTHERWISE ASSUME THE FILE CONTAINS ONLY LOGGING SETTINGS 

164 if "logging settings" in yamlContent: 

165 yamlContent = yamlContent["logging settings"] 

166 yamlContent["version"] = 1 

167 

168 if "handlers" in yamlContent and "file" in yamlContent["handlers"] and "filename" in yamlContent["handlers"]["file"]: 

169 loggingDir = os.path.dirname( 

170 yamlContent["handlers"]["file"]["filename"]) 

171 # Recursively create missing directories 

172 if not os.path.exists(loggingDir): 

173 os.makedirs(loggingDir) 

174 

175 if "root" in yamlContent and "level" in yamlContent["root"]: 

176 level = yamlContent["root"]["level"] 

177 

178 logging.config.dictConfig(yamlContent) 

179 # SET THE ROOT LOGGER 

180 logger = logging.getLogger(__name__) 

181 

182 logging.captureWarnings(True) 

183 

184 coloredlogs.DEFAULT_FIELD_STYLES = { 

185 'asctime': {'color': 'green', 'faint': True}, 

186 'levelname': {'color': 'white'}, 

187 'pathname': {'color': 'cyan', 'faint': True}, 

188 'funcName': {'color': 'magenta', }, 

189 'lineno': {'color': 'cyan', 'faint': True} 

190 } 

191 coloredlogs.DEFAULT_LEVEL_STYLES = { 

192 'debug': {'color': 'black', 'bright': True}, 

193 'info': {'color': 'white', 'bright': True}, 

194 'warning': {'color': 'yellow'}, 

195 'error': {'color': 'red'}, 

196 'critical': {'color': 'white', 'background': 'red'}} 

197 coloredlogs.install(level=level, logger=logger, fmt=yamlContent[ 

198 "formatters"]["console_style"]["format"], datefmt=yamlContent[ 

199 "formatters"]["console_style"]["datefmt"]) 

200 

201 return logger 

202 

203 

204class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler): 

205 """ 

206 *rotating file handler for logging* 

207 """ 

208 

209 def doRollover(self): 

210 """ 

211 *Override base class method to make the new log file group writable.* 

212 """ 

213 # Rotate the file first. 

214 handlers.RotatingFileHandler.doRollover(self) 

215 

216 # Add group write to the current permissions. 

217 currMode = os.stat(self.baseFilename).st_mode 

218 os.chmod(self.baseFilename, currMode | stat.S_IWGRP | 

219 stat.S_IRGRP | stat.S_IWOTH | stat.S_IROTH) 

220 

221 

222class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler): 

223 """ 

224 *rotating file handler for logging* 

225 """ 

226 

227 def _open(self): 

228 prevumask = os.umask(0) 

229 rtv = logging.handlers.RotatingFileHandler._open(self) 

230 os.umask(prevumask) 

231 return rtv 

232 

233 

234class emptyLogger(object): 

235 """ 

236 *A fake logger object so user can set ``log=False`` if required* 

237 

238 **Usage** 

239 

240 ```python 

241 if log == False: 

242 from fundamentals.logs import emptyLogger 

243 log = emptyLogger() 

244 ``` 

245 

246 """ 

247 

248 def info(self, argu): 

249 pass 

250 

251 def error(self, argu): 

252 pass 

253 

254 def debug(self, argu): 

255 pass 

256 

257 def critical(self, argu): 

258 pass 

259 

260 def warning(self, argu): 

261 pass