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 logCommandFileExpression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
33 '''Class to handle log mechanism.
38 all_in_terminal=False,
39 micro_command = False):
42 :param config pyconf.Config: The global configuration.
43 :param silent_sysstd boolean: if True, do not write anything
47 self.default_level = 3
48 self.silentSysStd = silent_sysstd
50 # Construct xml log file location for sat prints.
54 hour_command_host = (config.VARS.datehour + "_" +
55 config.VARS.command + "_" +
57 logFileName = prefix + hour_command_host + ".xml"
58 logFilePath = os.path.join(config.USER.log_dir, logFileName)
59 # Construct txt file location in order to log
60 # the external commands calls (cmake, make, git clone, etc...)
61 txtFileName = prefix + hour_command_host + ".txt"
62 txtFilePath = os.path.join(config.USER.log_dir, "OUT", txtFileName)
64 src.ensure_path_exists(os.path.dirname(logFilePath))
65 src.ensure_path_exists(os.path.dirname(txtFilePath))
67 # The path of the log files (one for sat traces, and the other for
68 # the system commands traces)
69 self.logFileName = logFileName
70 self.logFilePath = logFilePath
71 self.txtFileName = txtFileName
72 self.txtFilePath = txtFilePath
74 # The list of all log files corresponding to the current command and
75 # the commands called by the current command
76 self.l_logFiles = [logFilePath, txtFilePath]
78 # Initialize xml instance and put first fields
79 # like beginTime, user, command, etc...
80 self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand",
81 attrib = {"application" : config.VARS.application})
82 self.put_initial_xml_fields()
83 # Initialize the txt file for reading
84 self.logTxtFile = open(str(self.txtFilePath), 'w')
85 # If the option all_in_terminal was called, all the system commands
86 # are redirected to the terminal
88 self.logTxtFile = sys.__stdout__
90 def put_initial_xml_fields(self):
91 '''Method called at class initialization : Put all fields
92 corresponding to the command context (user, time, ...)
95 self.xmlFile.add_simple_node("Site", attrib={"command" :
96 self.config.VARS.command})
97 # version of salomeTools
98 self.xmlFile.append_node_attrib("Site", attrib={"satversion" :
99 self.config.INTERNAL.sat_version})
100 # machine name on which the command has been launched
101 self.xmlFile.append_node_attrib("Site", attrib={"hostname" :
102 self.config.VARS.hostname})
103 # Distribution of the machine
104 self.xmlFile.append_node_attrib("Site", attrib={"OS" :
105 self.config.VARS.dist})
106 # The user that have launched the command
107 self.xmlFile.append_node_attrib("Site", attrib={"user" :
108 self.config.VARS.user})
109 # The time when command was launched
110 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
111 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
112 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" :
114 # The application if any
115 if "APPLICATION" in self.config:
116 self.xmlFile.append_node_attrib("Site",
117 attrib={"application" : self.config.VARS.application})
118 # The initialization of the trace node
119 self.xmlFile.add_simple_node("Log",text="")
120 # The system commands logs
121 self.xmlFile.add_simple_node("OutLog",
122 text=os.path.join("OUT", self.txtFileName))
123 # The initialization of the node where
124 # to put the links to the other sat commands that can be called by any
126 self.xmlFile.add_simple_node("Links")
132 full_launched_command):
133 '''Add a link to another log file.
135 :param log_file_name str: The file name of the link.
136 :param command_name str: The name of the command linked.
137 :param command_res str: The result of the command linked. "0" or "1"
138 :parma full_launched_command str: The full lanch command
141 xmlLinks = self.xmlFile.xmlroot.find("Links")
142 src.xmlManager.add_simple_node(xmlLinks,
144 text = log_file_name,
145 attrib = {"command" : command_name,
146 "passed" : command_res,
147 "launchedCommand" : full_launched_command})
149 def write(self, message, level=None, screenOnly=False):
150 '''the function used in the commands
151 that will print in the terminal and the log file.
153 :param message str: The message to print.
154 :param level int: The output level corresponding
155 to the message 0 < level < 6.
156 :param screenOnly boolean: if True, do not write in log file.
158 # do not write message starting with \r to log file
159 if not message.startswith("\r") and not screenOnly:
160 self.xmlFile.append_node_text("Log",
161 printcolors.cleancolor(message))
163 # get user or option output level
164 current_output_verbose_level = self.config.USER.output_verbose_level
165 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
166 # clean the message color if the terminal is redirected by user
167 # ex: sat compile appli > log.txt
168 message = printcolors.cleancolor(message)
170 # Print message regarding the output level value
172 if level <= current_output_verbose_level and not self.silentSysStd:
173 sys.stdout.write(message)
175 if self.default_level <= current_output_verbose_level and not self.silentSysStd:
176 sys.stdout.write(message)
179 def error(self, message):
182 :param message str: The message to print.
184 # Print in the log file
185 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
187 # Print in the terminal and clean colors if the terminal
188 # is redirected by user
189 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
190 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
192 sys.stderr.write(_('ERROR:') + message)
198 self.logTxtFile.flush()
200 def end_write(self, attribute):
201 '''Method called just after command end : Put all fields
202 corresponding to the command end context (time).
203 Write the log xml file on the hard drive.
204 And display the command to launch to get the log
206 :param attribute dict: the attribute to add to the node "Site".
208 # Get current time (end of command) and format it
209 dt = datetime.datetime.now()
210 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
211 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
214 total_time = timedelta_total_seconds(delta)
215 hours = int(total_time / 3600)
216 minutes = int((total_time - hours*3600) / 60)
217 seconds = total_time - hours*3600 - minutes*60
218 # Add the fields corresponding to the end time
219 # and the total time of command
220 endtime = dt.strftime('%Y/%m/%d %Hh%Mm%Ss')
221 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
222 self.xmlFile.append_node_attrib("Site",
223 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
225 # Add the attribute passed to the method
226 self.xmlFile.append_node_attrib("Site", attrib=attribute)
228 # Call the method to write the xml file on the hard drive
229 self.xmlFile.write_tree(stylesheet = "command.xsl")
231 # Dump the config in a pyconf file in the log directory
232 logDir = self.config.USER.log_dir
233 dumpedPyconfFileName = (self.config.VARS.datehour
235 + self.config.VARS.command
237 dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
238 f = open(dumpedPyconfFilePath, 'w')
239 self.config.__save__(f)
243 def date_to_datetime(date):
244 '''Little method that gets year, mon, day, hour ,
245 minutes and seconds from a str in format YYYYMMDD_HHMMSS
247 :param date str: The date in format YYYYMMDD_HHMMSS
248 :return: the same date and time in separate variables.
249 :rtype: (str,str,str,str,str,str)
257 return Y, m, dd, H, M, S
259 def timedelta_total_seconds(timedelta):
260 '''Little method to replace total_seconds from
261 datetime module in order to be compatible with old python versions
263 :param timedelta datetime.timedelta: The delta between two dates
264 :return: The number of seconds corresponding to timedelta.
268 timedelta.microseconds + 0.0 +
269 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
271 def show_command_log(logFilePath, cmd, application, notShownCommands):
272 '''Used in updateHatXml. Determine if the log xml file logFilePath
273 has to be shown or not in the hat log.
275 :param logFilePath str: the path to the command xml log file
276 :param cmd str: the command of the log file
277 :param application str: the application passed as parameter
278 to the salomeTools command
279 :param notShownCommands list: the list of commands
280 that are not shown by default
282 :return: True if cmd is not in notShownCommands and the application
283 in the log file corresponds to application
286 # When the command is not in notShownCommands, no need to go further :
288 if cmd in notShownCommands:
291 # Get the application of the log file
293 logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
294 except Exception as e:
295 msg = _("WARNING: the log file %s cannot be read:" % logFilePath)
296 sys.stdout.write(printcolors.printcWarning("%s\n%s\n" % (msg, e)))
299 if 'application' in logFileXml.xmlroot.keys():
300 appliLog = logFileXml.xmlroot.get('application')
301 # if it corresponds, then the log has to be shown
302 if appliLog == application:
303 return True, appliLog
304 elif application != 'None':
305 return False, appliLog
307 return True, appliLog
309 if application == 'None':
314 def list_log_file(dirPath, expression):
315 '''Find all files corresponding to expression in dirPath
317 :param dirPath str: the directory where to search the files
318 :param expression str: the regular expression of files to find
319 :return: the list of files path and informations about it
323 for fileName in os.listdir(dirPath):
324 # YYYYMMDD_HHMMSS_namecmd.xml
326 oExpr = re.compile(sExpr)
327 if oExpr.search(fileName):
328 # get date and hour and format it
329 date_hour_cmd_host = fileName.split('_')
330 date_not_formated = date_hour_cmd_host[0]
331 date = "%s/%s/%s" % (date_not_formated[6:8],
332 date_not_formated[4:6],
333 date_not_formated[0:4])
334 hour_not_formated = date_hour_cmd_host[1]
335 hour = "%s:%s:%s" % (hour_not_formated[0:2],
336 hour_not_formated[2:4],
337 hour_not_formated[4:6])
338 if len(date_hour_cmd_host) < 4:
339 cmd = date_hour_cmd_host[2][:-len('.xml')]
342 cmd = date_hour_cmd_host[2]
343 host = date_hour_cmd_host[3][:-len('.xml')]
344 lRes.append((os.path.join(dirPath, fileName),
353 def update_hat_xml(logDir, application=None, notShownCommands = []):
354 '''Create the xml file in logDir that contain all the xml file
355 and have a name like YYYYMMDD_HHMMSS_namecmd.xml
357 :param logDir str: the directory to parse
358 :param application str: the name of the application if there is any
360 # Create an instance of XmlLogFile class to create hat.xml file
361 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
362 xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
363 "LOGlist", {"application" : application})
364 # parse the log directory to find all the command logs,
365 # then add it to the xml file
366 lLogFile = list_log_file(logDir, logCommandFileExpression)
367 for filePath, __, date, __, hour, cmd, __ in lLogFile:
368 showLog, cmdAppli = show_command_log(filePath, cmd,
369 application, notShownCommands)
370 #if cmd not in notShownCommands:
372 # add a node to the hat.xml file
373 xmlHat.add_simple_node("LogCommand",
374 text=os.path.basename(filePath),
375 attrib = {"date" : date,
378 "application" : cmdAppli})
380 # Write the file on the hard drive
381 xmlHat.write_tree('hat.xsl')