]> SALOME platform Git repositories - tools/sat.git/blob - src/logger.py
Salome HOME
Improve tests
[tools/sat.git] / src / logger.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  CEA/DEN
4 #
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.
9 #
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.
14 #
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
19 import sys
20 import os
21 import datetime
22 import re
23
24 import src
25 from . import printcolors
26 from . import xmlManager
27
28 logCommandFileExpression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
29
30 class Logger(object):
31     '''Class to handle log mechanism.
32     '''
33     def __init__(self, config, silent_sysstd=False):
34         '''Initialization
35         
36         :param config pyconf.Config: The global configuration.
37         :param silent_sysstd boolean: if True, do not write anything
38                                       in terminal.
39         '''
40         self.config = config
41         self.default_level = 3
42         self.silentSysStd = silent_sysstd
43         
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)
51         
52         src.ensure_path_exists(os.path.dirname(logFilePath))
53         src.ensure_path_exists(os.path.dirname(txtFilePath))
54         
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')
66         
67     def put_initial_xml_fields(self):
68         '''Method called at class initialization : Put all fields 
69            corresponding to the command context (user, time, ...)
70         '''
71         # command name
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" : 
90                                                         date_hour})
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))
99
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.
103         
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.
108         '''
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))
113
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)
120         
121         # Print message regarding the output level value
122         if level:
123             if level <= current_output_level and not self.silentSysStd:
124                 sys.stdout.write(message)
125         else:
126             if self.default_level <= current_output_level and not self.silentSysStd:
127                 sys.stdout.write(message)
128
129     def error(self, message):
130         '''Print an error.
131         
132         :param message str: The message to print.
133         '''
134         # Print in the log file
135         self.xmlFile.append_node_text("traces", _('ERROR:') + message)
136
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))
141         else:
142             sys.stderr.write(_('ERROR:') + message)
143
144     def flush(self):
145         '''Flush terminal
146         '''
147         sys.stdout.flush()
148         
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
154         
155         :param attribute dict: the attribute to add to the node "Site".
156         '''       
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))
161         tf = dt
162         delta = tf - t0
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)})
173         
174         # Add the attribute passed to the method
175         self.xmlFile.append_node_attrib("Site", attrib=attribute)
176         
177         # Call the method to write the xml file on the hard drive
178         self.xmlFile.write_tree(stylesheet = "command.xsl")
179         
180
181 def date_to_datetime(date):
182     '''Little method that gets year, mon, day, hour , 
183        minutes and seconds from a str in format YYYYMMDD_HHMMSS
184     
185     :param date str: The date in format YYYYMMDD_HHMMSS
186     :return: the same date and time in separate variables.
187     :rtype: (str,str,str,str,str,str)
188     '''
189     Y = date[:4]
190     m = date[4:6]
191     dd = date[6:8]
192     H = date[9:11]
193     M = date[11:13]
194     S = date[13:15]
195     return Y, m, dd, H, M, S
196
197 def timedelta_total_seconds(timedelta):
198     '''Little method to replace total_seconds from 
199        datetime module in order to be compatible with old python versions
200     
201     :param timedelta datetime.timedelta: The delta between two dates
202     :return: The number of seconds corresponding to timedelta.
203     :rtype: float
204     '''
205     return (
206         timedelta.microseconds + 0.0 +
207         (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
208         
209 def show_command_log(logFilePath, cmd, application, notShownCommands):
210     '''Used in updateHatXml. Determine if the log xml file logFilePath 
211        has to be shown or not in the hat log.
212     
213     :param logFilePath str: the path to the command xml log file
214     :param cmd str: the command of the log file
215     :param application str: the application passed as parameter 
216                             to the salomeTools command
217     :param notShownCommands list: the list of commands 
218                                   that are not shown by default
219     
220     :return: True if cmd is not in notShownCommands and the application 
221              in the log file corresponds to application
222     :rtype: boolean
223     '''
224     # When the command is not in notShownCommands, no need to go further :
225     # Do not show
226     if cmd in notShownCommands:
227         return False, None
228  
229     # Get the application of the log file
230     logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
231     if 'application' in logFileXml.xmlroot.keys():
232         appliLog = logFileXml.xmlroot.get('application')
233         # if it corresponds, then the log has to be shown
234         if appliLog == application:
235             return True, appliLog
236         elif application != 'None':
237             return False, appliLog
238         
239         return True, appliLog
240     
241     if application == 'None':
242             return True, None    
243         
244     return False, None
245
246 def list_log_file(dirPath, expression):
247     '''Find all files corresponding to expression in dirPath
248     
249     :param dirPath str: the directory where to search the files
250     :param expression str: the regular expression of files to find
251     :return: the list of files path and informations about it
252     :rtype: list
253     '''
254     lRes = []
255     for fileName in os.listdir(dirPath):
256         # YYYYMMDD_HHMMSS_namecmd.xml
257         sExpr = expression
258         oExpr = re.compile(sExpr)
259         if oExpr.search(fileName):
260             # get date and hour and format it
261             date_hour_cmd = fileName.split('_')
262             date_not_formated = date_hour_cmd[0]
263             date = "%s/%s/%s" % (date_not_formated[6:8], 
264                                  date_not_formated[4:6], date_not_formated[0:4] )
265             hour_not_formated = date_hour_cmd[1]
266             hour = "%s:%s:%s" % (hour_not_formated[0:2], 
267                                  hour_not_formated[2:4], hour_not_formated[4:6])
268             cmd = date_hour_cmd[2][:-len('.xml')]
269             lRes.append((os.path.join(dirPath, fileName), 
270                          date_not_formated, date, hour_not_formated, hour, cmd))
271     return lRes
272
273 def update_hat_xml(logDir, application=None, notShownCommands = []):
274     '''Create the xml file in logDir that contain all the xml file 
275        and have a name like YYYYMMDD_HHMMSS_namecmd.xml
276     
277     :param logDir str: the directory to parse
278     :param application str: the name of the application if there is any
279     '''
280     # Create an instance of XmlLogFile class to create hat.xml file
281     xmlHatFilePath = os.path.join(logDir, 'hat.xml')
282     xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
283                                     "LOGlist", {"application" : application})
284     # parse the log directory to find all the command logs, 
285     # then add it to the xml file
286     lLogFile = list_log_file(logDir, logCommandFileExpression)
287     for filePath, _, date, _, hour, cmd in lLogFile:
288         showLog, cmdAppli = show_command_log(filePath, cmd,
289                                               application, notShownCommands)
290         #if cmd not in notShownCommands:
291         if showLog:
292             # add a node to the hat.xml file
293             xmlHat.add_simple_node("LogCommand", 
294                                    text=os.path.basename(filePath), 
295                                    attrib = {"date" : date, 
296                                              "hour" : hour, 
297                                              "cmd" : cmd, 
298                                              "application" : cmdAppli})
299     
300     # Write the file on the hard drive
301     xmlHat.write_tree('hat.xsl')