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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

""" 

---------------------------------------------------------------------- 

Authors: Jan-Justin van Tonder 

---------------------------------------------------------------------- 

Wraps the logic required to setup and use a custom logger while 

disabling the built-in flask logger. 

---------------------------------------------------------------------- 

Example usage: 

 

# First import the logger from the hutts_logger module... 

from hutts_utils.hutts_logger import logger 

# then start logging. 

logger.info('logging an example') 

 

There are 5 logging levels, as shown below with their corresponding 

function call (from lowest to highest level): 

 

DEBUG - logger.debug(message) 

INFO - logger.info(message) 

WARNING - logger.warning(message) 

ERROR - logger.error(message) 

CRITICAL - logger.critical(message) 

 

The default level of the logger will be INFO, unless the flask app is 

run in debug mode, in which case the logger level will be DEBUG. 

What this means is that messages lower than INFO, i.e. DEBUG, will 

not be shown, again this is unless the flask app is run in debug 

mode. 

 

NOTE: A function (prettify_json_message) has been included to take 

a json obj/dict and return a prettified string of said json obj/dict 

in the event that someone wishes to display a message in the form of 

a json obj/dict. 

---------------------------------------------------------------------- 

TODO: Look into handling the constants in a config file instead. 

---------------------------------------------------------------------- 

""" 

 

import colorlog 

import errno 

import json 

import logging 

import os 

from logging.handlers import RotatingFileHandler 

 

"""Specifies the name of the custom logger.""" 

LOGGING_LOGGER_NAME = 'hutts_logger' 

"""Specifies the default level for logging.""" 

LOGGING_DEFAULT_LEVEL = logging.DEBUG 

"""Specifies the date format to be used by the various logging formatters.""" 

LOGGING_LOG_DATE_FMT = '%Y-%m-%d %H:%M:%S' 

 

"""Indicates whether or not to log to console.""" 

LOGGING_LOG_TO_CONSOLE = True 

"""Specifies the log format to be used when logging to the console.""" 

LOGGING_LOG_TO_CONSOLE_FMT = '[%(asctime)s.%(msecs)03d]%(log_color)s[%(levelname)8s]%(reset)s%(message_log_color)s ' \ 

'-- (%(filename)s:%(lineno)d) -- %(message)s' 

""" 

Specifies the colours to be used to indicate the different levels when logging to console. 

For a more detailed description see the colorlog documentation: https://github.com/borntyping/python-colorlog 

""" 

LOGGING_LOG_TO_CONSOLE_COLOURS = { 

'DEBUG': 'bold_cyan', 

'INFO': 'bold_white', 

'WARNING': 'bold_yellow', 

'ERROR': 'bold_red', 

'CRITICAL': 'bold_red,bg_white', 

} 

""" 

Specifies the secondary colours to be used to indicate the different levels when logging to console. 

For a more detailed description see the colorlog documentation: https://github.com/borntyping/python-colorlog 

""" 

LOGGING_LOG_TO_CONSOLE_SEC_COLOURS = { 

'message': { 

'ERROR': 'red', 

'CRITICAL': 'bold_red,bg_white', 

} 

} 

 

"""Indicates whether or not to log to a file.""" 

LOGGING_LOG_TO_FILE = True 

"""Specifies the name of the log file.""" 

LOGGING_LOG_TO_FILE_FILENAME = 'hutts_verification.log' 

"""Specifies the default directory for the log file in the event that one is not specified during setup.""" 

LOGGING_LOG_TO_FILE_DEFAULT_DIR = 'log/' 

"""Specifies the log format to be used when logging to a file.""" 

LOGGING_LOG_TO_FILE_FMT = '[%(asctime)s.%(msecs)03d][%(levelname)8s] -- (%(filename)s:%(lineno)d) -- %(message)s' 

"""Specifies the maximum number of bytes for the log file before a rotate (assuming a rotating file is used).""" 

