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 in terminal.
40 self.default_level = 3
41 self.silentSysStd = silent_sysstd
43 # Construct xml log file location for sat prints.
44 logFileName = config.VARS.datehour + "_" + config.VARS.command + ".xml"
45 logFilePath = os.path.join(config.SITE.log.logDir, logFileName)
46 # Construct txt file location in order to log the external commands calls (cmake, make, git clone, etc...)
47 txtFileName = config.VARS.datehour + "_" + config.VARS.command + ".txt"
48 txtFilePath = os.path.join(config.SITE.log.logDir, "OUT", txtFileName)
50 src.ensure_path_exists(os.path.dirname(logFilePath))
51 src.ensure_path_exists(os.path.dirname(txtFilePath))
53 self.logFileName = logFileName
54 self.logFilePath = logFilePath
55 self.txtFileName = txtFileName
56 self.txtFilePath = txtFilePath
57 # Initialize xml instance and put first fields like beginTime, user, command, etc...
58 self.xmlFile = xmlManager.xmlLogFile(logFilePath, "SATcommand", attrib = {"application" : config.VARS.application})
59 self.putInitialXMLFields()
60 # Initialize the txt file for reading
61 self.logTxtFile = open(str(self.txtFilePath), 'w', buffering=0)
63 def putInitialXMLFields(self):
64 '''Method called at class initialization : Put all fields corresponding to the command context (user, time, ...)
67 self.xmlFile.add_simple_node("Site", attrib={"command" : self.config.VARS.command})
68 # version of salomeTools
69 self.xmlFile.append_node_attrib("Site", attrib={"satversion" : self.config.INTERNAL.sat_version})
70 # machine name on which the command has been launched
71 self.xmlFile.append_node_attrib("Site", attrib={"hostname" : self.config.VARS.hostname})
72 # Distribution of the machine
73 self.xmlFile.append_node_attrib("Site", attrib={"OS" : self.config.VARS.dist})
74 # The user that have launched the command
75 self.xmlFile.append_node_attrib("Site", attrib={"user" : self.config.VARS.user})
76 # The time when command was launched
77 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
78 date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
79 self.xmlFile.append_node_attrib("Site", attrib={"beginTime" : date_hour})
80 # The application if any
81 if "APPLICATION" in self.config:
82 self.xmlFile.append_node_attrib("Site", attrib={"application" : self.config.VARS.application})
83 # The initialization of the trace node
84 self.xmlFile.add_simple_node("Log",text="")
85 self.xmlFile.add_simple_node("OutLog",text=os.path.join("OUT", self.txtFileName))
87 def write(self, message, level=None, screenOnly=False):
88 '''the function used in the commands that will print in the terminal and the log file.
90 :param message str: The message to print.
91 :param level int: The output level corresponding to the message 0 < level < 6.
92 :param screenOnly boolean: if True, do not write in log file.
94 # do not write message starting with \r to log file
95 if not message.startswith("\r") and not screenOnly:
96 self.xmlFile.append_node_text("Log", printcolors.cleancolor(message))
98 # get user or option output level
99 current_output_level = self.config.USER.output_level
100 if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
101 # clean the message color if the terminal is redirected by user
102 # ex: sat compile appli > log.txt
103 message = printcolors.cleancolor(message)
105 # Print message regarding the output level value
107 if level <= current_output_level and not self.silentSysStd:
108 sys.stdout.write(message)
110 if self.default_level <= current_output_level and not self.silentSysStd:
111 sys.stdout.write(message)
113 def error(self, message):
116 :param message str: The message to print.
118 # Print in the log file
119 self.xmlFile.append_node_text("traces", _('ERROR:') + message)
121 # Print in the terminal and clean colors if the terminal is redirected by user
122 if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
123 sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
125 sys.stderr.write(_('ERROR:') + message)
132 def endWrite(self, attribute):
133 '''Method called just after command end : Put all fields corresponding to the command end context (time).
134 Write the log xml file on the hard drive.
135 And display the command to launch to get the log
137 :param attribute dict: the attribute to add to the node "Site".
139 # Get current time (end of command) and format it
140 dt = datetime.datetime.now()
141 Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
142 t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
145 total_time = timedelta_total_seconds(delta)
146 hours = int(total_time / 3600)
147 minutes = int((total_time - hours*3600) / 60)
148 seconds = total_time - hours*3600 - minutes*60
149 # Add the fields corresponding to the end time and the total time of command
150 endtime = dt.strftime('%d/%Y/%m %Hh%Mm%Ss')
151 self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
152 self.xmlFile.append_node_attrib("Site", attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
154 # Add the attribute passed to the method
155 self.xmlFile.append_node_attrib("Site", attrib=attribute)
157 # Call the method to write the xml file on the hard drive
158 self.xmlFile.write_tree(stylesheet = "command.xsl")
161 def date_to_datetime(date):
162 '''Little method that gets year, mon, day, hour , minutes and seconds from a str in format YYYYMMDD_HHMMSS
164 :param date str: The date in format YYYYMMDD_HHMMSS
165 :return: the same date and time in separate variables.
166 :rtype: (str,str,str,str,str,str)
174 return Y, m, dd, H, M, S
176 def timedelta_total_seconds(timedelta):
177 '''Little method to replace total_seconds from datetime module in order to be compatible with old python versions
179 :param timedelta datetime.timedelta: The delta between two dates
180 :return: The number of seconds corresponding to timedelta.
184 timedelta.microseconds + 0.0 +
185 (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
187 def showcommandLog(logFilePath, cmd, application, notShownCommands):
188 '''Used in updateHatXml. Determine if the log xml file logFilePath has to be shown or not in the hat log.
190 :param logFilePath str: the path to the command xml log file
191 :param cmd str: the command of the log file
192 :param application str: the application passed as parameter to the salomeTools command
193 :param notShownCommands list: the list of commands that are not shown by default
195 :return: True if cmd is not in notShownCommands and the application in the log file corresponds to application
198 # When the command is not in notShownCommands, no need to go further. Do not show
199 if cmd in notShownCommands:
202 # Get the application of the log file
203 logFileXml = src.xmlManager.readXmlFile(logFilePath)
204 if 'application' in logFileXml.xmlroot.keys():
205 appliLog = logFileXml.xmlroot.get('application')
206 # if it corresponds, then the log has to be shown
207 if appliLog == application:
208 return True, appliLog
209 elif application != 'None':
210 return False, appliLog
212 return True, appliLog
214 if application == 'None':
219 def listLogFile(dirPath, expression):
220 '''Find all files corresponding to expression in dirPath
222 :param dirPath str: the directory where to search the files
223 :param expression str: the regular expression of files to find
224 :return: the list of files path and informations about it
228 for fileName in os.listdir(dirPath):
229 # YYYYMMDD_HHMMSS_namecmd.xml
231 oExpr = re.compile(sExpr)
232 if oExpr.search(fileName):
233 # get date and hour and format it
234 date_hour_cmd = fileName.split('_')
235 date_not_formated = date_hour_cmd[0]
236 date = "%s/%s/%s" % (date_not_formated[6:8], date_not_formated[4:6], date_not_formated[0:4] )
237 hour_not_formated = date_hour_cmd[1]
238 hour = "%s:%s:%s" % (hour_not_formated[0:2], hour_not_formated[2:4], hour_not_formated[4:6])
239 cmd = date_hour_cmd[2][:-len('.xml')]
240 lRes.append((os.path.join(dirPath, fileName), date_not_formated, date, hour_not_formated, hour, cmd))
243 def update_hat_xml(logDir, application=None, notShownCommands = []):
244 '''Create the xml file in logDir that contain all the xml file and have a name like YYYYMMDD_HHMMSS_namecmd.xml
246 :param logDir str: the directory to parse
247 :param application str: the name of the application if there is any
249 # Create an instance of xmlLogFile class to create hat.xml file
250 xmlHatFilePath = os.path.join(logDir, 'hat.xml')
251 xmlHat = src.xmlManager.xmlLogFile(xmlHatFilePath, "LOGlist", {"application" : application})
252 # parse the log directory to find all the command logs, then add it to the xml file
253 lLogFile = listLogFile(logDir, logCommandFileExpression)
254 for filePath, _, date, _, hour, cmd in lLogFile:
255 showLog, cmdAppli = showcommandLog(filePath, cmd, application, notShownCommands)
256 #if cmd not in notShownCommands:
258 # add a node to the hat.xml file
259 xmlHat.add_simple_node("LogCommand", text=os.path.basename(filePath), attrib = {"date" : date, "hour" : hour, "cmd" : cmd, "application" : cmdAppli})
261 # Write the file on the hard drive
262 xmlHat.write_tree('hat.xsl')