Coverage for /var/devmt/py/utils4_1.8.0/utils4/log.py: 100%

37 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2025-08-26 21:39 +0100

1# -*- coding: utf-8 -*- 

2""" 

3:Purpose: This is a small class module designed as a central log file 

4 creator. 

5 

6 The calling program is responsible for passing the proper 

7 arguments to create the log file header. For example:: 

8 

9 (printheader=True, headertext='some,header,text,here') 

10 

11 It is suggested to initialise the :class:`~Log` class 

12 at program startup, with the ``printheader`` parameter 

13 set to ``True``. This is safe because if the log file 

14 already exists, the header will not be re-created. However, 

15 if the log file does not exist, it will be created with a 

16 header, using the value of the ``headertext`` parameter. 

17 

18:Developer: J Berendt 

19:Email: support@s3dev.uk 

20 

21:Comments: n/a 

22 

23:Example: 

24 

25 To use the :class:`~Log` class in your project:: 

26 

27 >>> from utils4.log import Log 

28 

29 >> header = 'COMMENT,TYPE,VAL,UNIT' 

30 >> logger = Log(filepath='/tmp/testlog.log', 

31 printheader=True, 

32 headertext=header) 

33 >> logger.write(text='Most cows can jump over the moon,Fact,94.2,pct') 

34 

35""" 

36 

37import getpass 

38import os 

39import socket 

40from datetime import datetime as dt 

41 

42 

43class Log: 

44 """This is a small and simple log file creator/writer class. 

45 

46 The calling program is responsible for passing the proper arguments 

47 to create the log file header. For example:: 

48 

49 (printheader=True, headertext='some,header,text,here') 

50 

51 On class instantiation, validation is performed to determine if the 

52 log file exists. If the log file does not exist, a header is 

53 required before writing to the log file. These parameters can be passed to 

54 the class on instantiation, and will be ignored if the log file already 

55 exists. 

56 

57 Args: 

58 filepath (str): Full path to the log file. 

59 autofill (bool, optional): Automatically populate ``datetime.now()``, 

60 host and username values, to each log entry. Defaults to True. 

61 printheader (bool, optional): Print a log file header using the text 

62 passed into the ``headertext`` parameter. Defaults to False. 

63 

64 .. note:: The header will only be written if the log file does not 

65 exist. 

66 

67 headertext (str, optional): String of delimited column labels to be 

68 written as the header. Defaults to ''. 

69 sep (str, optional): Separator to be used in the log file. This 

70 separator is used when writing the autofill values. 

71 Defaults to ','. 

72 

73 :File Validation: 

74 On class instantiation, tests are performed to ensure the log 

75 file is being populated in a logical way. 

76 

77 * If ``printheader`` is ``False``, and the log file does not 

78 exist, the user is notified. 

79 * If ``printheader`` is ``True``, yet ``headertext`` is 

80 blank, the user is instructed to pass header text. 

81 * If ``printheader`` is ``True``, yet the log file already 

82 exists, the header will not be written. 

83 

84 :Example: 

85 

86 To use the :class:`~Log` class in your project:: 

87 

88 >>> from utils4.log import Log 

89 

90 >>> header = 'COMMENT,TYPE,VAL,UNIT' 

91 >>> logger = Log(filepath='/tmp/testlog.log', 

92 printheader=True, 

93 headertext=header) 

94 

95 >>> logger.write(text='Most cows can jump over the moon,Fact,94.2,pct') 

96 

97 """ 

98 

99 def __init__(self, filepath, *, autofill=True, printheader=False, headertext='', sep=','): 

100 """Log class initialiser.""" 

101 self._filepath = filepath 

102 self._autofill = autofill 

103 self._printheader = printheader 

104 self._headertext = headertext 

105 self._sep = sep 

106 self._host = socket.gethostname() 

107 self._user = getpass.getuser() 

108 self._autotext = '' 

109 self._setup() 

110 

111 def write(self, text: str): 

112 """Write text to the log file defined at instantiation. 

113 

114 Args: 

115 text (str): Delimited text string to be written to the log. 

116 

117 Note: 

118 If ``autofill`` is ``True``, the datetime, host and username 

119 values **will be populated automatically**; these do *not* 

120 need to be passed into the ``text`` argument. 

121 

122 :Design: 

123 If the ``autofill`` argument is ``True``, the current 

124 datetime, host and username values are written (in that 

125 order), ahead of the text string provided to the ``text`` 

126 argument. The ``sep`` parameter (defined at instantiation), 

127 is used to separate these auto-populated fields. 

128 

129 :Example: 

130 To write to the log file:: 

131 

132 >>> from utils4.log import Log 

133 

134 >>> logger = Log(filepath='/tmp/testlog.log, autofill=True) 

135 >>> logger.write(text='Just adding some random text to my log') 

136 

137 """ 

138 try: 

139 if text: 

140 auto_text = f'{dt.now()}{self._sep}{self._autotext}' if self._autofill else '' 

141 with open(self._filepath, 'a', encoding='utf-8') as f: 

142 f.write(auto_text) 

143 f.write(text) 

144 f.write('\n') 

145 except Exception as err: # pragma: nocover 

146 print(err) 

147 

148 def write_blank_line(self): 

149 """Write a blank line to the log file. 

150 

151 Note: 

152 The ``autofill`` information is **not** written. This is a 

153 true blank line, created by writing the system's native line 

154 separator character(s)to the log. 

155 

156 :Example: 

157 To write a blank line to the log file:: 

158 

159 >>> from utils4.log import Log 

160 

161 >>> logger = Log(filepath='/tmp/testlog.log', autofill=True) 

162 >>> logger.write_blank_line() 

163 

164 """ 

165 try: 

166 with open(self._filepath, 'a', encoding='utf-8') as f: 

167 f.write('\n') 

168 except Exception as err: # pragma: nocover 

169 print(err) 

170 

171 def _setup(self): 

172 """Setup tasks performed on class instantiation. 

173 

174 :Tasks: 

175 

176 - Create the log file. 

177 - Write the header. 

178 

179 :Validation: 

180 

181 - If the log file does not exist, ensure a header is requested. 

182 - If the header is requested, ensure the header text is provided. 

183 

184 Raised: 

185 UserWarning: If either of the validation criteria are not met. 

186 

187 """ 

188 self._autotext = (f'{self._host}{self._sep}{self._user}{self._sep}' 

189 if self._autofill else '') 

190 # Verify the logfile does not exists, ensure a header is requested. 

191 if (not self._printheader) & (not os.path.exists(self._filepath)): 

192 raise UserWarning('Header expected, log does not already exist.\n' 

193 '- Log file does not exist, therefore a header must be requested,\n' 

194 ' as the header is written at the time of log file creation.\n') 

195 # Verify header text is populated, if print header is requested. 

196 if self._printheader & (not self._headertext): 

197 raise UserWarning('Header requested, but header text not received.\n' 

198 '- The printheader argument is True, however the headertext string\n' 

199 ' is blank. A headertext string must also be supplied.\n') 

200 # Write the header if 1) requested, 2) header text provided and 3) log file does not exist. 

201 if all([self._printheader, self._headertext, not os.path.exists(self._filepath)]): 

202 with open(self._filepath, 'w', encoding='utf-8') as f: 

203 f.write(self._headertext) 

204 f.write('\n')