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