Coverage for fundamentals/logs.py : 47%

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*
6:Author:
7 David Young
8"""
9from builtins import object
10import os
11import sys
12import logging
13import coloredlogs
14from logging import handlers
17def console_logger(
18 level="WARNING"
19):
20 """
21 *Setup and return a console logger*
23 **Key Arguments**
25 - ``level`` -- the level of logging required
28 **Return**
30 - ``logger`` -- the console logger
33 **Usage**
35 ```python
36 from fundamentals import logs
37 log = logs.console_logger(
38 level="DEBUG"
39 )
40 log.debug("Testing console logger")
41 ```
43 """
44 ## STANDARD LIB ##
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 ##
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]"""
74 logging.config.dictConfig(yaml.load(loggerConfig))
75 logger = logging.getLogger(__name__)
77 return logger
80def setup_dryx_logging(yaml_file):
81 """
82 *setup dryx style python logging*
84 **Key Arguments**
86 - ``level`` -- the level of logging required
89 **Return**
91 - ``logger`` -- the console logger
94 **Usage**
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 ```
104 Here is an example of the settings in the yaml file:
106 ```yaml
107 version: 1
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 ```
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
150 # IMPORT CUSTOM HANDLER THAT ALLOWS GROUP WRITING
151 handlers.GroupWriteRotatingFileHandler = GroupWriteRotatingFileHandler
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()
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
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)
175 if "root" in yamlContent and "level" in yamlContent["root"]:
176 level = yamlContent["root"]["level"]
178 logging.config.dictConfig(yamlContent)
179 # SET THE ROOT LOGGER
180 logger = logging.getLogger(__name__)
182 logging.captureWarnings(True)
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"])
201 return logger
204class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
205 """
206 *rotating file handler for logging*
207 """
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)
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)
222class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
223 """
224 *rotating file handler for logging*
225 """
227 def _open(self):
228 prevumask = os.umask(0)
229 rtv = logging.handlers.RotatingFileHandler._open(self)
230 os.umask(prevumask)
231 return rtv
234class emptyLogger(object):
235 """
236 *A fake logger object so user can set ``log=False`` if required*
238 **Usage**
240 ```python
241 if log == False:
242 from fundamentals.logs import emptyLogger
243 log = emptyLogger()
244 ```
246 """
248 def info(self, argu):
249 pass
251 def error(self, argu):
252 pass
254 def debug(self, argu):
255 pass
257 def critical(self, argu):
258 pass
260 def warning(self, argu):
261 pass