LOGGING_LOG_TO_FILE_MAX_BYTES = 100000 

"""Specifies the number of backups for the log file (assuming a rotating file is used).""" 

LOGGING_LOG_TO_FILE_BACKUP_COUNT = 1 

"""Specifies the encoding for the log file.""" 

LOGGING_LOG_TO_FILE_ENCODING = 'utf8' 

 

""" 

A global reference to the custom logger to be used. 

It is initialised to the default python logger to avoid errors during installation of packages. 

""" 

logger = None 

 

 

def setup_logger(): 

""" 

This function is responsible for creating the custom logger and delegating the creation of its handlers. 

 

Author: 

Jan-Justin van Tonder 

""" 

global logger 

logger = logging.getLogger(__name__) 

logger.setLevel(LOGGING_DEFAULT_LEVEL) 

if LOGGING_LOG_TO_CONSOLE: 

console_handler = get_console_handler() 

console_handler.setLevel(LOGGING_DEFAULT_LEVEL) 

logger.addHandler(console_handler) 

if LOGGING_LOG_TO_FILE: 

file_handler = get_file_handler(LOGGING_LOG_TO_FILE_DEFAULT_DIR) 

file_handler.setLevel(logging.INFO) 

logger.addHandler(file_handler) 

 

 

def disable_flask_logging(app_instance): 

""" 

This function disables the flask logging, which interferes with the custom logger. 

 

Author: 

Jan-Justin van Tonder 

 

Args: 

app_instance (obj): A reference to the current flask server application. 

""" 

app_instance.handlers = [] 

app_instance.logger.propagate = False 

logging.getLogger('werkzeug').disabled = True 

 

 

def get_console_handler(): 

""" 

This function is responsible for creating a console log handler with a global format and returning it. 

 

Returns: 

handler (obj): A log handler that is responsible for logging to the console. 

""" 

formatter = colorlog.ColoredFormatter( 

fmt=LOGGING_LOG_TO_CONSOLE_FMT, 

datefmt=LOGGING_LOG_DATE_FMT, 

log_colors=LOGGING_LOG_TO_CONSOLE_COLOURS, 

secondary_log_colors=LOGGING_LOG_TO_CONSOLE_SEC_COLOURS 

) 

handler = logging.StreamHandler() 

handler.setFormatter(formatter) 

return handler 

 

 

def get_file_handler(log_dir=None): 

""" 

This function is responsible for creating a file log handler with a global format and returning it. 

 

Returns: 

handler (obj): A log handler that is responsible for logging to a file. 

""" 

log_file_dir = log_dir if log_dir else LOGGING_LOG_TO_FILE_DEFAULT_DIR 

try: 

if not os.path.exists(log_file_dir): 

os.makedirs(log_file_dir) 

except OSError as exception: 

if exception.errno != errno.EEXIST: 

raise 

log_file_path = os.path.join(log_file_dir, LOGGING_LOG_TO_FILE_FILENAME) 

formatter = logging.Formatter(fmt=LOGGING_LOG_TO_FILE_FMT, datefmt=LOGGING_LOG_DATE_FMT) 

handler = RotatingFileHandler( 

log_file_path, 

encoding=LOGGING_LOG_TO_FILE_ENCODING, 

maxBytes=LOGGING_LOG_TO_FILE_MAX_BYTES, 

backupCount=LOGGING_LOG_TO_FILE_BACKUP_COUNT 

) 

handler.setFormatter(formatter) 

return handler 

 

 

def prettify_json_message(json_message): 

""" 

This function is a helper function that is used to prettify a json/dict message obj so that is more readable 

for humans when it is logged. 

 

Args: 

json_message (dict): A message that is to be prettified before being logged. 

 

Returns: 

(str): A prettified json message string. 

""" 

return json.dumps(json_message, indent=2, sort_keys=True) 

 

 

# Set up the logger. 

setup_logger()