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
25 from . import printcolors
26 from . import xmlManager
28 logCommandFileExpression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
31 '''Class to handle log mechanism.
33 def __init__(self, config, silent_sysstd=False):
36 :param config pyconf.Config: The global configuration.
37 :param silent_sysstd boolean: if True, do not write anything
41 self.default_level = 3
42 self.silentSysStd = silent_sysstd
44 # Construct xml log file location for sat prints.
45 logFileName = config.VARS.datehour + "_" + config.VARS.command + ".xml"
46 logFilePath = os.path.join(config.SITE.log.logDir, logFileName)
47 # Construct txt file location in order to log
48 # the external commands calls (cmake, make, git clone, etc...)
49 txtFileName = config.VARS.datehour + "_" + config.VARS.command + ".txt"
50 txtFilePath = os.path.join(config.SITE.log.logDir, "OUT", txtFileName)
52 src.ensure_path_exists(os.path.dirname(logFilePath))
53 src.ensure_path_exists(os.path.dirname(txtFilePath))
55 self.logFileName = logFileName
56 self.logFilePath = logFilePath
57 self.txtFileName = txtFileName
58 self.txtFilePath = txtFilePath
59 # Initialize xml instance and put first fields
60 # like beginTime, user, command, etc...
61 self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand",
62 attrib = {"application" : config.VARS.application})
63 self.put_initial_xml_fields()
64 # Initialize the txt file for reading
65 self.logTxtFile = open(str(self.txtFilePath), 'w')
67 def put_initial_xml_fields(self):
68 '''Method called at class initialization : Put all fields
69 corresponding to the command context (user, time, ...)
72 self.xmlFile.add_simple_node("Site", attrib={"command" :
73 self.config.VARS.command})
74 # version of salomeTools
75 self.xmlFile.append_node_attrib("Site", attrib={"satversion" :
76 self.config.INTERNAL.sat_version})
77 # machine name on which the command has been launched
78 self.xmlFile.append_node_attrib("Site", attrib={"hostname" :
79 self.config.VARS.hostname})
80 # Distribution of the machine
81 self.xmlFile.append_node_attrib("Site", attrib={"OS" :
82 self.config.VARS.dist})
83 # The user that have launched the command
84 self.xmlFile.append_node_attrib("Site", attrib={"user" :
85 self.config.VARS.user})
86 # The time when command was launched
87 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
88 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
89 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" :
91 # The application if any
92 if "APPLICATION" in self.config:
93 self.xmlFile.append_node_attrib("Site",
94 attrib={"application" : self.config.VARS.application})
95 # The initialization of the trace node
96 self.xmlFile.add_simple_node("Log",text="")
97 self.xmlFile.add_simple_node("OutLog",
98 text=os.path.join("OUT", self.txtFileName))
100 def write(self, message, level=None, screenOnly=False):
101 '''the function used in the commands
102 that will print in the terminal and the log file.
104 :param message str: The message to print.
105 :param level int: The output level corresponding
106 to the message 0 < level < 6.
107 :param screenOnly boolean: if True, do not write in log file.
109 # do not write message starting with \r to log file
110 if not message.startswith("\r") and not screenOnly:
111 self.xmlFile.append_node_text("Log",
112 printcolors.cleancolor(message))
114 # get user or option output level
115 current_output_level = self.config.USER.output_level
116 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
117 # clean the message color if the terminal is redirected by user
118 # ex: sat compile appli > log.txt
119 message = printcolors.cleancolor(message)
121 # Print message regarding the output level value
123 if level <= current_output_level and not self.silentSysStd:
124 sys.stdout.write(message)
126 if self.default_level <= current_output_level and not self.silentSysStd:
127 sys.stdout.write(message)
129 def error(self, message):
132 :param message str: The message to print.
134 # Print in the log file
135 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
137 # Print in the terminal and clean colors if the terminal
138 # is redirected by user
139 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
140 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
142 sys.stderr.write(_('ERROR:') + message)
149 def end_write(self, attribute):
150 '''Method called just after command end : Put all fields
151 corresponding to the command end context (time).
152 Write the log xml file on the hard drive.
153 And display the command to launch to get the log
155 :param attribute dict: the attribute to add to the node "Site".
157 # Get current time (end of command) and format it
158 dt = datetime.datetime.now()
159 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
160 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
163 total_time = timedelta_total_seconds(delta)
164 hours = int(total_time / 3600)
165 minutes = int((total_time - hours*3600) / 60)
166 seconds = total_time - hours*3600 - minutes*60
167 # Add the fields corresponding to the end time
168 # and the total time of command
169 endtime = dt.strftime('%d/%Y/%m %Hh%Mm%Ss')
170 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
171 self.xmlFile.append_node_attrib("Site",
172 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
174 # Add the attribute passed to the method
175 self.xmlFile.append_node_attrib("Site", attrib=attribute)
177 # Call the method to write the xml file on the hard drive
178 self.xmlFile.write_tree(stylesheet = "command.xsl")
180 # Dump the config in a pyconf file in the log directory
181 logDir = self.config.SITE.log.logDir
182 dumpedPyconfFileName = (self.config.VARS.datehour
184 + self.config.VARS.command
186 dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
187 f = open(dumpedPyconfFilePath, 'w')
188 self.config.__save__(f)
192 def date_to_datetime(date):
193 '''Little method that gets year, mon, day, hour ,
194 minutes and seconds from a str in format YYYYMMDD_HHMMSS
196 :param date str: The date in format YYYYMMDD_HHMMSS
197 :return: the same date and time in separate variables.
198 :rtype: (str,str,str,str,str,str)
206 return Y, m, dd, H, M, S
208 def timedelta_total_seconds(timedelta):
209 '''Little method to replace total_seconds from
210 datetime module in order to be compatible with old python versions
212 :param timedelta datetime.timedelta: The delta between two dates
213 :return: The number of seconds corresponding to timedelta.
217 timedelta.microseconds + 0.0 +
218 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
220 def show_command_log(logFilePath, cmd, application, notShownCommands):
221 '''Used in updateHatXml. Determine if the log xml file logFilePath
222 has to be shown or not in the hat log.
224 :param logFilePath str: the path to the command xml log file
225 :param cmd str: the command of the log file
226 :param application str: the application passed as parameter
227 to the salomeTools command
228 :param notShownCommands list: the list of commands
229 that are not shown by default
231 :return: True if cmd is not in notShownCommands and the application
232 in the log file corresponds to application
235 # When the command is not in notShownCommands, no need to go further :
237 if cmd in notShownCommands:
240 # Get the application of the log file
241 logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
242 if 'application' in logFileXml.xmlroot.keys():
243 appliLog = logFileXml.xmlroot.get('application')
244 # if it corresponds, then the log has to be shown
245 if appliLog == application:
246 return True, appliLog
247 elif application != 'None':
248 return False, appliLog
250 return True, appliLog
252 if application == 'None':
257 def list_log_file(dirPath, expression):
258 '''Find all files corresponding to expression in dirPath
260 :param dirPath str: the directory where to search the files
261 :param expression str: the regular expression of files to find
262 :return: the list of files path and informations about it
266 for fileName in os.listdir(dirPath):
267 # YYYYMMDD_HHMMSS_namecmd.xml
269 oExpr = re.compile(sExpr)
270 if oExpr.search(fileName):
271 # get date and hour and format it
272 date_hour_cmd = fileName.split('_')
273 date_not_formated = date_hour_cmd[0]
274 date = "%s/%s/%s" % (date_not_formated[6:8],
275 date_not_formated[4:6], date_not_formated[0:4] )
276 hour_not_formated = date_hour_cmd[1]
277 hour = "%s:%s:%s" % (hour_not_formated[0:2],
278 hour_not_formated[2:4], hour_not_formated[4:6])
279 cmd = date_hour_cmd[2][:-len('.xml')]
280 lRes.append((os.path.join(dirPath, fileName),
281 date_not_formated, date, hour_not_formated, hour, cmd))
284 def update_hat_xml(logDir, application=None, notShownCommands = []):
285 '''Create the xml file in logDir that contain all the xml file
286 and have a name like YYYYMMDD_HHMMSS_namecmd.xml
288 :param logDir str: the directory to parse
289 :param application str: the name of the application if there is any
291 # Create an instance of XmlLogFile class to create hat.xml file
292 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
293 xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
294 "LOGlist", {"application" : application})
295 # parse the log directory to find all the command logs,
296 # then add it to the xml file
297 lLogFile = list_log_file(logDir, logCommandFileExpression)
298 for filePath, _, date, _, hour, cmd in lLogFile:
299 showLog, cmdAppli = show_command_log(filePath, cmd,
300 application, notShownCommands)
301 #if cmd not in notShownCommands:
303 # add a node to the hat.xml file
304 xmlHat.add_simple_node("LogCommand",
305 text=os.path.basename(filePath),
306 attrib = {"date" : date,
309 "application" : cmdAppli})
311 # Write the file on the hard drive
312 xmlHat.write_tree('hat.xsl')