3 # Copyright (C) 2010-2012 CEA/DEN
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 '''In this file are implemented the classes and method relative to the logging
27 from . import printcolors
28 from . import xmlManager
30 log_macro_command_file_expression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
31 log_all_command_file_expression = "^.*[0-9]{8}_+[0-9]{6}_+.*\.xml$"
34 '''Class to handle log mechanism.
39 all_in_terminal=False,
40 micro_command = False):
43 :param config pyconf.Config: The global configuration.
44 :param silent_sysstd boolean: if True, do not write anything
48 self.default_level = 3
49 self.silentSysStd = silent_sysstd
51 # Construct xml log file location for sat prints.
55 hour_command_host = (config.VARS.datehour + "_" +
56 config.VARS.command + "_" +
58 logFileName = prefix + hour_command_host + ".xml"
59 log_dir = src.get_log_path(config)
60 logFilePath = os.path.join(log_dir, logFileName)
61 # Construct txt file location in order to log
62 # the external commands calls (cmake, make, git clone, etc...)
63 txtFileName = prefix + hour_command_host + ".txt"
64 txtFilePath = os.path.join(log_dir, "OUT", txtFileName)
66 src.ensure_path_exists(os.path.dirname(logFilePath))
67 src.ensure_path_exists(os.path.dirname(txtFilePath))
69 # The path of the log files (one for sat traces, and the other for
70 # the system commands traces)
71 self.logFileName = logFileName
72 self.logFilePath = logFilePath
73 self.txtFileName = txtFileName
74 self.txtFilePath = txtFilePath
76 # The list of all log files corresponding to the current command and
77 # the commands called by the current command
78 self.l_logFiles = [logFilePath, txtFilePath]
80 # Initialize xml instance and put first fields
81 # like beginTime, user, command, etc...
82 self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand",
83 attrib = {"application" : config.VARS.application})
84 self.put_initial_xml_fields()
85 # Initialize the txt file for reading
86 self.logTxtFile = open(str(self.txtFilePath), 'w')
87 # If the option all_in_terminal was called, all the system commands
88 # are redirected to the terminal
90 self.logTxtFile = sys.__stdout__
92 def put_initial_xml_fields(self):
93 '''Method called at class initialization : Put all fields
94 corresponding to the command context (user, time, ...)
97 self.xmlFile.add_simple_node("Site", attrib={"command" :
98 self.config.VARS.command})
99 # version of salomeTools
100 self.xmlFile.append_node_attrib("Site", attrib={"satversion" :
101 self.config.INTERNAL.sat_version})
102 # machine name on which the command has been launched
103 self.xmlFile.append_node_attrib("Site", attrib={"hostname" :
104 self.config.VARS.hostname})
105 # Distribution of the machine
106 self.xmlFile.append_node_attrib("Site", attrib={"OS" :
107 self.config.VARS.dist})
108 # The user that have launched the command
109 self.xmlFile.append_node_attrib("Site", attrib={"user" :
110 self.config.VARS.user})
111 # The time when command was launched
112 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
113 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
114 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" :
116 # The application if any
117 if "APPLICATION" in self.config:
118 self.xmlFile.append_node_attrib("Site",
119 attrib={"application" : self.config.VARS.application})
120 # The initialization of the trace node
121 self.xmlFile.add_simple_node("Log",text="")
122 # The system commands logs
123 self.xmlFile.add_simple_node("OutLog",
124 text=os.path.join("OUT", self.txtFileName))
125 # The initialization of the node where
126 # to put the links to the other sat commands that can be called by any
128 self.xmlFile.add_simple_node("Links")
134 full_launched_command):
135 '''Add a link to another log file.
137 :param log_file_name str: The file name of the link.
138 :param command_name str: The name of the command linked.
139 :param command_res str: The result of the command linked. "0" or "1"
140 :parma full_launched_command str: The full lanch command
143 xmlLinks = self.xmlFile.xmlroot.find("Links")
144 src.xmlManager.add_simple_node(xmlLinks,
146 text = log_file_name,
147 attrib = {"command" : command_name,
148 "passed" : command_res,
149 "launchedCommand" : full_launched_command})
151 def write(self, message, level=None, screenOnly=False):
152 '''the function used in the commands
153 that will print in the terminal and the log file.
155 :param message str: The message to print.
156 :param level int: The output level corresponding
157 to the message 0 < level < 6.
158 :param screenOnly boolean: if True, do not write in log file.
160 # do not write message starting with \r to log file
161 if not message.startswith("\r") and not screenOnly:
162 self.xmlFile.append_node_text("Log",
163 printcolors.cleancolor(message))
165 # get user or option output level
166 current_output_verbose_level = self.config.USER.output_verbose_level
167 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
168 # clean the message color if the terminal is redirected by user
169 # ex: sat compile appli > log.txt
170 message = printcolors.cleancolor(message)
172 # Print message regarding the output level value
174 if level <= current_output_verbose_level and not self.silentSysStd:
175 sys.stdout.write(message)
177 if self.default_level <= current_output_verbose_level and not self.silentSysStd:
178 sys.stdout.write(message)
181 def error(self, message):
184 :param message str: The message to print.
186 # Print in the log file
187 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
189 # Print in the terminal and clean colors if the terminal
190 # is redirected by user
191 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
192 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
194 sys.stderr.write(_('ERROR:') + message)
200 self.logTxtFile.flush()
202 def end_write(self, attribute):
203 '''Method called just after command end : Put all fields
204 corresponding to the command end context (time).
205 Write the log xml file on the hard drive.
206 And display the command to launch to get the log
208 :param attribute dict: the attribute to add to the node "Site".
210 # Get current time (end of command) and format it
211 dt = datetime.datetime.now()
212 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
213 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
216 total_time = timedelta_total_seconds(delta)
217 hours = int(total_time / 3600)
218 minutes = int((total_time - hours*3600) / 60)
219 seconds = total_time - hours*3600 - minutes*60
220 # Add the fields corresponding to the end time
221 # and the total time of command
222 endtime = dt.strftime('%Y/%m/%d %Hh%Mm%Ss')
223 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
224 self.xmlFile.append_node_attrib("Site",
225 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
227 # Add the attribute passed to the method
228 self.xmlFile.append_node_attrib("Site", attrib=attribute)
230 # Call the method to write the xml file on the hard drive
231 self.xmlFile.write_tree(stylesheet = "command.xsl")
233 # Dump the config in a pyconf file in the log directory
234 logDir = src.get_log_path(self.config)
235 dumpedPyconfFileName = (self.config.VARS.datehour
237 + self.config.VARS.command
239 dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
240 f = open(dumpedPyconfFilePath, 'w')
241 self.config.__save__(f)
245 def date_to_datetime(date):
246 '''Little method that gets year, mon, day, hour ,
247 minutes and seconds from a str in format YYYYMMDD_HHMMSS
249 :param date str: The date in format YYYYMMDD_HHMMSS
250 :return: the same date and time in separate variables.
251 :rtype: (str,str,str,str,str,str)
259 return Y, m, dd, H, M, S
261 def timedelta_total_seconds(timedelta):
262 '''Little method to replace total_seconds from
263 datetime module in order to be compatible with old python versions
265 :param timedelta datetime.timedelta: The delta between two dates
266 :return: The number of seconds corresponding to timedelta.
270 timedelta.microseconds + 0.0 +
271 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
273 def show_command_log(logFilePath, cmd, application, notShownCommands):
274 '''Used in updateHatXml. Determine if the log xml file logFilePath
275 has to be shown or not in the hat log.
277 :param logFilePath str: the path to the command xml log file
278 :param cmd str: the command of the log file
279 :param application str: the application passed as parameter
280 to the salomeTools command
281 :param notShownCommands list: the list of commands
282 that are not shown by default
284 :return: True if cmd is not in notShownCommands and the application
285 in the log file corresponds to application
288 # When the command is not in notShownCommands, no need to go further :
290 if cmd in notShownCommands:
291 return False, None, None
293 # Get the application of the log file
295 logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
296 except Exception as e:
297 msg = _("WARNING: the log file %s cannot be read:" % logFilePath)
298 sys.stdout.write(printcolors.printcWarning("%s\n%s\n" % (msg, e)))
299 return False, None, None
301 if 'application' in logFileXml.xmlroot.keys():
302 appliLog = logFileXml.xmlroot.get('application')
303 launched_cmd = logFileXml.xmlroot.find('Site').attrib['launchedCommand']
304 # if it corresponds, then the log has to be shown
305 if appliLog == application:
306 return True, appliLog, launched_cmd
307 elif application != 'None':
308 return False, appliLog, launched_cmd
310 return True, appliLog, launched_cmd
312 if application == 'None':
313 return True, None, None
315 return False, None, None
317 def list_log_file(dirPath, expression):
318 '''Find all files corresponding to expression in dirPath
320 :param dirPath str: the directory where to search the files
321 :param expression str: the regular expression of files to find
322 :return: the list of files path and informations about it
326 for fileName in os.listdir(dirPath):
327 # YYYYMMDD_HHMMSS_namecmd.xml
329 oExpr = re.compile(sExpr)
330 if oExpr.search(fileName):
332 if fileName.startswith("micro_"):
333 file_name = fileName[len("micro_"):]
334 # get date and hour and format it
335 date_hour_cmd_host = file_name.split('_')
336 date_not_formated = date_hour_cmd_host[0]
337 date = "%s/%s/%s" % (date_not_formated[6:8],
338 date_not_formated[4:6],
339 date_not_formated[0:4])
340 hour_not_formated = date_hour_cmd_host[1]
341 hour = "%s:%s:%s" % (hour_not_formated[0:2],
342 hour_not_formated[2:4],
343 hour_not_formated[4:6])
344 if len(date_hour_cmd_host) < 4:
345 cmd = date_hour_cmd_host[2][:-len('.xml')]
348 cmd = date_hour_cmd_host[2]
349 host = date_hour_cmd_host[3][:-len('.xml')]
350 lRes.append((os.path.join(dirPath, fileName),
359 def update_hat_xml(logDir, application=None, notShownCommands = []):
360 '''Create the xml file in logDir that contain all the xml file
361 and have a name like YYYYMMDD_HHMMSS_namecmd.xml
363 :param logDir str: the directory to parse
364 :param application str: the name of the application if there is any
366 # Create an instance of XmlLogFile class to create hat.xml file
367 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
368 xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
369 "LOGlist", {"application" : application})
370 # parse the log directory to find all the command logs,
371 # then add it to the xml file
372 lLogFile = list_log_file(logDir, log_macro_command_file_expression)
373 for filePath, __, date, __, hour, cmd, __ in lLogFile:
374 showLog, cmdAppli, full_cmd = show_command_log(filePath, cmd,
375 application, notShownCommands)
376 #if cmd not in notShownCommands:
378 # add a node to the hat.xml file
379 xmlHat.add_simple_node("LogCommand",
380 text=os.path.basename(filePath),
381 attrib = {"date" : date,
384 "application" : cmdAppli,
385 "full_command" : full_cmd})
387 # Write the file on the hard drive
388 xmlHat.write_tree('hat.xsl')