]> SALOME platform Git repositories - tools/sat.git/blob - src/logger.py
Salome HOME
rename level into verbose, and set verbose 0 to silent option
[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 '''In this file are implemented the classes and method relative to the logging
19 '''
20
21 import sys
22 import os
23 import datetime
24 import re
25
26 import src
27 from . import printcolors
28 from . import xmlManager
29
30 logCommandFileExpression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
31
32 class Logger(object):
33     '''Class to handle log mechanism.
34     '''
35     def __init__(self, config, silent_sysstd=False):
36         '''Initialization
37         
38         :param config pyconf.Config: The global configuration.
39         :param silent_sysstd boolean: if True, do not write anything
40                                       in terminal.
41         '''
42         self.config = config
43         self.default_level = 3
44         self.silentSysStd = silent_sysstd
45         
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)
53         
54         src.ensure_path_exists(os.path.dirname(logFilePath))
55         src.ensure_path_exists(os.path.dirname(txtFilePath))
56         
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')
68         
69     def put_initial_xml_fields(self):
70         '''Method called at class initialization : Put all fields 
71            corresponding to the command context (user, time, ...)
72         '''
73         # command name
74         self.xmlFile.add_simple_node("Site", attrib={"command" : 
75                                                      self.config.VARS.command})
76         # version of salomeTools
77         self.xmlFile.append_node_attrib("Site", attrib={"satversion" : 
78                                             self.config.INTERNAL.sat_version})
79         # machine name on which the command has been launched
80         self.xmlFile.append_node_attrib("Site", attrib={"hostname" : 
81                                                     self.config.VARS.hostname})
82         # Distribution of the machine
83         self.xmlFile.append_node_attrib("Site", attrib={"OS" : 
84                                                         self.config.VARS.dist})
85         # The user that have launched the command
86         self.xmlFile.append_node_attrib("Site", attrib={"user" : 
87                                                         self.config.VARS.user})
88         # The time when command was launched
89         Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
90         date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
91         self.xmlFile.append_node_attrib("Site", attrib={"beginTime" : 
92                                                         date_hour})
93         # The application if any
94         if "APPLICATION" in self.config:
95             self.xmlFile.append_node_attrib("Site", 
96                         attrib={"application" : self.config.VARS.application})
97         # The initialization of the trace node
98         self.xmlFile.add_simple_node("Log",text="")
99         self.xmlFile.add_simple_node("OutLog",
100                                     text=os.path.join("OUT", self.txtFileName))
101
102     def write(self, message, level=None, screenOnly=False):
103         '''the function used in the commands 
104         that will print in the terminal and the log file.
105         
106         :param message str: The message to print.
107         :param level int: The output level corresponding 
108                           to the message 0 < level < 6.
109         :param screenOnly boolean: if True, do not write in log file.
110         '''
111         # do not write message starting with \r to log file
112         if not message.startswith("\r") and not screenOnly:
113             self.xmlFile.append_node_text("Log", 
114                                           printcolors.cleancolor(message))
115
116         # get user or option output level
117         current_output_verbose_level = self.config.USER.output_verbose_level
118         if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
119             # clean the message color if the terminal is redirected by user
120             # ex: sat compile appli > log.txt
121             message = printcolors.cleancolor(message)
122         
123         # Print message regarding the output level value
124         if level:
125             if level <= current_output_verbose_level and not self.silentSysStd:
126                 sys.stdout.write(message)
127         else:
128             if self.default_level <= current_output_verbose_level and not self.silentSysStd:
129                 sys.stdout.write(message)
130
131     def error(self, message):
132         '''Print an error.
133         
134         :param message str: The message to print.
135         '''
136         # Print in the log file
137         self.xmlFile.append_node_text("traces", _('ERROR:') + message)
138
139         # Print in the terminal and clean colors if the terminal 
140         # is redirected by user
141         if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
142             sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
143         else:
144             sys.stderr.write(_('ERROR:') + message)
145
146     def flush(self):
147         '''Flush terminal
148         '''
149         sys.stdout.flush()
150         
151     def end_write(self, attribute):
152         '''Method called just after command end : Put all fields 
153            corresponding to the command end context (time).
154            Write the log xml file on the hard drive.
155            And display the command to launch to get the log
156         
157         :param attribute dict: the attribute to add to the node "Site".
158         '''       
159         # Get current time (end of command) and format it
160         dt = datetime.datetime.now()
161         Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
162         t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
163         tf = dt
164         delta = tf - t0
165         total_time = timedelta_total_seconds(delta)
166         hours = int(total_time / 3600)
167         minutes = int((total_time - hours*3600) / 60)
168         seconds = total_time - hours*3600 - minutes*60
169         # Add the fields corresponding to the end time
170         # and the total time of command
171         endtime = dt.strftime('%d/%Y/%m %Hh%Mm%Ss')
172         self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
173         self.xmlFile.append_node_attrib("Site", 
174                 attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
175         
176         # Add the attribute passed to the method
177         self.xmlFile.append_node_attrib("Site", attrib=attribute)
178         
179         # Call the method to write the xml file on the hard drive
180         self.xmlFile.write_tree(stylesheet = "command.xsl")
181         
182         # Dump the config in a pyconf file in the log directory
183         logDir = self.config.SITE.log.log_dir
184         dumpedPyconfFileName = (self.config.VARS.datehour 
185                                 + "_" 
186                                 + self.config.VARS.command 
187                                 + ".pyconf")
188         dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
189         f = open(dumpedPyconfFilePath, 'w')
190         self.config.__save__(f)
191         f.close()
192         
193
194 def date_to_datetime(date):
195     '''Little method that gets year, mon, day, hour , 
196        minutes and seconds from a str in format YYYYMMDD_HHMMSS
197     
198     :param date str: The date in format YYYYMMDD_HHMMSS
199     :return: the same date and time in separate variables.
200     :rtype: (str,str,str,str,str,str)
201     '''
202     Y = date[:4]
203     m = date[4:6]
204     dd = date[6:8]
205     H = date[9:11]
206     M = date[11:13]
207     S = date[13:15]
208     return Y, m, dd, H, M, S
209
210 def timedelta_total_seconds(timedelta):
211     '''Little method to replace total_seconds from 
212        datetime module in order to be compatible with old python versions
213     
214     :param timedelta datetime.timedelta: The delta between two dates
215     :return: The number of seconds corresponding to timedelta.
216     :rtype: float
217     '''
218     return (
219         timedelta.microseconds + 0.0 +
220         (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
221         
222 def show_command_log(logFilePath, cmd, application, notShownCommands):
223     '''Used in updateHatXml. Determine if the log xml file logFilePath 
224        has to be shown or not in the hat log.
225     
226     :param logFilePath str: the path to the command xml log file
227     :param cmd str: the command of the log file
228     :param application str: the application passed as parameter 
229                             to the salomeTools command
230     :param notShownCommands list: the list of commands 
231                                   that are not shown by default
232     
233     :return: True if cmd is not in notShownCommands and the application 
234              in the log file corresponds to application
235     :rtype: boolean
236     '''
237     # When the command is not in notShownCommands, no need to go further :
238     # Do not show
239     if cmd in notShownCommands:
240         return False, None
241  
242     # Get the application of the log file
243     logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
244     if 'application' in logFileXml.xmlroot.keys():
245         appliLog = logFileXml.xmlroot.get('application')
246         # if it corresponds, then the log has to be shown
247         if appliLog == application:
248             return True, appliLog
249         elif application != 'None':
250             return False, appliLog
251         
252         return True, appliLog
253     
254     if application == 'None':
255             return True, None    
256         
257     return False, None
258
259 def list_log_file(dirPath, expression):
260     '''Find all files corresponding to expression in dirPath
261     
262     :param dirPath str: the directory where to search the files
263     :param expression str: the regular expression of files to find
264     :return: the list of files path and informations about it
265     :rtype: list
266     '''
267     lRes = []
268     for fileName in os.listdir(dirPath):
269         # YYYYMMDD_HHMMSS_namecmd.xml
270         sExpr = expression
271         oExpr = re.compile(sExpr)
272         if oExpr.search(fileName):
273             # get date and hour and format it
274             date_hour_cmd = fileName.split('_')
275             date_not_formated = date_hour_cmd[0]
276             date = "%s/%s/%s" % (date_not_formated[6:8], 
277                                  date_not_formated[4:6], date_not_formated[0:4] )
278             hour_not_formated = date_hour_cmd[1]
279             hour = "%s:%s:%s" % (hour_not_formated[0:2], 
280                                  hour_not_formated[2:4], hour_not_formated[4:6])
281             cmd = date_hour_cmd[2][:-len('.xml')]
282             lRes.append((os.path.join(dirPath, fileName), 
283                          date_not_formated, date, hour_not_formated, hour, cmd))
284     return lRes
285
286 def update_hat_xml(logDir, application=None, notShownCommands = []):
287     '''Create the xml file in logDir that contain all the xml file 
288        and have a name like YYYYMMDD_HHMMSS_namecmd.xml
289     
290     :param logDir str: the directory to parse
291     :param application str: the name of the application if there is any
292     '''
293     # Create an instance of XmlLogFile class to create hat.xml file
294     xmlHatFilePath = os.path.join(logDir, 'hat.xml')
295     xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
296                                     "LOGlist", {"application" : application})
297     # parse the log directory to find all the command logs, 
298     # then add it to the xml file
299     lLogFile = list_log_file(logDir, logCommandFileExpression)
300     for filePath, _, date, _, hour, cmd in lLogFile:
301         showLog, cmdAppli = show_command_log(filePath, cmd,
302                                               application, notShownCommands)
303         #if cmd not in notShownCommands:
304         if showLog:
305             # add a node to the hat.xml file
306             xmlHat.add_simple_node("LogCommand", 
307                                    text=os.path.basename(filePath), 
308                                    attrib = {"date" : date, 
309                                              "hour" : hour, 
310                                              "cmd" : cmd, 
311                                              "application" : cmdAppli})
312     
313     # Write the file on the hard drive
314     xmlHat.write_tree('hat.xsl')