Salome HOME
Merge branch 'occ/psutil'
[modules/kernel.git] / src / KERNEL_PY / kernel / logger.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
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, or (at your option) any later version.
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 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21
22 #=============================================================================
23 #  Author    : Guillaume Boulant (CSSI)
24 #  Rewritten by Renaud Barate (EDF R&D)
25 #=============================================================================
26
27 ## \defgroup logger logger
28 #  \{ 
29 #  \details
30 #  This module defines a class which provides logging facility in Salome.
31 #  \}
32
33 """
34 This module defines a class which provides logging facility in Salome:
35 """
36
37 import sys, os
38 import logging
39
40 from salome.kernel.deprecation import deprecated
41 from salome.kernel import termcolor
42 import salome.kernel.logconfig
43
44 ## This class formats and displays log messages in Salome environment. It
45 #  inherits \b logging.Logger class defined in \b logging module from Python 
46 #  library, so all methods from \b logging.Logger can be used here. 
47 #  The format of the traces is:
48 #  LEVEL[keyword] : Message  
49 #  
50 #  ,where \em LEVEL is the level of the message (\em DEBUG, \em INFO, etc.),
51 #  \em keyword is the name of the logger, and \em Message is the message to log.
52 #    
53 #  When creating a new Logger object, the parameter \em keyword defines the
54 #  name of the logger, \em level defines the logging level (default is
55 #  \b logging.DEBUG if KERNEL module is configured with --enable-debug option 
56 #  or \b logging.WARNING otherwise), and \em color defines the color
57 #  of the log messages for this logger (log messages will appear in color
58 #  only when displayed on color - capable ASCII terminals). See module
59 #  \ref termcolor "salome.kernel.termcolor" for the color constants.
60 #    
61 #  By default, log messages will be displayed only on standard output. They
62 #  can also be recorded in a file (see method setLogFile()). For now,
63 #  the CORBA-based logging facility can not be used through this class.
64 #
65 #  A source filename \em sourceFileName can be defined. If this argument is
66 #  specified, then the \em keyword is modified to the basename of the 
67 #  \em sourceFileName
68 #    
69 #  Basic usage::
70 #  \code    
71 #  from salome.kernel.logger import Logger
72 #  log = Logger("Test")
73 #  log.debug("Debug message")
74 #  log.info("Information message")
75 #  log.warning("Warning message")
76 #  log.error("Error message")
77 #  log.critical("Fatal error message")
78 #  \endcode
79 #  \ingroup logger
80 class Logger(logging.Logger):
81     """
82     This class formats and displays log messages in Salome environment. It
83     inherits :class:`Logger<logging.Logger>` class defined in :mod:`logging`
84     module from Python library, so all methods from :class:`logging.Logger`
85     can be used here. The format of the traces is:
86     
87     LEVEL    [keyword] : Message
88     
89     where `LEVEL` is the level of the message (`DEBUG`, `INFO`, etc.),
90     `keyword` is the name of the logger, and `Message` is the message to log.
91     
92     When creating a new Logger object, the parameter `keyword` defines the
93     name of the logger, `level` defines the logging level (default is
94     :const:`logging.DEBUG` if KERNEL module is configured with --enable-debug
95     option or :const:`logging.WARNING` otherwise), and `color` defines the color
96     of the log messages for this logger (log messages will appear in color
97     only when displayed on color-capable ASCII terminals). See module
98     :mod:`salome.kernel.termcolor` for the color constants.
99     
100     By default, log messages will be displayed only on standard output. They
101     can also be recorded in a file (see method :meth:`setLogFile`). For now,
102     the CORBA-based logging facility can not be used through this class.
103
104     A source filename `sourceFileName` can be defined. If this argument is
105     specified, then the `keyword` is modified to the basename of the `sourceFileName`
106     
107     Basic usage::
108     
109         from salome.kernel.logger import Logger
110         log = Logger("Test")
111         log.debug("Debug message")
112         log.info("Information message")
113         log.warning("Warning message")
114         log.error("Error message")
115         log.critical("Fatal error message")
116
117     """
118
119     def __init__(self, keyword = "KEY", level = salome.kernel.logconfig.loggingLevel,
120                  color = None, sourceFileName=None):
121
122         if sourceFileName is not None:
123             keyword = os.path.basename(sourceFileName).split('.')[0]
124         logging.Logger.__init__(self, keyword, level)
125         self._baseFormatString = "%(levelname)-8s [%(name)s] : %(message)s"
126         self._baseFormatter = logging.Formatter(self._baseFormatString)
127         if hasattr(sys.stdout, "flush"):
128             self._stdoutStream = sys.stdout
129         else:
130             self._stdoutStream = _UnFlushableLogStream(sys.stdout)
131         self._stdoutHandler = logging.StreamHandler(self._stdoutStream)
132         self._stdoutHandler.setLevel(logging.DEBUG)
133         self.setColor(color)
134         self.addHandler(self._stdoutHandler)
135         self._fileHandler = None
136
137     ## Log all messages, including DEBUG level messages (equivalent to
138     #  setLevel(logging.DEBUG)).
139     def showDebug(self):
140         """
141         Log all messages, including DEBUG level messages (equivalent to
142         ``setLevel(logging.DEBUG)``).
143         """
144         self.setLevel(logging.DEBUG)
145
146     ## Define a log file to record the log messages (in addition to the
147     #  standard output).
148     def setLogFile(self, logFilename):
149         """
150         Define a log file to record the log messages (in addition to the
151         standard output).
152         """
153         self.closeLogFile()
154         self._fileHandler = logging.FileHandler(logFilename, 'w')
155         self._fileHandler.setLevel(logging.DEBUG)
156         self._fileHandler.setFormatter(self._baseFormatter)
157         self.addHandler(self._fileHandler)
158
159     ## Set the color of log messages on color-capable terminals. If \em color
160     #  is \b None, the default color will be used.
161     def setColor(self, color):
162         """
163         Set the color of log messages on color-capable terminals. If `color`
164         is :const:`None`, the default color will be used.
165         """
166         if color is None or not termcolor.canDisplayColor(self._stdoutStream):
167             stdoutFormatter = self._baseFormatter
168         else:
169             format = ("%s%s%s" %
170                       (termcolor.getControlSequence(color),
171                        self._baseFormatString,
172                        termcolor.getControlSequence(termcolor.DEFAULT)))
173             stdoutFormatter = logging.Formatter(format)
174         self._stdoutHandler.setFormatter(stdoutFormatter)
175
176     ## Close the log file.
177     def closeLogFile(self):
178         """Close the log file."""
179         if self._fileHandler is not None:
180             self.removeHandler(self._fileHandler)
181             self._fileHandler.close()
182             self._fileHandler = None
183
184     ## Hide DEBUG level messages (equivalent to setLevel(logging.INFO)).
185     def hideDebug(self):
186         """
187         Hide DEBUG level messages (equivalent to ``setLevel(logging.INFO)``).
188         """
189         self.setLevel(logging.INFO)
190
191     @deprecated("Deprecated since version 5.1.5. Please replace with "
192                 "Logger.critical(message)")
193     
194     ## Log a message with CRITICAL level. This method only exists for
195     #  backward compatibility and is equivalent to \b critical(message).
196     def fatal(self, message):
197         """
198         Log a message with CRITICAL level. This method only exists for
199         backward compatibility and is equivalent to ``critical(message)``.
200         """
201         self.critical(message)
202
203 ## This utility class allows to log messages to a stream with no \b flush
204 #  method. This is useful to send log messages to \b PyOut objects.
205 #  \ingroup logger
206 class _UnFlushableLogStream:
207     """
208     This utility class allows to log messages to a stream with no `flush`
209     method. This is useful to send log messages to `PyOut` objects.
210     """
211
212     def __init__(self, stream):
213         self._stream = stream
214
215     def write(self, msg):
216         self._stream.write(msg)
217
218     def flush(self):
219         pass
220
221 ## This class extends Logger class and adds exception information
222 #  when DEBUG messages are recorded. It exists mainly for backward
223 #  compatibility, as the same thing can be done by calling
224 #  <em> Logger.debug(message, exc_info = True) </em>.
225 #  \ingroup logger
226 class ExtLogger(Logger):
227     """
228     This class extends :class:`Logger` class and adds exception information
229     when DEBUG messages are recorded. It exists mainly for backward
230     compatibility, as the same thing can be done by calling
231     ``Logger.debug(message, exc_info = True)``.
232     """
233     
234     @deprecated("Class ExtLogger is deprecated since version 5.1.5. See "
235                 "documentation for replacement.")
236     def __init__(self, keyword = "KEY",
237                  level = salome.kernel.logconfig.loggingLevel,
238                  color = None, sourceFileName=None):
239         Logger.__init__(self, keyword, level, color, sourceFileName)
240
241     ## Log a DEBUG message with exception information (equivalent to
242     #  <em> Logger.debug(message, exc_info = True) </em>).
243     def debug( self, message ):
244         """
245         Log a DEBUG message with exception information (equivalent to
246         ``Logger.debug(message, exc_info = True)``).
247         """
248         Logger.debug(self, message, exc_info = True)
249
250 ## Test function for logger module
251 #  \ingroup logger
252 def TEST_Logger():
253     """Test function for logger module"""
254     log = Logger("TST")
255
256     # Base methods
257     log.info("Information message")
258     log.debug("Debug message")
259     log.fatal("Fatal error message")
260
261     # Message building
262     data = 12
263     log.info("This message displays data = " + str(data))
264
265     data = {}
266     data["KERNEL"] = "V1"
267     data["GEOM"] = "V2"
268     log.info("This message displays data = " + str(data))
269
270     # Test with a non-string parameter
271     log.info(data)
272
273     # Test with a default instance
274     log = Logger()
275     log.info("Default logger")
276
277     # Test showDebug method
278     log.setLogFile("test.log")
279     log.debug("Debug trace")
280     log.hideDebug()
281     log.debug("This trace should NOT be displayed")
282     log.showDebug()
283     log.debug("This trace should be displayed")
284     log.closeLogFile()
285     log.info("After closing the log file")
286
287
288 # Main function only used to test the module
289 if __name__ == "__main__":
290     TEST_Logger()