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.
35 def __init__(self, config, silent_sysstd=False, all_in_terminal=False):
38 :param config pyconf.Config: The global configuration.
39 :param silent_sysstd boolean: if True, do not write anything
43 self.default_level = 3
44 self.silentSysStd = silent_sysstd
46 # Construct xml log file location for sat prints.
47 logFileName = config.VARS.datehour + "_" + config.VARS.command + ".xml"
48 logFilePath = os.path.join(config.SITE.log.log_dir, logFileName)
49 # Construct txt file location in order to log
50 # the external commands calls (cmake, make, git clone, etc...)
51 txtFileName = config.VARS.datehour + "_" + config.VARS.command + ".txt"
52 txtFilePath = os.path.join(config.SITE.log.log_dir, "OUT", txtFileName)
54 src.ensure_path_exists(os.path.dirname(logFilePath))
55 src.ensure_path_exists(os.path.dirname(txtFilePath))
57 self.logFileName = logFileName
58 self.logFilePath = logFilePath
59 self.txtFileName = txtFileName
60 self.txtFilePath = txtFilePath
61 # Initialize xml instance and put first fields
62 # like beginTime, user, command, etc...
63 self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand",
64 attrib = {"application" : config.VARS.application})
65 self.put_initial_xml_fields()
66 # Initialize the txt file for reading
67 self.logTxtFile = open(str(self.txtFilePath), 'w')
69 self.logTxtFile = sys.__stdout__
71 def put_initial_xml_fields(self):
72 '''Method called at class initialization : Put all fields
73 corresponding to the command context (user, time, ...)
76 self.xmlFile.add_simple_node("Site", attrib={"command" :
77 self.config.VARS.command})
78 # version of salomeTools
79 self.xmlFile.append_node_attrib("Site", attrib={"satversion" :
80 self.config.INTERNAL.sat_version})
81 # machine name on which the command has been launched
82 self.xmlFile.append_node_attrib("Site", attrib={"hostname" :
83 self.config.VARS.hostname})
84 # Distribution of the machine
85 self.xmlFile.append_node_attrib("Site", attrib={"OS" :
86 self.config.VARS.dist})
87 # The user that have launched the command
88 self.xmlFile.append_node_attrib("Site", attrib={"user" :
89 self.config.VARS.user})
90 # The time when command was launched
91 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
92 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
93 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" :
95 # The application if any
96 if "APPLICATION" in self.config:
97 self.xmlFile.append_node_attrib("Site",
98 attrib={"application" : self.config.VARS.application})
99 # The initialization of the trace node
100 self.xmlFile.add_simple_node("Log",text="")
101 # The system commands logs
102 self.xmlFile.add_simple_node("OutLog",
103 text=os.path.join("OUT", self.txtFileName))
104 # The initialization of the node where
105 # to put the links to the other sat commands that can be called by any
107 self.xmlFile.add_simple_node("Links")
109 def write(self, message, level=None, screenOnly=False):
110 '''the function used in the commands
111 that will print in the terminal and the log file.
113 :param message str: The message to print.
114 :param level int: The output level corresponding
115 to the message 0 < level < 6.
116 :param screenOnly boolean: if True, do not write in log file.
118 # do not write message starting with \r to log file
119 if not message.startswith("\r") and not screenOnly:
120 self.xmlFile.append_node_text("Log",
121 printcolors.cleancolor(message))
123 # get user or option output level
124 current_output_verbose_level = self.config.USER.output_verbose_level
125 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
126 # clean the message color if the terminal is redirected by user
127 # ex: sat compile appli > log.txt
128 message = printcolors.cleancolor(message)
130 # Print message regarding the output level value
132 if level <= current_output_verbose_level and not self.silentSysStd:
133 sys.stdout.write(message)
135 if self.default_level <= current_output_verbose_level and not self.silentSysStd:
136 sys.stdout.write(message)
138 def error(self, message):
141 :param message str: The message to print.
143 # Print in the log file
144 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
146 # Print in the terminal and clean colors if the terminal
147 # is redirected by user
148 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
149 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
151 sys.stderr.write(_('ERROR:') + message)
157 self.logTxtFile.flush()
159 def end_write(self, attribute):
160 '''Method called just after command end : Put all fields
161 corresponding to the command end context (time).
162 Write the log xml file on the hard drive.
163 And display the command to launch to get the log
165 :param attribute dict: the attribute to add to the node "Site".
167 # Get current time (end of command) and format it
168 dt = datetime.datetime.now()
169 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
170 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
173 total_time = timedelta_total_seconds(delta)
174 hours = int(total_time / 3600)
175 minutes = int((total_time - hours*3600) / 60)
176 seconds = total_time - hours*3600 - minutes*60
177 # Add the fields corresponding to the end time
178 # and the total time of command
179 endtime = dt.strftime('%d/%Y/%m %Hh%Mm%Ss')
180 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
181 self.xmlFile.append_node_attrib("Site",
182 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
184 # Add the attribute passed to the method
185 self.xmlFile.append_node_attrib("Site", attrib=attribute)
187 # Call the method to write the xml file on the hard drive
188 self.xmlFile.write_tree(stylesheet = "command.xsl")
190 # Dump the config in a pyconf file in the log directory
191 logDir = self.config.SITE.log.log_dir
192 dumpedPyconfFileName = (self.config.VARS.datehour
194 + self.config.VARS.command
196 dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
197 f = open(dumpedPyconfFilePath, 'w')
198 self.config.__save__(f)
202 def date_to_datetime(date):
203 '''Little method that gets year, mon, day, hour ,
204 minutes and seconds from a str in format YYYYMMDD_HHMMSS
206 :param date str: The date in format YYYYMMDD_HHMMSS
207 :return: the same date and time in separate variables.
208 :rtype: (str,str,str,str,str,str)
216 return Y, m, dd, H, M, S
218 def timedelta_total_seconds(timedelta):
219 '''Little method to replace total_seconds from
220 datetime module in order to be compatible with old python versions
222 :param timedelta datetime.timedelta: The delta between two dates
223 :return: The number of seconds corresponding to timedelta.
227 timedelta.microseconds + 0.0 +
228 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
230 def show_command_log(logFilePath, cmd, application, notShownCommands):
231 '''Used in updateHatXml. Determine if the log xml file logFilePath
232 has to be shown or not in the hat log.
234 :param logFilePath str: the path to the command xml log file
235 :param cmd str: the command of the log file
236 :param application str: the application passed as parameter
237 to the salomeTools command
238 :param notShownCommands list: the list of commands
239 that are not shown by default
241 :return: True if cmd is not in notShownCommands and the application
242 in the log file corresponds to application
245 # When the command is not in notShownCommands, no need to go further :
247 if cmd in notShownCommands:
250 # Get the application of the log file
251 logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
252 if 'application' in logFileXml.xmlroot.keys():
253 appliLog = logFileXml.xmlroot.get('application')
254 # if it corresponds, then the log has to be shown
255 if appliLog == application:
256 return True, appliLog
257 elif application != 'None':
258 return False, appliLog
260 return True, appliLog
262 if application == 'None':
267 def list_log_file(dirPath, expression):
268 '''Find all files corresponding to expression in dirPath
270 :param dirPath str: the directory where to search the files
271 :param expression str: the regular expression of files to find
272 :return: the list of files path and informations about it
276 for fileName in os.listdir(dirPath):
277 # YYYYMMDD_HHMMSS_namecmd.xml
279 oExpr = re.compile(sExpr)
280 if oExpr.search(fileName):
281 # get date and hour and format it
282 date_hour_cmd = fileName.split('_')
283 date_not_formated = date_hour_cmd[0]
284 date = "%s/%s/%s" % (date_not_formated[6:8],
285 date_not_formated[4:6], date_not_formated[0:4] )
286 hour_not_formated = date_hour_cmd[1]
287 hour = "%s:%s:%s" % (hour_not_formated[0:2],
288 hour_not_formated[2:4], hour_not_formated[4:6])
289 cmd = date_hour_cmd[2][:-len('.xml')]
290 lRes.append((os.path.join(dirPath, fileName),
291 date_not_formated, date, hour_not_formated, hour, cmd))
294 def update_hat_xml(logDir, application=None, notShownCommands = []):
295 '''Create the xml file in logDir that contain all the xml file
296 and have a name like YYYYMMDD_HHMMSS_namecmd.xml
298 :param logDir str: the directory to parse
299 :param application str: the name of the application if there is any
301 # Create an instance of XmlLogFile class to create hat.xml file
302 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
303 xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
304 "LOGlist", {"application" : application})
305 # parse the log directory to find all the command logs,
306 # then add it to the xml file
307 lLogFile = list_log_file(logDir, logCommandFileExpression)
308 for filePath, _, date, _, hour, cmd in lLogFile:
309 showLog, cmdAppli = show_command_log(filePath, cmd,
310 application, notShownCommands)
311 #if cmd not in notShownCommands:
313 # add a node to the hat.xml file
314 xmlHat.add_simple_node("LogCommand",
315 text=os.path.basename(filePath),
316 attrib = {"date" : date,
319 "application" : cmdAppli})
321 # Write the file on the hard drive
322 xmlHat.write_tree('hat.xsl')