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
20 Implements the classes and method relative to the logging
30 from . import printcolors
31 from . import xmlManager
33 log_macro_command_file_expression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
34 log_all_command_file_expression = "^.*[0-9]{8}_+[0-9]{6}_+.*\.xml$"
38 Class to handle log mechanism.
43 all_in_terminal=False,
44 micro_command = False):
47 :param config pyconf.Config: The global configuration.
48 :param silent_sysstd boolean: if True, do not write anything
52 self.default_level = 3
53 self.silentSysStd = silent_sysstd
55 # Construct xml log file location for sat prints.
59 hour_command_host = (config.VARS.datehour + "_" +
60 config.VARS.command + "_" +
62 logFileName = prefix + hour_command_host + ".xml"
63 log_dir = src.get_log_path(config)
64 logFilePath = os.path.join(log_dir, logFileName)
65 # Construct txt file location in order to log
66 # the external commands calls (cmake, make, git clone, etc...)
67 txtFileName = prefix + hour_command_host + ".txt"
68 txtFilePath = os.path.join(log_dir, "OUT", txtFileName)
70 src.ensure_path_exists(os.path.dirname(logFilePath))
71 src.ensure_path_exists(os.path.dirname(txtFilePath))
73 # The path of the log files (one for sat traces, and the other for
74 # the system commands traces)
75 self.logFileName = logFileName
76 self.logFilePath = logFilePath
77 self.txtFileName = txtFileName
78 self.txtFilePath = txtFilePath
80 # The list of all log files corresponding to the current command and
81 # the commands called by the current command
82 self.l_logFiles = [logFilePath, txtFilePath]
84 # Initialize xml instance and put first fields
85 # like beginTime, user, command, etc...
86 self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand",
87 attrib = {"application" : config.VARS.application})
88 self.put_initial_xml_fields()
89 # Initialize the txt file for reading
91 self.logTxtFile = open(str(self.txtFilePath), 'w')
93 #msg1 = _("WARNING! Trying to write to a file that"
94 # " is not accessible:")
95 #msg2 = _("The logs won't be written.")
96 #print("%s\n%s\n%s\n" % (src.printcolors.printcWarning(msg1),
97 # src.printcolors.printcLabel(str(self.txtFilePath)),
98 # src.printcolors.printcWarning(msg2) ))
99 self.logTxtFile = tempfile.TemporaryFile()
101 # If the option all_in_terminal was called, all the system commands
102 # are redirected to the terminal
104 self.logTxtFile = sys.__stdout__
106 def put_initial_xml_fields(self):
108 Called at class initialization: Put all fields
109 corresponding to the command context (user, time, ...)
112 self.xmlFile.add_simple_node("Site", attrib={"command" :
113 self.config.VARS.command})
114 # version of salomeTools
115 self.xmlFile.append_node_attrib("Site", attrib={"satversion" :
116 self.config.INTERNAL.sat_version})
117 # machine name on which the command has been launched
118 self.xmlFile.append_node_attrib("Site", attrib={"hostname" :
119 self.config.VARS.hostname})
120 # Distribution of the machine
121 self.xmlFile.append_node_attrib("Site", attrib={"OS" :
122 self.config.VARS.dist})
123 # The user that have launched the command
124 self.xmlFile.append_node_attrib("Site", attrib={"user" :
125 self.config.VARS.user})
126 # The time when command was launched
127 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
128 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
129 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" :
131 # The application if any
132 if "APPLICATION" in self.config:
133 self.xmlFile.append_node_attrib("Site",
134 attrib={"application" : self.config.VARS.application})
135 # The initialization of the trace node
136 self.xmlFile.add_simple_node("Log",text="")
137 # The system commands logs
138 self.xmlFile.add_simple_node("OutLog",
139 text=os.path.join("OUT", self.txtFileName))
140 # The initialization of the node where
141 # to put the links to the other sat commands that can be called by any
143 self.xmlFile.add_simple_node("Links")
149 full_launched_command):
150 """Add a link to another log file.
152 :param log_file_name str: The file name of the link.
153 :param command_name str: The name of the command linked.
154 :param command_res str: The result of the command linked. "0" or "1"
155 :parma full_launched_command str: The full lanch command
158 xmlLinks = self.xmlFile.xmlroot.find("Links")
159 src.xmlManager.add_simple_node(xmlLinks,
161 text = log_file_name,
162 attrib = {"command" : command_name,
163 "passed" : command_res,
164 "launchedCommand" : full_launched_command})
166 def write(self, message, level=None, screenOnly=False):
168 function used in the commands
169 to print in the terminal and the log file.
171 :param message str: The message to print.
172 :param level int: The output level corresponding
173 to the message 0 < level < 6.
174 :param screenOnly boolean: if True, do not write in log file.
176 # do not write message starting with \r to log file
177 if not message.startswith("\r") and not screenOnly:
178 self.xmlFile.append_node_text("Log",
179 printcolors.cleancolor(message))
181 # get user or option output level
182 current_output_verbose_level = self.config.USER.output_verbose_level
183 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
184 # clean the message color if the terminal is redirected by user
185 # ex: sat compile appli > log.txt
186 message = printcolors.cleancolor(message)
188 # Print message regarding the output level value
190 if level <= current_output_verbose_level and not self.silentSysStd:
191 sys.stdout.write(message)
193 if self.default_level <= current_output_verbose_level and not self.silentSysStd:
194 sys.stdout.write(message)
197 def error(self, message):
200 :param message str: The message to print.
202 # Print in the log file
203 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
205 # Print in the terminal and clean colors if the terminal
206 # is redirected by user
207 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
208 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
210 sys.stderr.write(_('ERROR:') + message)
215 self.logTxtFile.flush()
217 def end_write(self, attribute):
219 Called just after command end: Put all fields
220 corresponding to the command end context (time).
221 Write the log xml file on the hard drive.
222 And display the command to launch to get the log
224 :param attribute dict: the attribute to add to the node "Site".
226 # Get current time (end of command) and format it
227 dt = datetime.datetime.now()
228 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
229 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
232 total_time = timedelta_total_seconds(delta)
233 hours = int(total_time / 3600)
234 minutes = int((total_time - hours*3600) / 60)
235 seconds = total_time - hours*3600 - minutes*60
236 # Add the fields corresponding to the end time
237 # and the total time of command
238 endtime = dt.strftime('%Y/%m/%d %Hh%Mm%Ss')
239 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
240 self.xmlFile.append_node_attrib("Site",
241 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
243 # Add the attribute passed to the method
244 self.xmlFile.append_node_attrib("Site", attrib=attribute)
246 # Call the method to write the xml file on the hard drive
247 self.xmlFile.write_tree(stylesheet = "command.xsl")
249 # Dump the config in a pyconf file in the log directory
250 logDir = src.get_log_path(self.config)
251 dumpedPyconfFileName = (self.config.VARS.datehour
253 + self.config.VARS.command
255 dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
257 f = open(dumpedPyconfFilePath, 'w')
258 self.config.__save__(f)
263 def date_to_datetime(date):
265 From a string date in format YYYYMMDD_HHMMSS
266 returns list year, mon, day, hour, minutes, seconds
268 :param date str: The date in format YYYYMMDD_HHMMSS
269 :return: the same date and time in separate variables.
270 :rtype: (str,str,str,str,str,str)
278 return Y, m, dd, H, M, S
280 def timedelta_total_seconds(timedelta):
282 Replace total_seconds from datetime module
283 in order to be compatible with old python versions
285 :param timedelta datetime.timedelta: The delta between two dates
286 :return: The number of seconds corresponding to timedelta.
290 timedelta.microseconds + 0.0 +
291 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
293 def show_command_log(logFilePath, cmd, application, notShownCommands):
295 Used in updateHatXml.
296 Determine if the log xml file logFilePath
297 has to be shown or not in the hat log.
299 :param logFilePath str: the path to the command xml log file
300 :param cmd str: the command of the log file
301 :param application str: the application passed as parameter
302 to the salomeTools command
303 :param notShownCommands list: the list of commands
304 that are not shown by default
306 :return: True if cmd is not in notShownCommands and the application
307 in the log file corresponds to application
310 # When the command is not in notShownCommands, no need to go further :
312 if cmd in notShownCommands:
313 return False, None, None
315 # Get the application of the log file
317 logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
318 except Exception as e:
319 msg = _("WARNING: the log file %s cannot be read:" % logFilePath)
320 sys.stdout.write(printcolors.printcWarning("%s\n%s\n" % (msg, e)))
321 return False, None, None
323 if 'application' in logFileXml.xmlroot.keys():
324 appliLog = logFileXml.xmlroot.get('application')
325 launched_cmd = logFileXml.xmlroot.find('Site').attrib['launchedCommand']
326 # if it corresponds, then the log has to be shown
327 if appliLog == application:
328 return True, appliLog, launched_cmd
329 elif application != 'None':
330 return False, appliLog, launched_cmd
332 return True, appliLog, launched_cmd
334 if application == 'None':
335 return True, None, None
337 return False, None, None
339 def list_log_file(dirPath, expression):
340 """Find all files corresponding to expression in dirPath
342 :param dirPath str: the directory where to search the files
343 :param expression str: the regular expression of files to find
344 :return: the list of files path and informations about it
348 for fileName in os.listdir(dirPath):
349 # YYYYMMDD_HHMMSS_namecmd.xml
351 oExpr = re.compile(sExpr)
352 if oExpr.search(fileName):
354 if fileName.startswith("micro_"):
355 file_name = fileName[len("micro_"):]
356 # get date and hour and format it
357 date_hour_cmd_host = file_name.split('_')
358 date_not_formated = date_hour_cmd_host[0]
359 date = "%s/%s/%s" % (date_not_formated[6:8],
360 date_not_formated[4:6],
361 date_not_formated[0:4])
362 hour_not_formated = date_hour_cmd_host[1]
363 hour = "%s:%s:%s" % (hour_not_formated[0:2],
364 hour_not_formated[2:4],
365 hour_not_formated[4:6])
366 if len(date_hour_cmd_host) < 4:
367 cmd = date_hour_cmd_host[2][:-len('.xml')]
370 cmd = date_hour_cmd_host[2]
371 host = date_hour_cmd_host[3][:-len('.xml')]
372 lRes.append((os.path.join(dirPath, fileName),
381 def update_hat_xml(logDir, application=None, notShownCommands = []):
383 Create the xml file in logDir that contain all the xml file
384 and have a name like YYYYMMDD_HHMMSS_namecmd.xml
386 :param logDir str: the directory to parse
387 :param application str: the name of the application if there is any
389 # Create an instance of XmlLogFile class to create hat.xml file
390 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
391 xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
392 "LOGlist", {"application" : application})
393 # parse the log directory to find all the command logs,
394 # then add it to the xml file
395 lLogFile = list_log_file(logDir, log_macro_command_file_expression)
396 for filePath, __, date, __, hour, cmd, __ in lLogFile:
397 showLog, cmdAppli, full_cmd = show_command_log(filePath, cmd,
398 application, notShownCommands)
399 #if cmd not in notShownCommands:
401 # add a node to the hat.xml file
402 xmlHat.add_simple_node("LogCommand",
403 text=os.path.basename(filePath),
404 attrib = {"date" : date,
407 "application" : cmdAppli,
408 "full_command" : full_cmd})
410 # Write the file on the hard drive
411 xmlHat.write_tree('hat.xsl')